让步接收审批前端代码
修改提交明细页面
This commit is contained in:
parent
d0f1a35d70
commit
9b6d5bb0f1
@ -90,4 +90,9 @@ export const concessionApplyApi = {
|
||||
return request({ url: `/biz/concessionApply/template`, method: 'get', responseType: 'blob' })
|
||||
},
|
||||
|
||||
//撤回审批申请
|
||||
approveCancel(params?:{id?:number}){
|
||||
return request({url:`/biz/concessionApply/approveCancel`,method:'get',params})
|
||||
}
|
||||
|
||||
}
|
||||
@ -24,7 +24,11 @@ export interface QcResultDetail {
|
||||
|
||||
updateTime?: string
|
||||
|
||||
files?:any[]
|
||||
files?:any[],
|
||||
|
||||
title?:string,
|
||||
images?:any[],
|
||||
attachments?:any[]
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { request } from '@/utils/request'
|
||||
import { F } from 'vue-router/dist/router-CWoNjPRp.mjs'
|
||||
|
||||
// 分页结果
|
||||
export interface PageResult<T> {
|
||||
@ -371,6 +372,34 @@ export interface SysFile {
|
||||
remark?: string
|
||||
}
|
||||
|
||||
|
||||
export interface DingTalkImage{
|
||||
id?:number
|
||||
mainId?:number
|
||||
fileName?:string
|
||||
fileSuffix?:string
|
||||
fileSize?:number
|
||||
localFilePath?:string
|
||||
fileUrl?:string
|
||||
sortNum?:number
|
||||
createTime?:string
|
||||
}
|
||||
|
||||
export interface DingTalkAttachment{
|
||||
id?:number
|
||||
mainId?:number
|
||||
fileName?:string
|
||||
fileSuffix?:string
|
||||
fileSize?:number
|
||||
localFilePath?:string
|
||||
ddSpaceId?:string
|
||||
ddFileId?:string
|
||||
uploadDdStatus?:string
|
||||
sortNum?:number
|
||||
createTime?:string
|
||||
|
||||
}
|
||||
|
||||
export const fileApi = {
|
||||
page(params: { page: number; pageSize: number; originalName?: string; fileType?: string }): Promise<PageResult<SysFile>> {
|
||||
return request({ url: '/sys/file/page', method: 'get', params })
|
||||
@ -407,6 +436,28 @@ export const fileApi = {
|
||||
})
|
||||
},
|
||||
|
||||
uploadDingImage(file: File):Promise<DingTalkImage> {
|
||||
const formData = new FormData()
|
||||
formData.append("file",file)
|
||||
return request({
|
||||
url:"/sys/file/uploadDingImage",
|
||||
method:'post',
|
||||
data:formData
|
||||
})
|
||||
},
|
||||
|
||||
uploadDingAttachment(file: File):Promise<DingTalkAttachment> {
|
||||
const formData = new FormData()
|
||||
formData.append("file",file)
|
||||
|
||||
return request({
|
||||
url:"/sys/file/uploadDingAttachment",
|
||||
method:"post",
|
||||
data:formData
|
||||
})
|
||||
},
|
||||
|
||||
|
||||
uploadAndDingding(file: File, path?: string, groupId?: number | null): Promise<SysFile>{
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
@ -441,6 +492,8 @@ export const fileApi = {
|
||||
})
|
||||
},
|
||||
|
||||
|
||||
|
||||
getDownloadUrl(id: number): string {
|
||||
return `/api/sys/file/download/${id}`
|
||||
},
|
||||
|
||||
@ -140,7 +140,7 @@
|
||||
</template>
|
||||
</n-modal>
|
||||
|
||||
<!-- 审批记录弹窗 -->
|
||||
<!-- 审批记录弹窗 -->
|
||||
<n-modal v-model:show="recordModalVisible" ref="exportWrapRef" preset="card" title="审批记录" style="width: 800px">
|
||||
<n-card bordered style="width:100%;max-width:800px;padding:24px;position:relative;">
|
||||
<div class="header-bar" style="display:flex;justify-content:space-between;align-items:center;margin-bottom:16px;">
|
||||
@ -217,14 +217,38 @@
|
||||
|
||||
</n-modal>
|
||||
|
||||
|
||||
<n-image-group
|
||||
v-model:show="showRef"
|
||||
v-model:current="currentIndex"
|
||||
:src-list="previewSrcList"
|
||||
/>
|
||||
|
||||
<n-modal v-model:show="fileModalShow" title="附件列表" style="width: 400px;">
|
||||
<n-space vertical :size="12">
|
||||
<div v-for="(item, idx) in fileList" :key="idx" style="display:flex;align-items:center;justify-content:space-between;">
|
||||
<span>{{ item.fileName }}</span>
|
||||
<n-button
|
||||
size="small"
|
||||
type="primary"
|
||||
@click="window.open(item, '_blank')"
|
||||
>
|
||||
下载
|
||||
</n-button>
|
||||
</div>
|
||||
</n-space>
|
||||
</n-modal>
|
||||
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, h, onMounted } from 'vue'
|
||||
import { NButton, NSpace, NIcon, NUpload, useMessage,NTag, useDialog, type DataTableColumns, type UploadCustomRequestOptions } from 'naive-ui'
|
||||
import { NButton, NSpace, NIcon, NUpload, useMessage,NTag, useDialog, type DataTableColumns, type UploadCustomRequestOptions, NDropdown, NImage } from 'naive-ui'
|
||||
import { SearchOutline, RefreshOutline, AddOutline, TrashOutline, CreateOutline, CloudUploadOutline, DownloadOutline,DocumentTextOutline,
|
||||
CashOutline as CashIcon
|
||||
CashOutline as CashIcon,
|
||||
EllipsisHorizontalCircle
|
||||
} from '@vicons/ionicons5'
|
||||
import { concessionApplyApi, DetailModel, type ConcessionApply } from '@/api/concessionApply'
|
||||
import { dictDataApi } from '@/api/org'
|
||||
@ -287,6 +311,13 @@ const approveStatusOptions = ref<{ label: string; value: any }[]>([])
|
||||
const formRules = {
|
||||
}
|
||||
|
||||
const showRef = ref(false)
|
||||
const currentIndex = ref(0)
|
||||
const previewSrcList = ref([])
|
||||
|
||||
const fileModalShow = ref(false)
|
||||
const fileList = ref([])
|
||||
|
||||
// 表格列
|
||||
const columns: DataTableColumns<ConcessionApply> = [
|
||||
{ type: 'selection' },
|
||||
@ -295,7 +326,68 @@ const columns: DataTableColumns<ConcessionApply> = [
|
||||
{ title: '让步接收数量', key: 'concessionQty',align:"center",minWidth:"150" },
|
||||
{ title: '申请原因', key: 'applyRemark',align:"center",minWidth:"150" },
|
||||
{ title: '申请内容', key: 'applyContent',align:"center",minWidth:"200" },
|
||||
{ title: '申请附件', key: 'applyFile',align:"center",minWidth:"150" },
|
||||
{
|
||||
title: '申请图片',
|
||||
key: 'applyImages',
|
||||
align: "center",
|
||||
minWidth: "100",
|
||||
render(row) {
|
||||
// 处理图片路径,分割成数组并过滤空值
|
||||
const imageUrls = row.applyImages?.split(",").filter(url => url.trim() !== "") || [];
|
||||
if (imageUrls.length === 0) return "-";
|
||||
|
||||
// 拼接成完整的可访问URL
|
||||
const fullUrls = imageUrls.map(path => {
|
||||
if (path.startsWith("http")) return path;
|
||||
return `${window.location.origin}${path}`;
|
||||
});
|
||||
|
||||
// 3. 渲染缩略图:只显示第一张 + 带 +n 标识
|
||||
return h('div', {
|
||||
style: 'display: flex; align-items: center; justify-content: center; gap: 6px;'
|
||||
}, [
|
||||
// 第一张缩略图
|
||||
h('img', {
|
||||
src: fullUrls[0],
|
||||
alt: `申请图片`,
|
||||
style: 'width: 60px; height: 60px; object-fit: cover; border-radius: 4px; cursor: pointer;',
|
||||
onClick: () => {
|
||||
// 点击缩略图时,给预览组件赋值并打开
|
||||
previewSrcList.value = fullUrls;
|
||||
currentIndex.value = 0;
|
||||
showRef.value = true;
|
||||
}
|
||||
}),
|
||||
// 多图标识
|
||||
fullUrls.length > 1 ? h('span', {
|
||||
style: 'font-size: 12px; color: #666; background: #f0f0f0; padding: 2px 6px; border-radius: 10px;'
|
||||
}, `+${fullUrls.length - 1}`) : null
|
||||
]);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '附件',
|
||||
key: 'applyAttachments',
|
||||
align: "center",
|
||||
minWidth: "180",
|
||||
render(row) {
|
||||
const files = row.applyAttachments?.split(",").filter(url => url.trim() !== "") || [];
|
||||
if (files.length === 0) return "-"
|
||||
|
||||
return h('div', {style: 'display:flex;flex-direction:column;gap:4px;'},
|
||||
files.map((item, idx) =>
|
||||
h('div', {key: idx, style: 'display:flex;align-items:center;gap:6px;'}, [
|
||||
h('span', {style: 'font-size:12px;'}, item.fileName),
|
||||
h('a', {
|
||||
href: item,
|
||||
target: '_blank',
|
||||
style: 'color:#409eff;font-size:12px;cursor:pointer;'
|
||||
}, '查看')
|
||||
])
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
{ title: '申请人', key: 'applyUserName',align:"center",minWidth:"150" },
|
||||
{ title: '部门名称', key: 'deptName',align:"center",minWidth:"150" },
|
||||
// { title: '钉钉单据编号', key: 'instanceId',align:"center",minWidth:"150" },
|
||||
@ -310,7 +402,7 @@ const columns: DataTableColumns<ConcessionApply> = [
|
||||
if(row.approveResult == '已通过') return h(NTag, { type: 'success', size: 'small' }, { default: () => row.approveResult })
|
||||
return row.approveResult
|
||||
}
|
||||
},
|
||||
},
|
||||
{ title: '审批人', key: 'approveUserName',align:"center",minWidth:"150" },
|
||||
{ title: '创建时间', key: 'createTime', width: 180 },
|
||||
{ title: '更新时间', key: 'updateTime', width: 180 },
|
||||
@ -327,9 +419,29 @@ const columns: DataTableColumns<ConcessionApply> = [
|
||||
h(NButton, { size: 'small', quaternary: true, type: 'error', onClick: () => handleDelete(row) }, {
|
||||
default: () => [h(NIcon, null, { default: () => h(TrashOutline) }), ' 删除']
|
||||
}),
|
||||
h(NButton, { size: 'small', quaternary: true, type: 'warring', onClick: () => handleViewApproveRecord(row) }, {
|
||||
default: () => [h(NIcon, null, { default: () => h(DocumentTextOutline) }), ' 审批记录']
|
||||
})
|
||||
h(NDropdown,{
|
||||
trigger: 'click',
|
||||
options:[
|
||||
{label:'审批记录',key:'approveRecord'},
|
||||
{type:'divider',show: row.approveStatus === '待审批'},
|
||||
{label:"撤回审批",key:'approveCancel',show: row.approveStatus === '待审批'}
|
||||
],
|
||||
onSelect:(key:string)=>{
|
||||
switch(key) {
|
||||
case 'approveRecord':
|
||||
handleViewApproveRecord(row)
|
||||
break;
|
||||
case 'approveCancel':
|
||||
handleCancel(row)
|
||||
break
|
||||
}
|
||||
}
|
||||
},{
|
||||
default:()=>h(NButton,{size:'small',quaternary:true},{
|
||||
default:()=>[h(NIcon,null,{default:()=>h(EllipsisHorizontalCircle)})]
|
||||
})
|
||||
}
|
||||
)
|
||||
])
|
||||
}
|
||||
}
|
||||
@ -715,6 +827,25 @@ async function downloadOutline() {
|
||||
}
|
||||
|
||||
|
||||
async function handleCancel(row) {
|
||||
|
||||
dialog.warning({
|
||||
title: '提示',
|
||||
content: `确定要撤回ID:${row.id}审批申请吗?`,
|
||||
positiveText: '确定',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: async () => {
|
||||
try{
|
||||
await concessionApplyApi.approveCancel({id:row.id});
|
||||
message.success('撤回成功')
|
||||
loadData()
|
||||
}catch(error){}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadData()
|
||||
loadDictOptions()
|
||||
|
||||
@ -224,139 +224,7 @@
|
||||
<!-- </n-card>-->
|
||||
</n-modal>
|
||||
|
||||
<!--提交判定明细面板-->
|
||||
<n-modal v-model:show="submitDetailModalVisible" preset="card" title="提交判定明细" style="width: 800px">
|
||||
<n-collapse default-expanded-names="['ok', 'scrap', 'rework','concession']">
|
||||
<n-collapse-item title="合格" name="ok">
|
||||
<n-form ref="formRef" :model="formDetailOKData" :rules="formRules" label-placement="left" label-width="100px">
|
||||
<n-grid x-gap="12" :cols="2">
|
||||
<n-gi>
|
||||
<n-form-item label="判定数量" path="qty">
|
||||
<n-input-number v-model:value="formDetailOKData.qty" placeholder="请输入判定数量" :min="0" />
|
||||
</n-form-item>
|
||||
</n-gi>
|
||||
<n-gi>
|
||||
<n-form-item label="批次号或SN码" path="traceCode" v-show="traceCodeShow">
|
||||
<n-input v-model:value="formDetailOKData.traceCode" placeholder="请输入批次号或SN码(启用追溯时必填)" />
|
||||
</n-form-item>
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
<n-form-item label="备注" >
|
||||
<n-input
|
||||
v-model:value="formDetailOKData.remark"
|
||||
type="textarea"
|
||||
placeholder="输入备注原因"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
</n-collapse-item>
|
||||
<n-collapse-item title="报废" name="scrap">
|
||||
<n-form ref="formRef" :model="formDetailScrapData" :rules="formRules" label-placement="left" label-width="100px">
|
||||
|
||||
<n-grid x-gap="12" :cols="2">
|
||||
<n-gi>
|
||||
<n-form-item label="判定数量" path="qty">
|
||||
<n-input-number v-model:value="formDetailScrapData.qty" placeholder="请输入判定数量" :min="0" />
|
||||
</n-form-item>
|
||||
</n-gi>
|
||||
<n-gi>
|
||||
<n-form-item label="批次号或SN码" path="traceCode" v-show="traceCodeShow">
|
||||
<n-input v-model:value="formDetailScrapData.traceCode" placeholder="请输入批次号或SN码(启用追溯时必填)" />
|
||||
</n-form-item>
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
<n-form-item label="备注" >
|
||||
<n-input
|
||||
v-model:value="formDetailScrapData.remark"
|
||||
type="textarea"
|
||||
placeholder="输入备注原因"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
</n-collapse-item>
|
||||
<n-collapse-item title="返工" name="rework">
|
||||
<n-form ref="formRef" :model="formDetailReworkData" :rules="formRules" label-placement="left" label-width="100px">
|
||||
<n-grid x-gap="12" :cols="2">
|
||||
<n-gi>
|
||||
<n-form-item label="判定数量" path="qty">
|
||||
<n-input-number v-model:value="formDetailReworkData.qty" placeholder="请输入判定数量" :min="0" />
|
||||
</n-form-item>
|
||||
</n-gi>
|
||||
<n-gi>
|
||||
<n-form-item label="批次号或SN码" path="traceCode" v-show="traceCodeShow">
|
||||
<n-input v-model:value="formDetailReworkData.traceCode" placeholder="请输入批次号或SN码(启用追溯时必填)" />
|
||||
</n-form-item>
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
<n-form-item label="备注" >
|
||||
<n-input
|
||||
v-model:value="formDetailReworkData.remark"
|
||||
type="textarea"
|
||||
placeholder="输入备注原因"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
</n-collapse-item>
|
||||
<n-collapse-item title="让步接收" name="concession">
|
||||
<n-form ref="formRef" :model="formDetailConcessionData" :rules="formRules" label-placement="left" label-width="100px">
|
||||
<n-grid x-gap="12" :cols="2">
|
||||
<n-gi>
|
||||
<n-form-item label="判定数量" path="qty">
|
||||
<n-input-number v-model:value="formDetailConcessionData.qty" placeholder="请输入判定数量" :min="0" />
|
||||
</n-form-item>
|
||||
</n-gi>
|
||||
<n-gi>
|
||||
<n-form-item label="批次号或SN码" path="traceCode" v-show="traceCodeShow">
|
||||
<n-input v-model:value="formDetailConcessionData.traceCode" placeholder="请输入批次号或SN码(启用追溯时必填)" />
|
||||
</n-form-item>
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
<n-form-item label="备注" >
|
||||
<n-input
|
||||
v-model:value="formDetailConcessionData.remark"
|
||||
type="textarea"
|
||||
placeholder="输入备注原因"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item label="附件">
|
||||
<n-upload
|
||||
multiple
|
||||
directory-dnd
|
||||
:max="5"
|
||||
:action="null"
|
||||
:custom-request="()=>{}"
|
||||
@change="handleUploadChange"
|
||||
:file-list="fileList"
|
||||
accept=".pdf,.doc,.docx,.xls,.xlsx,.jpg,.jpeg,.png,.gif,.bmp"
|
||||
:max-size="10*1024*1024"
|
||||
>
|
||||
<n-upload-dragger>
|
||||
<div style="margin-bottom: 12px">
|
||||
<n-icon size="48" :depth="3">
|
||||
<ArchiveIcon />
|
||||
</n-icon>
|
||||
</div>
|
||||
<n-text style="font-size: 16px">
|
||||
点击或者拖动文件到该区域来上传
|
||||
</n-text>
|
||||
<n-p depth="3" style="margin: 8px 0 0 0">
|
||||
仅支持 PDF、DOC、DOCX、XLS、XLSX、JPG、PNG、GIF,单文件最大10MB
|
||||
</n-p>
|
||||
</n-upload-dragger>
|
||||
</n-upload>
|
||||
</n-form-item>
|
||||
|
||||
|
||||
</n-form>
|
||||
</n-collapse-item>
|
||||
</n-collapse>
|
||||
<template #footer>
|
||||
<n-space justify="end">
|
||||
<n-button @click="submitDetailModalVisible = false">取消</n-button>
|
||||
<n-button type="primary" @click="handleSubmitResultDetail">确定</n-button>
|
||||
</n-space>
|
||||
</template>
|
||||
</n-modal>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
@ -385,6 +253,7 @@ import value = Language.value;
|
||||
import {QcResultDetail} from "@/api/qcResultDetail.ts";
|
||||
import router from "@/router";
|
||||
import dd from 'dingtalk-jsapi'
|
||||
import { env } from 'echarts'
|
||||
|
||||
const message = useMessage()
|
||||
const dialog = useDialog()
|
||||
@ -558,9 +427,6 @@ const defaultFormDetailData:QcResultDetail = {
|
||||
actionStatus: undefined
|
||||
}
|
||||
|
||||
//提交附件
|
||||
const fileList = ref<any[]>([])
|
||||
const uploadFiles = ref<File[]>([])
|
||||
|
||||
const formDetailOKData = reactive<QcResultDetail>({ ...defaultFormDetailData, resultType: 1,handleAction:'下一道工序',actionStatus:1 })
|
||||
const formDetailScrapData = reactive<QcResultDetail>({ ...defaultFormDetailData,resultType: 2,handleAction:'报废次品',actionStatus:1 })
|
||||
@ -977,19 +843,15 @@ async function loadAssignWorkOrder() {
|
||||
|
||||
//提交判定明细
|
||||
function submitResultDetail(row) {
|
||||
Object.assign(formDetailOKData, reactive<QcResultDetail>({ ...defaultFormDetailData, resultType: 1,handleAction:'下一道工序',actionStatus:1 }))
|
||||
Object.assign(formDetailScrapData, reactive<QcResultDetail>({ ...defaultFormDetailData,resultType: 2,handleAction:'报废次品',actionStatus:1 }))
|
||||
Object.assign(formDetailReworkData, reactive<QcResultDetail>({ ...defaultFormDetailData,resultType: 3,handleAction:'返工',actionStatus:1 }))
|
||||
Object.assign(formDetailConcessionData, reactive<QcResultDetail>({ ...defaultFormDetailData,resultType: 4,handleAction:'让步接收',actionStatus:1 }))
|
||||
|
||||
if (row.traceType != 1) {
|
||||
traceCodeShow.value = true
|
||||
}
|
||||
submitFlag.value = true
|
||||
submitDetailModalVisible.value = true
|
||||
formDetailOKData.qcId = row.id
|
||||
formDetailScrapData.qcId = row.id
|
||||
formDetailReworkData.qcId = row.id
|
||||
formDetailConcessionData.qcId = row.id
|
||||
|
||||
|
||||
let path = `/qc/submitDetail/${row.qcNo}/${row.id}/${submitFlag.value}/${traceCodeShow.value}`
|
||||
router.push(path)
|
||||
}
|
||||
|
||||
|
||||
@ -1015,20 +877,12 @@ async function handleSubmitResultDetail() {
|
||||
|
||||
//提交部分判定明细
|
||||
function submitBFResultDetail(row) {
|
||||
// Object.assign(formDetailOKData, reactive<QcResultDetail>({ ...defaultFormDetailData, resultType: 1,handleAction:'下一道工序',actionStatus:1 }))
|
||||
// Object.assign(formDetailScrapData, reactive<QcResultDetail>({ ...defaultFormDetailData,resultType: 2,handleAction:'报废次品',actionStatus:1 }))
|
||||
// Object.assign(formDetailReworkData, reactive<QcResultDetail>({ ...defaultFormDetailData,resultType: 3,handleAction:'返工',actionStatus:1 }))
|
||||
// Object.assign(formDetailConcessionData, reactive<QcResultDetail>({ ...defaultFormDetailData,resultType: 4,handleAction:'让步接收',actionStatus:1 }))
|
||||
|
||||
|
||||
if (row.traceType != 1) {
|
||||
traceCodeShow.value = true
|
||||
}
|
||||
submitFlag.value = false
|
||||
// submitDetailModalVisible.value = true
|
||||
// formDetailOKData.qcId = row.id
|
||||
// formDetailScrapData.qcId = row.id
|
||||
// formDetailReworkData.qcId = row.id
|
||||
// formDetailConcessionData.qcId = row.id
|
||||
|
||||
|
||||
//version-2
|
||||
@ -1064,67 +918,6 @@ function handleCancelQuality(row) {
|
||||
}
|
||||
|
||||
|
||||
const isDingTalkEnv = () => {
|
||||
// 确保 dd 对象存在,并且其 platform 属性不是 'notInDingTalk'
|
||||
return typeof dd !== 'undefined' && dd.env && dd.env.platform !== 'notInDingTalk';
|
||||
};
|
||||
|
||||
//上传附件
|
||||
async function handleUploadChange ({ file, fileList: list }: any) {
|
||||
|
||||
// 允许的后缀白名单
|
||||
const allowExt = ['pdf','doc','docx','xls','xlsx','jpg','jpeg','png','gif','bmp']
|
||||
const invalidFiles:any[] = []
|
||||
|
||||
list.forEach(item=>{
|
||||
const name = item.file.name.toLowerCase()
|
||||
const ext = name.split('.').pop()
|
||||
if(!allowExt.includes(ext)){
|
||||
invalidFiles.push(item.file.name)
|
||||
item.status = 'error'
|
||||
}
|
||||
})
|
||||
if(invalidFiles.length){
|
||||
window.$message.error(`【${invalidFiles.join('、')}】文件格式不支持`)
|
||||
}
|
||||
|
||||
|
||||
|
||||
fileList.value = list
|
||||
console.log('选中的文件列表', fileList.value)
|
||||
|
||||
|
||||
const validList = list.filter(item =>
|
||||
item.status === 'pending' && item.file && item.status !== 'error'
|
||||
)
|
||||
|
||||
const successIds = list.filter(x=>x.status==='success' && x.file).map(x=>x.id)
|
||||
// 只保留页面还存在的附件信息
|
||||
uploadFiles.value = uploadFiles.value.filter(item=> successIds.includes(item.uid))
|
||||
|
||||
// 只上传pending新文件
|
||||
const pendingList = list.filter(item => item.status === 'pending' && item.file && item.status !== 'error')
|
||||
if(pendingList.length === 0) return
|
||||
|
||||
const uploadPromises = validList.map(async (item: any) => {
|
||||
try {
|
||||
const res = await fileApi.uploadAndDingding(item.file, null, null)
|
||||
uploadFiles.value.push(res)
|
||||
const targetFile = fileList.value.find(f => f.id === item.id)
|
||||
if (targetFile) {
|
||||
targetFile.status = 'success'
|
||||
targetFile.percentage = 100
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('文件上传失败', error)
|
||||
const targetFile = fileList.value.find(f => f.id === item.id)
|
||||
if (targetFile) targetFile.status = 'error'
|
||||
}
|
||||
})
|
||||
|
||||
await Promise.all(uploadPromises)
|
||||
}
|
||||
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
@ -87,17 +87,18 @@
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="让步接收" tab="让步接收">
|
||||
<n-form ref="formRef" :model="formDetailConcessionData" label-placement="left" label-width="100px">
|
||||
|
||||
<n-grid x-gap="12" :cols="2">
|
||||
<n-gi>
|
||||
<n-form-item label="判定数量" path="qty">
|
||||
<n-input-number v-model:value="formDetailConcessionData.qty" placeholder="请输入判定数量" :min="0" />
|
||||
</n-form-item>
|
||||
</n-gi>
|
||||
<n-gi>
|
||||
<n-form-item label="批次号或SN码" path="traceCode" v-show="traceCodeShow">
|
||||
<n-input v-model:value="formDetailConcessionData.traceCode" placeholder="请输入批次号或SN码(启用追溯时必填)" />
|
||||
</n-form-item>
|
||||
</n-gi>
|
||||
<n-gi>
|
||||
<n-form-item label="判定数量" path="qty">
|
||||
<n-input-number v-model:value="formDetailConcessionData.qty" placeholder="请输入判定数量" :min="0" />
|
||||
</n-form-item>
|
||||
</n-gi>
|
||||
<n-gi>
|
||||
<n-form-item label="批次号或SN码" path="traceCode" v-show="traceCodeShow">
|
||||
<n-input v-model:value="formDetailConcessionData.traceCode" placeholder="请输入批次号或SN码(启用追溯时必填)" />
|
||||
</n-form-item>
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
<n-grid x-gap="8" :cols="3">
|
||||
<n-gi>
|
||||
@ -110,6 +111,22 @@
|
||||
</n-form-item>
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
<n-grid x-gap="8" :cols="3">
|
||||
<n-gi>
|
||||
<n-form-item label="图片">
|
||||
<n-upload
|
||||
action="null"
|
||||
:max="5"
|
||||
:custom-request="()=>{}"
|
||||
@change="handleUploadImageChange"
|
||||
v-model:file-list="imageList"
|
||||
list-type="image-card"
|
||||
>
|
||||
点击上传
|
||||
</n-upload>
|
||||
</n-form-item>
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
<n-grid x-gap="8" :cols="3">
|
||||
<n-gi>
|
||||
<n-form-item label="附件">
|
||||
@ -120,8 +137,8 @@
|
||||
:action="null"
|
||||
:custom-request="()=>{}"
|
||||
@change="handleUploadChange"
|
||||
:file-list="fileList"
|
||||
accept=".pdf,.doc,.docx,.xls,.xlsx,.jpg,.jpeg,.png,.gif,.bmp"
|
||||
v-model:file-list="fileList"
|
||||
accept=".pdf,.doc,.docx,.xls,.xlsx"
|
||||
:max-size="10*1024*1024"
|
||||
>
|
||||
<n-upload-dragger>
|
||||
@ -167,6 +184,7 @@ import {useRoute} from "vue-router";
|
||||
import {QcResultDetail} from "@/api/qcResultDetail.ts";
|
||||
import {fileApi} from '@/api/system1'
|
||||
import { qualityTestingApi, type QualityTesting } from '@/api/qualityTesting'
|
||||
import router from '@/router';
|
||||
|
||||
const message = useMessage()
|
||||
const route = useRoute()
|
||||
@ -175,9 +193,19 @@ const qcId =ref( Number(route.params.id))
|
||||
const traceCodeShow = ref(route.params.traceCodeShow === 'true')
|
||||
const submitFlag = ref(route.params.submitFlag === 'true')
|
||||
|
||||
|
||||
const MAX_FILE_SIZE = 10 * 1024 * 1024
|
||||
//提交图片
|
||||
const imageList = ref<any[]>([])
|
||||
const uploadImages = ref<File[]>([])
|
||||
const imageExts = ['jpg', 'jpeg', 'png']
|
||||
|
||||
|
||||
//提交附件
|
||||
const fileList = ref<any[]>([])
|
||||
const uploadFiles = ref<File[]>([])
|
||||
const fileExts = ['pdf', 'doc', 'docx', 'xls', 'xlsx']
|
||||
|
||||
|
||||
const title = ref(`质检编号: ${route.params.qcNo}`)
|
||||
|
||||
@ -189,7 +217,11 @@ const defaultFormDetailData:QcResultDetail = {
|
||||
traceCode: undefined,
|
||||
defectCode: undefined,
|
||||
handleAction: undefined,
|
||||
actionStatus: undefined
|
||||
actionStatus: undefined,
|
||||
remark: '',
|
||||
title:undefined,
|
||||
images:[],
|
||||
attachments:[]
|
||||
}
|
||||
|
||||
const formDetailOKData = reactive<QcResultDetail>({ ...defaultFormDetailData, resultType: 1,handleAction:'下一道工序',actionStatus:1 })
|
||||
@ -200,7 +232,13 @@ const formDetailConcessionData = reactive<QcResultDetail>({ ...defaultFormDetail
|
||||
|
||||
async function handleSubmitResultDetail() {
|
||||
|
||||
formDetailConcessionData.files = uploadFiles.value
|
||||
formDetailOKData.qcId = qcId.value
|
||||
formDetailScrapData.qcId = qcId.value
|
||||
formDetailReworkData.qcId = qcId.value
|
||||
formDetailConcessionData.qcId =qcId.value
|
||||
|
||||
formDetailConcessionData.images = uploadImages.value
|
||||
formDetailConcessionData.attachments = uploadFiles.value
|
||||
|
||||
const submitParam = [
|
||||
formDetailOKData,
|
||||
@ -208,68 +246,127 @@ async function handleSubmitResultDetail() {
|
||||
formDetailReworkData,
|
||||
formDetailConcessionData]
|
||||
|
||||
console.log(submitParam);
|
||||
|
||||
|
||||
submitFlag.value? await qualityTestingApi.submit(submitParam) : await qualityTestingApi.submitBF(submitParam)
|
||||
|
||||
message.success('提交成功')
|
||||
router.go(-1)
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
async function handleUploadChange ({ file, fileList: list }: any) {
|
||||
async function handleUploadChange ({ file, fileList }: { file: any, fileList: any[] }) {
|
||||
const invalidNames: string[] = []
|
||||
|
||||
// 允许的后缀白名单
|
||||
const allowExt = ['pdf','doc','docx','xls','xlsx','jpg','jpeg','png','gif','bmp']
|
||||
const invalidFiles:any[] = []
|
||||
fileList.forEach(item => {
|
||||
if (!item.file) return
|
||||
const f = item.file
|
||||
const ext = f.name.toLowerCase().split('.').pop() || ''
|
||||
|
||||
list.forEach(item=>{
|
||||
const name = item.file.name.toLowerCase()
|
||||
const ext = name.split('.').pop()
|
||||
if(!allowExt.includes(ext)){
|
||||
invalidFiles.push(item.file.name)
|
||||
if (!fileExts.includes(ext)) {
|
||||
invalidNames.push(f.name)
|
||||
item.status = 'error'
|
||||
}
|
||||
})
|
||||
if(invalidFiles.length){
|
||||
window.$message.error(`【${invalidFiles.join('、')}】文件格式不支持`)
|
||||
}
|
||||
|
||||
|
||||
|
||||
fileList.value = list
|
||||
console.log('选中的文件列表', fileList.value)
|
||||
|
||||
|
||||
const validList = list.filter(item =>
|
||||
item.status === 'pending' && item.file && item.status !== 'error'
|
||||
)
|
||||
|
||||
const successIds = list.filter(x=>x.status==='success' && x.file).map(x=>x.id)
|
||||
// 只保留页面还存在的附件信息
|
||||
uploadFiles.value = uploadFiles.value.filter(item=> successIds.includes(item.uid))
|
||||
|
||||
// 只上传pending新文件
|
||||
const pendingList = list.filter(item => item.status === 'pending' && item.file && item.status !== 'error')
|
||||
if(pendingList.length === 0) return
|
||||
|
||||
const uploadPromises = validList.map(async (item: any) => {
|
||||
try {
|
||||
const res = await fileApi.uploadAndDingding(item.file, null, null)
|
||||
uploadFiles.value.push(res)
|
||||
const targetFile = fileList.value.find(f => f.id === item.id)
|
||||
if (targetFile) {
|
||||
targetFile.status = 'success'
|
||||
targetFile.percentage = 100
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('文件上传失败', error)
|
||||
const targetFile = fileList.value.find(f => f.id === item.id)
|
||||
if (targetFile) targetFile.status = 'error'
|
||||
if (invalidNames.length) {
|
||||
message.error(`格式不支持:${invalidNames.join('、')}`)
|
||||
}
|
||||
})
|
||||
|
||||
await Promise.all(uploadPromises)
|
||||
const pendingList = fileList.filter(
|
||||
item => item.status === 'pending' && item.file && item.status !== 'error'
|
||||
)
|
||||
if (pendingList.length === 0) return
|
||||
|
||||
|
||||
const promises = pendingList.map(async (item) => {
|
||||
try {
|
||||
const res = await fileApi.uploadDingAttachment(item.file)
|
||||
uploadFiles.value.push({ uid: item.id, ...res })
|
||||
item.status = 'success'
|
||||
item.percentage = 100
|
||||
message.success("附件上传成功")
|
||||
} catch (err) {
|
||||
console.error('附件上传失败', err)
|
||||
item.status = 'error'
|
||||
}
|
||||
})
|
||||
|
||||
// await Promise.all(promises)
|
||||
|
||||
|
||||
// const successIds = fileList.filter(x => x.status === 'success').map(x => x.id)
|
||||
// uploadFiles.value = uploadFiles.value.filter(att => successIds.includes(att.uid))
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
async function handleUploadImageChange({ file, fileList }: { file: any, fileList: any[] }) {
|
||||
|
||||
const invalidNames: string[] = []
|
||||
fileList.forEach(item => {
|
||||
if (!item.file) return
|
||||
const f = item.file
|
||||
const ext = f.name.toLowerCase().split('.').pop() || ''
|
||||
|
||||
if (!imageExts.includes(ext)) {
|
||||
invalidNames.push(f.name)
|
||||
item.status = 'error'
|
||||
}
|
||||
if (f.size > MAX_FILE_SIZE) {
|
||||
message.error(`${f.name} 超出10MB大小限制`)
|
||||
item.status = 'error'
|
||||
}
|
||||
})
|
||||
|
||||
if (invalidNames.length) {
|
||||
message.error(`格式不支持:${invalidNames.join('、')}`)
|
||||
}
|
||||
|
||||
// 2. 筛选待上传合法文件
|
||||
const pendingList = fileList.filter(
|
||||
item => item.status === 'pending' && item.file && item.status !== 'error'
|
||||
)
|
||||
if (pendingList.length === 0) return
|
||||
|
||||
// 3. 批量上传
|
||||
const promises = pendingList.map(async (item) => {
|
||||
try {
|
||||
const res = await fileApi.uploadDingImage(item.file)
|
||||
uploadImages.value.push({ uid: item.id, ...res })
|
||||
item.status = 'success'
|
||||
item.percentage = 100
|
||||
message.success("上传成功")
|
||||
} catch (err) {
|
||||
console.error('图片上传失败', err)
|
||||
item.status = 'error'
|
||||
}
|
||||
})
|
||||
|
||||
// await Promise.all(promises)
|
||||
|
||||
|
||||
// const successIds = fileList.filter(x => x.status === 'success').map(x => x.id)
|
||||
// uploadImages.value = uploadImages.value.filter(img => successIds.includes(img.uid))
|
||||
|
||||
}
|
||||
|
||||
function handleReset() {
|
||||
Object.assign(formDetailOKData, { ...defaultFormDetailData, resultType: 1, handleAction: '下一道工序', actionStatus: 1 })
|
||||
Object.assign(formDetailScrapData, { ...defaultFormDetailData, resultType: 2, handleAction: '报废次品', actionStatus: 1 })
|
||||
Object.assign(formDetailReworkData, { ...defaultFormDetailData, resultType: 3, handleAction: '返工', actionStatus: 1 })
|
||||
Object.assign(formDetailConcessionData, { ...defaultFormDetailData, resultType: 4, handleAction: '让步接收', actionStatus: 1 })
|
||||
|
||||
imageList.value = []
|
||||
uploadImages.value = []
|
||||
fileList.value = []
|
||||
uploadFiles.value = []
|
||||
|
||||
message.info("表单已重置")
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -58,6 +58,18 @@
|
||||
</div>
|
||||
</n-card>
|
||||
</div>
|
||||
<n-grid :cols="2" :x-gap="12">
|
||||
<n-gi>
|
||||
<n-card title="产出率" size="small" :bordered="false" content-style="background: transparent">
|
||||
<n-progress type="circle" status="success" :percentage="stats.outputRate" />
|
||||
</n-card>
|
||||
</n-gi>
|
||||
<n-gi>
|
||||
<n-card title="报废率" size="small" :bordered="false" content-style="background: transparent">
|
||||
<n-progress type="circle" status="error" :percentage="stats.scrapRate" />
|
||||
</n-card>
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
<n-grid :cols="1" :x-gap="24" class="stats-charts">
|
||||
<n-gi>
|
||||
<n-card title="汇报统计图" size="small" :bordered="false" content-style="background: transparent">
|
||||
@ -115,6 +127,9 @@ async function loadStatistics() {
|
||||
|
||||
Object.assign(stats,res)
|
||||
|
||||
|
||||
console.log(stats);
|
||||
|
||||
updateCharts()
|
||||
}catch{}
|
||||
}
|
||||
|
||||
@ -275,7 +275,7 @@
|
||||
|
||||
<!--查询汇报详情-->
|
||||
|
||||
<n-modal v-model:show="reportDetailVisible" preset="card" :title="reportDetailTitle" style="width: 800px">
|
||||
<n-modal v-model:show="reportDetailVisible" preset="card" :title="reportDetailTitle" style="width: 1000px">
|
||||
<n-card hoverable style="margin-bottom: 12px" >
|
||||
<template #header>
|
||||
<div style="display:flex;justify-content:space-between;align-items:center;width:100%">
|
||||
|
||||
Loading…
Reference in New Issue
Block a user