feat(biz): 订单模块新增详情查看和作废功能并优化附件管理

This commit is contained in:
zxy 2026-05-13 16:45:48 +08:00
parent 51b7227c5f
commit 18b50cee6f
5 changed files with 213 additions and 15 deletions

View File

@ -87,7 +87,7 @@ public class TsoOrderController {
respPageResult.getList().forEach(item -> { respPageResult.getList().forEach(item -> {
AdminUserDO userEntity = userService.getUser(item.getSaleMan()); AdminUserDO userEntity = userService.getUser(item.getSaleMan());
if (userEntity != null) { if (userEntity != null) {
item.setSaleManName(userEntity.getUsername()); item.setSaleManName(userEntity.getNickname());
} }
}); });
} }

View File

@ -39,6 +39,10 @@ public class SecurityConfiguration {
.antMatchers(adminSeverContextPath + "/**").anonymous(); .antMatchers(adminSeverContextPath + "/**").anonymous();
// 文件读取 // 文件读取
registry.antMatchers(buildAdminApi("/infra/file/*/get/**")).permitAll(); registry.antMatchers(buildAdminApi("/infra/file/*/get/**")).permitAll();
registry.antMatchers(buildAdminApi("/infra/file/upload")).permitAll();
registry.antMatchers(buildAdminApi("/infra/file/uploadBatch")).permitAll();
registry.antMatchers(buildAdminApi("/infra/file/uploadWatch")).permitAll();
} }
}; };

View File

@ -2,9 +2,10 @@ package com.ningxia.yunxi.chemmes.module.infra.service.logger;
import com.ningxia.yunxi.chemmes.framework.common.pojo.PageResult; import com.ningxia.yunxi.chemmes.framework.common.pojo.PageResult;
import com.ningxia.yunxi.chemmes.framework.common.util.object.BeanUtils; import com.ningxia.yunxi.chemmes.framework.common.util.object.BeanUtils;
import com.ningxia.yunxi.chemmes.framework.tenant.core.aop.TenantIgnore;
import com.ningxia.yunxi.chemmes.module.infra.api.logger.dto.ApiAccessLogCreateReqDTO; import com.ningxia.yunxi.chemmes.module.infra.api.logger.dto.ApiAccessLogCreateReqDTO;
import com.ningxia.yunxi.chemmes.module.infra.dal.dataobject.logger.ApiAccessLogDO;
import com.ningxia.yunxi.chemmes.module.infra.controller.admin.logger.vo.apiaccesslog.ApiAccessLogPageReqVO; import com.ningxia.yunxi.chemmes.module.infra.controller.admin.logger.vo.apiaccesslog.ApiAccessLogPageReqVO;
import com.ningxia.yunxi.chemmes.module.infra.dal.dataobject.logger.ApiAccessLogDO;
import com.ningxia.yunxi.chemmes.module.infra.dal.mysql.logger.ApiAccessLogMapper; import com.ningxia.yunxi.chemmes.module.infra.dal.mysql.logger.ApiAccessLogMapper;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -27,6 +28,7 @@ public class ApiAccessLogServiceImpl implements ApiAccessLogService {
private ApiAccessLogMapper apiAccessLogMapper; private ApiAccessLogMapper apiAccessLogMapper;
@Override @Override
@TenantIgnore
public void createApiAccessLog(ApiAccessLogCreateReqDTO createDTO) { public void createApiAccessLog(ApiAccessLogCreateReqDTO createDTO) {
ApiAccessLogDO apiAccessLog = BeanUtils.toBean(createDTO, ApiAccessLogDO.class); ApiAccessLogDO apiAccessLog = BeanUtils.toBean(createDTO, ApiAccessLogDO.class);
if (apiAccessLog.getResultMsg().length()>200){ if (apiAccessLog.getResultMsg().length()>200){

View File

@ -217,6 +217,7 @@
<div class="product-detail"> <div class="product-detail">
<div class="section-header"> <div class="section-header">
<h3 class="section-title">产品明细</h3> <h3 class="section-title">产品明细</h3>
<el-button type="primary" plain size="small" @click="openMaterialSelect"> <el-button type="primary" plain size="small" @click="openMaterialSelect">
<Icon icon="ep:plus" /> 新增 <Icon icon="ep:plus" /> 新增
</el-button> </el-button>
@ -272,13 +273,53 @@
<!-- 附件信息区域 --> <!-- 附件信息区域 -->
<div class="attachment-info"> <div class="attachment-info">
<h3 class="section-title">附件信息</h3> <h3 class="section-title">附件信息</h3>
<el-form-item label="附件"> <el-form-item label="上传附件">
<div class="upload-placeholder"> <el-upload
<el-button type="primary" plain> ref="uploadRef"
<Icon icon="ep:upload" class="mr-5px" /> 点击上传 :action="uploadUrl"
:headers="uploadHeaders"
:auto-upload="true"
:limit="10"
:on-success="handleUploadSuccess"
:on-error="handleUploadError"
:on-remove="handleRemoveFile"
:on-exceed="handleExceed"
class="upload-component"
@before-upload="handleBeforeUpload"
>
<el-button type="primary" plain size="small">
<Icon icon="ep:upload" class="mr-5px" /> 上传文件
</el-button> </el-button>
<span class="upload-tip">支持格式doc, docx, pdf, jpg, jpeg, png 单个文件不超过50MB</span> <template #tip>
<div class="upload-tip">
支持扩展名: xls, xlsx, doc, docx, pdf, jpg...
</div> </div>
</template>
</el-upload>
<!-- 文件列表表格 -->
<el-table
v-if="uploadedFiles.length > 0"
:data="uploadedFiles"
border
size="small"
class="mt-20px"
>
<el-table-column type="index" label="序号" width="80" align="center" />
<el-table-column prop="name" label="文件名称" align="center" />
<el-table-column prop="uploadTime" label="上传时间" width="200" align="center" />
<el-table-column label="操作" width="80" align="center">
<template #default="scope">
<el-button
type="text"
size="small"
@click="removeUploadedFile(scope.row)"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
</el-form-item> </el-form-item>
</div> </div>
@ -331,6 +372,12 @@ const deptTreeRef = ref()
const userList = ref<UserApi.UserVO[]>([]) const userList = ref<UserApi.UserVO[]>([])
const userSelectLoading = ref(false) const userSelectLoading = ref(false)
//
const uploadRef = ref()
const uploadUrl = import.meta.env.VITE_UPLOAD_URL
const uploadHeaders = ref({})
const uploadedFiles = ref<Array<{ id: string; name: string; uploadTime: string; url: string }>>([])
// //
const purposeOptions = ref([]) const purposeOptions = ref([])
@ -497,6 +544,24 @@ const open = async (type: string, id?: number) => {
const rows = Array.isArray(rawItems) ? rawItems : (rawItems != null ? [rawItems] : []) const rows = Array.isArray(rawItems) ? rawItems : (rawItems != null ? [rawItems] : [])
productList.value = rows.length ? rows.map((r) => ({ ...r })) : [] productList.value = rows.length ? rows.map((r) => ({ ...r })) : []
//
const rawAttFile = data?.attFile
if (rawAttFile) {
try {
const attFileArray = typeof rawAttFile === 'string' ? JSON.parse(rawAttFile) : rawAttFile
if (Array.isArray(attFileArray)) {
uploadedFiles.value = attFileArray.map((item: any) => ({
id: item.filePath?.split('/').pop() || Date.now().toString(),
name: item.fileName,
uploadTime: item.uploadTime || '-',
url: item.filePath
}))
}
} catch (e) {
console.error('解析附件信息失败', e)
}
}
console.log('处理后productList:', productList.value) console.log('处理后productList:', productList.value)
} finally { } finally {
formLoading.value = false formLoading.value = false
@ -532,6 +597,16 @@ const submitForm = async () => {
await formRef.value?.validate?.() await formRef.value?.validate?.()
formLoading.value = true formLoading.value = true
// formData.attFile
if (uploadedFiles.value.length > 0) {
formData.attFile = JSON.stringify(uploadedFiles.value.map(item => ({
fileName: item.name,
filePath: item.url,
uploadTime: item.uploadTime
})))
}
const data = { const data = {
...formData, ...formData,
items: productList.value, items: productList.value,
@ -561,6 +636,16 @@ const submitAudit = async () => {
await formRef.value?.validate?.() await formRef.value?.validate?.()
formLoading.value = true formLoading.value = true
// formData.attFile
if (uploadedFiles.value.length > 0) {
formData.attFile = JSON.stringify(uploadedFiles.value.map(item => ({
fileName: item.name,
filePath: item.url,
uploadTime: item.uploadTime
})))
}
const data = { const data = {
...formData, ...formData,
ordStatus: 2, ordStatus: 2,
@ -580,6 +665,9 @@ const submitAudit = async () => {
} }
const resetForm = () => { const resetForm = () => {
//
uploadedFiles.value = []
// //
formData.id = undefined formData.id = undefined
formData.saleOrdNo = '自动生成' formData.saleOrdNo = '自动生成'
@ -771,6 +859,59 @@ const validateProductList = () => {
const removeProductItem = (index: number) => { const removeProductItem = (index: number) => {
productList.value.splice(index, 1) productList.value.splice(index, 1)
} }
//
const uploadStartTime = ref<Map<string, number>>(new Map())
//
const handleBeforeUpload = (file: any) => {
uploadStartTime.value.set(file.name, Date.now())
}
//
const handleUploadSuccess = (response: any, file: any, fileList: any) => {
if (response.code === 0) {
const fileUrl = response.data
//
const startTime = uploadStartTime.value.get(file.name) || Date.now()
const uploadDuration = ((Date.now() - startTime) / 1000).toFixed(2) + 's'
uploadStartTime.value.delete(file.name)
const fileId = fileUrl.split('/').pop() || Date.now().toString()
uploadedFiles.value.push({
id: fileId,
name: file.name,
uploadTime: uploadDuration, // "2.35s"
url: fileUrl
})
// ...
}
}
//
const handleUploadError = (error: any, file: any, fileList: any) => {
uploadStartTime.value.delete(file.name)
message.error('文件上传失败:' + (error.message || '未知错误'))
}
//
const handleRemoveFile = (file: any, fileList: any) => {
//
}
//
const handleExceed = (files: any, fileList: any) => {
message.warning(`最多只能上传10个文件`)
}
//
const removeUploadedFile = (row: any) => {
const index = uploadedFiles.value.findIndex(item => item.id === row.id)
if (index > -1) {
uploadedFiles.value.splice(index, 1)
message.success('文件删除成功')
}
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -832,8 +973,9 @@ const removeProductItem = (index: number) => {
.section-header { .section-header {
display: flex; display: flex;
justify-content: space-between; justify-content: flex-start;
align-items: center; align-items: center;
gap: 10px;
margin-bottom: 15px; margin-bottom: 15px;
} }
@ -871,10 +1013,17 @@ const removeProductItem = (index: number) => {
z-index: 2001; z-index: 2001;
} }
.upload-component {
margin-bottom: 10px;
}
.upload-tip { .upload-tip {
display: block;
margin-top: 8px;
font-size: 12px; font-size: 12px;
color: #909399; color: #909399;
margin-top: 8px;
}
.mt-20px {
margin-top: 20px;
} }
</style> </style>

View File

@ -101,7 +101,7 @@
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="合同编号" align="center" prop="contractNo" width="120px" /> <el-table-column label="合同编号" align="center" prop="contractNo" width="120px" />
<el-table-column label="业务人员" align="center" prop="saleMan" width="120px" /> <el-table-column label="业务人员" align="center" prop="saleManName" width="120px" />
<el-table-column label="是否变更" align="center" prop="isChange" width="120px"> <el-table-column label="是否变更" align="center" prop="isChange" width="120px">
<template #default="scope"> <template #default="scope">
<dict-tag :type="DICT_TYPE.SYSTEM_IS_CELL" :value="scope.row.isChange" /> <dict-tag :type="DICT_TYPE.SYSTEM_IS_CELL" :value="scope.row.isChange" />
@ -125,8 +125,16 @@
<dict-tag :type="DICT_TYPE.SHIPPING_STATUS" :value="scope.row.deliveryStatus" /> <dict-tag :type="DICT_TYPE.SHIPPING_STATUS" :value="scope.row.deliveryStatus" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" align="center" width="180px" fixed="right"> <el-table-column label="操作" align="center" width="300px" fixed="right">
<template #default="scope"> <template #default="scope">
<el-button
link
type="primary"
@click="openDetail(scope.row.id)"
v-hasPermi="['biz:order:query']"
>
详情
</el-button>
<el-button <el-button
link link
type="primary" type="primary"
@ -136,13 +144,22 @@
编辑 编辑
</el-button> </el-button>
<el-button <el-button
link
type="warning"
v-if="scope.row.ordStatus === 1"
@click="handleInvalidate(scope.row)"
v-hasPermi="['biz:order:update']"
>
作废
</el-button>
<!-- <el-button
link link
type="danger" type="danger"
@click="handleDelete(scope.row.id)" @click="handleDelete(scope.row.id)"
v-hasPermi="['biz:order:delete']" v-hasPermi="['biz:order:delete']"
> >
删除 删除
</el-button> </el-button> -->
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -157,6 +174,8 @@
<!-- 表单弹窗添加/修改 --> <!-- 表单弹窗添加/修改 -->
<OrderForm ref="formRef" @success="getList" @close="handleQuery"/> <OrderForm ref="formRef" @success="getList" @close="handleQuery"/>
<!-- 详情页面 -->
<OrderDetail ref="detailRef" />
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@ -164,6 +183,7 @@ import { getStrDictOptions, DICT_TYPE } from '@/utils/dict'
import { dateFormatter, getCurrentMonthRange } from '@/utils/formatTime' import { dateFormatter, getCurrentMonthRange } from '@/utils/formatTime'
import * as OrderApi from '@/api/biz/tsoorder/index' import * as OrderApi from '@/api/biz/tsoorder/index'
import OrderForm from './OrderForm.vue' import OrderForm from './OrderForm.vue'
import OrderDetail from './OrderDetail.vue'
defineOptions({ name: 'TsoOrder' }) defineOptions({ name: 'TsoOrder' })
@ -223,6 +243,12 @@ const openForm = (type: string, id?: number) => {
formRef.value.open(type, id) formRef.value.open(type, id)
} }
/** 详情操作 */
const detailRef = ref()
const openDetail = (id: number) => {
detailRef.value.open(id)
}
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = async (id: number) => { const handleDelete = async (id: number) => {
try { try {
@ -236,9 +262,26 @@ const handleDelete = async (id: number) => {
} catch {} } catch {}
} }
/** 作废按钮操作 */
const handleInvalidate = async (row: any) => {
try {
//
await message.confirm(`是否确认作废订单编号为"${row.saleOrdNo}"的数据项?`)
// 9
await OrderApi.updateOrder({
id: row.id,
ordStatus: 9
})
message.success('作废成功')
//
await getList()
} catch {}
}
/** 初始化 **/ /** 初始化 **/
onMounted(() => { onMounted(() => {
// // 1
queryParams.ordDate = getCurrentMonthRange()
}) })
</script> </script>