feat(biz): 订单模块新增详情查看和作废功能并优化附件管理
This commit is contained in:
parent
51b7227c5f
commit
18b50cee6f
@ -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());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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){
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user