|
|
|
@ -213,10 +213,37 @@ |
|
|
|
</el-footer> |
|
|
|
</el-dialog> |
|
|
|
|
|
|
|
<el-dialog title="机加工生产报工" :visible.sync="setUp.reportFlag" width="460px" :close-on-click-modal="false" v-drag> |
|
|
|
<el-dialog title="机加工节点报工" :visible.sync="setUp.reportFlag" width="460px" :close-on-click-modal="false" @close="handleReportDialogClose" v-drag> |
|
|
|
<el-form :model="reportData" label-width="110px" label-position="top"> |
|
|
|
<el-form-item label="任务单号"><el-input v-model="reportData.taskNo" disabled></el-input></el-form-item> |
|
|
|
<el-form-item label="本次报工数量"><el-input v-model="reportData.reportQty" :min="1" :max="999999" style="width: 100%"></el-input></el-form-item> |
|
|
|
<el-form-item label="报工节点"> |
|
|
|
<el-select v-model="reportData.nodeCode" placeholder="请选择节点" style="width: 100%" @change="handleReportNodeChange"> |
|
|
|
<el-option v-for="item in reportNodeOptions" :key="item.nodeCode" :label="item.nodeName" :value="item.nodeCode"></el-option> |
|
|
|
</el-select> |
|
|
|
</el-form-item> |
|
|
|
<el-form-item v-if="isMachiningProductionNode(reportData.nodeCode)" label="本次报工数量"> |
|
|
|
<el-input v-model="reportData.reportQty" :min="1" :max="999999" style="width: 100%"></el-input> |
|
|
|
</el-form-item> |
|
|
|
<el-form-item v-if="isMachiningInspectionNode(reportData.nodeCode)" label="检验影像上传"> |
|
|
|
<div class="inspection-upload-wrap"> |
|
|
|
<el-button plain type="primary" size="mini" @click="triggerInspectionImagePicker">选择图片</el-button> |
|
|
|
<span class="inspection-upload-tip">支持图片格式,可多选上传</span> |
|
|
|
<input |
|
|
|
ref="inspectionImageInput" |
|
|
|
class="inspection-file-input" |
|
|
|
type="file" |
|
|
|
accept="image/*" |
|
|
|
multiple |
|
|
|
@change="handleInspectionImageSelect"> |
|
|
|
<div v-if="inspectionImageList.length > 0" class="inspection-file-list"> |
|
|
|
<div v-for="(item, index) in inspectionImageList" :key="item.uid" class="inspection-file-item"> |
|
|
|
<span class="inspection-file-name">{{ item.name }}</span> |
|
|
|
<el-button type="text" style="color:#F56C6C" @click="removeInspectionImage(index)">删除</el-button> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<div v-else class="inspection-upload-empty">请至少上传1张检验图片</div> |
|
|
|
</div> |
|
|
|
</el-form-item> |
|
|
|
<el-form-item label="报工备注"><el-input v-model="reportData.remark" type="textarea" :rows="2"></el-input></el-form-item> |
|
|
|
</el-form> |
|
|
|
<el-footer style="height: 40px; margin-top: 50px; text-align: center"> |
|
|
|
@ -245,7 +272,7 @@ |
|
|
|
<el-dialog |
|
|
|
title="报工影像文件" |
|
|
|
:visible.sync="mediaDialogVisible" |
|
|
|
width="600px" |
|
|
|
width="680px" |
|
|
|
:close-on-click-modal="false" |
|
|
|
@close="handleMediaDialogClose" |
|
|
|
v-drag> |
|
|
|
@ -293,7 +320,7 @@ |
|
|
|
<el-table-column label="上传时间" min-width="170" align="center"> |
|
|
|
<template slot-scope="scope">{{ scope.row.createDate || '-' }}</template> |
|
|
|
</el-table-column> |
|
|
|
<el-table-column label="操作" width="90" align="center"> |
|
|
|
<el-table-column label="操作" width="60" align="center"> |
|
|
|
<template slot-scope="scope"> |
|
|
|
<el-button |
|
|
|
type="text" |
|
|
|
@ -338,7 +365,7 @@ |
|
|
|
</template> |
|
|
|
|
|
|
|
<script> |
|
|
|
import { deleteMachiningTask, finishMachiningTask, getMachiningTaskList, getNodeAssigneeList, getNodeAssigneeUsers, getReportLogList, reportMachiningTaskNode, saveMachiningTask, saveNodeAssignee } from '@/api/longchuang/productionPlan' |
|
|
|
import { deleteMachiningTask, finishMachiningTask, getMachiningTaskList, getNodeAssigneeList, getNodeAssigneeUsers, getReportLogList, reportMachiningTaskNode, reportWorkNodeWithMedia, saveMachiningTask, saveNodeAssignee } from '@/api/longchuang/productionPlan' |
|
|
|
import { getOssVideoStreamUrl, previewOssFileById2, queryOssFilePlus, removeOss } from '@/api/oss/oss' |
|
|
|
|
|
|
|
export default { |
|
|
|
@ -348,6 +375,8 @@ export default { |
|
|
|
searchData: { modelNo: '', materialName: '', materialSpec: '', status: '', planStartDate: '', planEndDate: '', page: 1, limit: 20 }, |
|
|
|
saveHeaderData: {}, |
|
|
|
reportData: { orderNo: '', taskNo: '', nodeCode: 'machiningProduction', reportQty: '', remark: '' }, |
|
|
|
reportNodeOptions: [], |
|
|
|
inspectionImageList: [], |
|
|
|
setUp: { reviewFlag: false, reportFlag: false, assignFlag: false, saveButton: false, reportButton: false, assignButton: false }, |
|
|
|
dataList: [], |
|
|
|
currentAssignOrder: { orderNo: '', orderType: 'MACHINING' }, |
|
|
|
@ -400,7 +429,19 @@ export default { |
|
|
|
const done = list.filter(item => item.status === '已完成').length |
|
|
|
const currentNode = (list.find(item => item.status !== '已完成') || {}).nodeName || '全部完成' |
|
|
|
const assigneeSummary = list.filter(item => item.assigneeUserName).map(item => `${item.nodeName}:${item.assigneeUserName}`).join(';') |
|
|
|
return { ...row, autoAssignAllUsers: !!row.autoAssignAllUsers, nodeReportMode: row.nodeReportMode || 'PARALLEL', nodeList: list, nodeDoneCount: done, nodeTotalCount: list.length, currentNode: row.currentNode || currentNode, reportQty: row.reportQty || 0, assigneeSummary: assigneeSummary || '-' } |
|
|
|
return { ...row, autoAssignAllUsers: !!row.autoAssignAllUsers, nodeReportMode: row.nodeReportMode || 'SEQUENTIAL', nodeList: list, nodeDoneCount: done, nodeTotalCount: list.length, currentNode: row.currentNode || currentNode, reportQty: row.reportQty || 0, assigneeSummary: assigneeSummary || '-' } |
|
|
|
}, |
|
|
|
getMachiningDefaultNodeList() { |
|
|
|
return [ |
|
|
|
{ nodeCode: 'machiningProduction', nodeName: '机加工生产', status: '未开始' }, |
|
|
|
{ nodeCode: 'machiningInspection', nodeName: '机加工检验', status: '未开始' } |
|
|
|
] |
|
|
|
}, |
|
|
|
isMachiningProductionNode(nodeCode) { |
|
|
|
return nodeCode === 'machiningProduction' |
|
|
|
}, |
|
|
|
isMachiningInspectionNode(nodeCode) { |
|
|
|
return nodeCode === 'machiningInspection' |
|
|
|
}, |
|
|
|
loadMockData() { |
|
|
|
this.dataList = [ |
|
|
|
@ -413,12 +454,13 @@ export default { |
|
|
|
taskQty: 120, |
|
|
|
reportQty: 30, |
|
|
|
autoAssignAllUsers: true, |
|
|
|
nodeReportMode: 'PARALLEL', |
|
|
|
nodeReportMode: 'SEQUENTIAL', |
|
|
|
planFinishDate: '2026-05-25', |
|
|
|
finishDate: '', |
|
|
|
status: '进行中', |
|
|
|
nodeList: [ |
|
|
|
{ nodeCode: 'machiningProduction', nodeName: '机加工生产', status: '进行中' } |
|
|
|
{ nodeCode: 'machiningProduction', nodeName: '机加工生产', status: '已完成' }, |
|
|
|
{ nodeCode: 'machiningInspection', nodeName: '机加工检验', status: '进行中' } |
|
|
|
] |
|
|
|
}, |
|
|
|
{ |
|
|
|
@ -430,13 +472,11 @@ export default { |
|
|
|
taskQty: 240, |
|
|
|
reportQty: 0, |
|
|
|
autoAssignAllUsers: true, |
|
|
|
nodeReportMode: 'PARALLEL', |
|
|
|
nodeReportMode: 'SEQUENTIAL', |
|
|
|
planFinishDate: '2026-05-27', |
|
|
|
finishDate: '', |
|
|
|
status: '已排产', |
|
|
|
nodeList: [ |
|
|
|
{ nodeCode: 'machiningProduction', nodeName: '机加工生产', status: '未开始' } |
|
|
|
] |
|
|
|
nodeList: this.getMachiningDefaultNodeList() |
|
|
|
} |
|
|
|
].map(this.normalizeRow) |
|
|
|
this.totalPage = this.dataList.length |
|
|
|
@ -489,8 +529,8 @@ export default { |
|
|
|
if (row.action && row.action !== '报工完成') { |
|
|
|
return false |
|
|
|
} |
|
|
|
const codeSet = ['machiningProduction'] |
|
|
|
const nameSet = ['机加工生产'] |
|
|
|
const codeSet = ['machiningInspection'] |
|
|
|
const nameSet = ['机加工检验', '检验'] |
|
|
|
const nodeCode = String(row.nodeCode || '').trim() |
|
|
|
const nodeName = String(row.nodeName || '').replace(/\s+/g, '') |
|
|
|
return codeSet.includes(nodeCode) || nameSet.some(item => item.replace(/\s+/g, '') === nodeName) |
|
|
|
@ -744,8 +784,8 @@ export default { |
|
|
|
}, |
|
|
|
openEditDialog(row) { |
|
|
|
this.saveHeaderData = row |
|
|
|
? { ...row, autoAssignAllUsers: !!row.autoAssignAllUsers, nodeReportMode: row.nodeReportMode || 'PARALLEL' } |
|
|
|
: { orderNo: '', modelNo: '', materialName: '', materialSpec: '', taskQty: 1, reportQty: 0, planFinishDate: '', status: '已排产', autoAssignAllUsers: true, nodeReportMode: 'PARALLEL', nodeList: [] } |
|
|
|
? { ...row, autoAssignAllUsers: !!row.autoAssignAllUsers, nodeReportMode: row.nodeReportMode || 'SEQUENTIAL' } |
|
|
|
: { orderNo: '', modelNo: '', materialName: '', materialSpec: '', taskQty: 1, reportQty: 0, planFinishDate: '', status: '已排产', autoAssignAllUsers: true, nodeReportMode: 'SEQUENTIAL', nodeList: [] } |
|
|
|
this.setUp.reviewFlag = true |
|
|
|
}, |
|
|
|
saveTask() { |
|
|
|
@ -759,7 +799,7 @@ export default { |
|
|
|
projectNo: this.saveHeaderData.modelNo, |
|
|
|
nodeList: this.saveHeaderData.nodeList && this.saveHeaderData.nodeList.length |
|
|
|
? this.saveHeaderData.nodeList |
|
|
|
: [{ nodeCode: 'machiningProduction', nodeName: '机加工生产', status: '未开始' }] |
|
|
|
: this.getMachiningDefaultNodeList() |
|
|
|
} |
|
|
|
this.setUp.saveButton = true |
|
|
|
saveMachiningTask(payload).then(({ data }) => { |
|
|
|
@ -785,16 +825,73 @@ export default { |
|
|
|
}) |
|
|
|
}, |
|
|
|
openReportDialog(row) { |
|
|
|
const defaultNode = (row.nodeList || []).find(item => item.status !== '已完成') || {} |
|
|
|
this.reportNodeOptions = (row.nodeList || []).filter(item => item.status !== '已完成') |
|
|
|
const defaultNode = this.reportNodeOptions[0] || {} |
|
|
|
const nodeCode = defaultNode.nodeCode || 'machiningProduction' |
|
|
|
this.reportData = { |
|
|
|
orderNo: row.orderNo, |
|
|
|
taskNo: row.orderNo, |
|
|
|
nodeCode: defaultNode.nodeCode || 'machiningProduction', |
|
|
|
reportQty: '', |
|
|
|
nodeCode: nodeCode, |
|
|
|
reportQty: this.isMachiningProductionNode(nodeCode) ? '' : 1, |
|
|
|
remark: '' |
|
|
|
} |
|
|
|
this.clearInspectionImageList() |
|
|
|
this.setUp.reportFlag = true |
|
|
|
}, |
|
|
|
handleReportNodeChange(nodeCode) { |
|
|
|
if (this.isMachiningProductionNode(nodeCode)) { |
|
|
|
this.reportData.reportQty = '' |
|
|
|
} else { |
|
|
|
this.reportData.reportQty = 1 |
|
|
|
} |
|
|
|
if (!this.isMachiningInspectionNode(nodeCode)) { |
|
|
|
this.clearInspectionImageList() |
|
|
|
} |
|
|
|
}, |
|
|
|
handleReportDialogClose() { |
|
|
|
this.clearInspectionImageList() |
|
|
|
}, |
|
|
|
triggerInspectionImagePicker() { |
|
|
|
if (this.$refs.inspectionImageInput) { |
|
|
|
this.$refs.inspectionImageInput.click() |
|
|
|
} |
|
|
|
}, |
|
|
|
handleInspectionImageSelect(event) { |
|
|
|
const fileList = Array.from((event && event.target && event.target.files) || []) |
|
|
|
if (!fileList.length) { |
|
|
|
return |
|
|
|
} |
|
|
|
let invalidCount = 0 |
|
|
|
fileList.forEach(file => { |
|
|
|
const fileType = String((file && file.type) || '').toLowerCase() |
|
|
|
const fileName = String((file && file.name) || '').toLowerCase() |
|
|
|
const isImage = fileType.indexOf('image/') === 0 || /\.(jpg|jpeg|png|gif|bmp|webp)$/.test(fileName) |
|
|
|
if (!file || !isImage) { |
|
|
|
invalidCount++ |
|
|
|
return |
|
|
|
} |
|
|
|
this.inspectionImageList.push({ |
|
|
|
uid: `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`, |
|
|
|
name: file.name || 'inspection-image', |
|
|
|
raw: file |
|
|
|
}) |
|
|
|
}) |
|
|
|
if (invalidCount > 0) { |
|
|
|
this.$message.warning('仅支持上传图片文件') |
|
|
|
} |
|
|
|
if (event && event.target) { |
|
|
|
event.target.value = '' |
|
|
|
} |
|
|
|
}, |
|
|
|
removeInspectionImage(index) { |
|
|
|
this.inspectionImageList.splice(index, 1) |
|
|
|
}, |
|
|
|
clearInspectionImageList() { |
|
|
|
this.inspectionImageList = [] |
|
|
|
if (this.$refs.inspectionImageInput) { |
|
|
|
this.$refs.inspectionImageInput.value = '' |
|
|
|
} |
|
|
|
}, |
|
|
|
openAssignDialog(row) { |
|
|
|
if (!row.orderNo) return this.$message.warning('请先保存任务单后再分配人员') |
|
|
|
this.currentAssignOrder = { orderNo: row.orderNo, orderType: 'MACHINING' } |
|
|
|
@ -835,10 +932,26 @@ export default { |
|
|
|
}) |
|
|
|
}, |
|
|
|
submitNodeReport() { |
|
|
|
if (!this.reportData.reportQty || Number(this.reportData.reportQty) <= 0) return this.$message.warning('请输入有效报工数量') |
|
|
|
if (!this.reportData.nodeCode) this.reportData.nodeCode = 'machiningProduction' |
|
|
|
if (this.isMachiningProductionNode(this.reportData.nodeCode)) { |
|
|
|
const qty = Number(this.reportData.reportQty) |
|
|
|
if (!Number.isFinite(qty) || qty <= 0) return this.$message.warning('请输入有效报工数量') |
|
|
|
this.submitMachiningProductionReport(qty) |
|
|
|
return |
|
|
|
} |
|
|
|
if (this.isMachiningInspectionNode(this.reportData.nodeCode)) { |
|
|
|
this.submitMachiningInspectionReport() |
|
|
|
return |
|
|
|
} |
|
|
|
this.$message.warning('当前节点暂不支持报工') |
|
|
|
}, |
|
|
|
submitMachiningProductionReport(reportQty) { |
|
|
|
const payload = { |
|
|
|
...this.reportData, |
|
|
|
reportQty: reportQty |
|
|
|
} |
|
|
|
this.setUp.reportButton = true |
|
|
|
reportMachiningTaskNode(this.reportData).then(({ data }) => { |
|
|
|
reportMachiningTaskNode(payload).then(({ data }) => { |
|
|
|
this.setUp.reportButton = false |
|
|
|
if (data && data.code === 0) { |
|
|
|
this.$message.success(data.msg || '报工成功') |
|
|
|
@ -847,18 +960,54 @@ export default { |
|
|
|
} else this.$message.error(data.msg || '报工失败') |
|
|
|
}).catch(() => { |
|
|
|
this.setUp.reportButton = false |
|
|
|
this.simulateNodeReport(this.reportData.orderNo, this.reportData.nodeCode, this.reportData.reportQty) |
|
|
|
this.simulateNodeReport(this.reportData.orderNo, this.reportData.nodeCode, reportQty) |
|
|
|
this.setUp.reportFlag = false |
|
|
|
this.$message.success('后端未完成,已在前端演示节点报工') |
|
|
|
}) |
|
|
|
}, |
|
|
|
submitMachiningInspectionReport() { |
|
|
|
const uploadFileList = this.inspectionImageList.map(item => item.raw).filter(item => !!item) |
|
|
|
if (!uploadFileList.length) { |
|
|
|
return this.$message.warning('请先上传检验图片') |
|
|
|
} |
|
|
|
const formData = new FormData() |
|
|
|
formData.append('orderNo', this.reportData.orderNo) |
|
|
|
formData.append('orderType', 'MACHINING') |
|
|
|
formData.append('nodeCode', this.reportData.nodeCode) |
|
|
|
formData.append('reportQty', '1') |
|
|
|
formData.append('remark', this.reportData.remark || '') |
|
|
|
formData.append('reportNode', 'true') |
|
|
|
uploadFileList.forEach(file => { |
|
|
|
formData.append('file', file, file.name || 'inspection-image') |
|
|
|
}) |
|
|
|
this.setUp.reportButton = true |
|
|
|
reportWorkNodeWithMedia(formData).then(({ data }) => { |
|
|
|
this.setUp.reportButton = false |
|
|
|
if (data && data.code === 0) { |
|
|
|
this.$message.success(data.msg || '检验报工成功') |
|
|
|
this.setUp.reportFlag = false |
|
|
|
this.clearInspectionImageList() |
|
|
|
this.searchTable() |
|
|
|
} else { |
|
|
|
this.$message.error(data.msg || '检验报工失败') |
|
|
|
} |
|
|
|
}).catch(() => { |
|
|
|
this.setUp.reportButton = false |
|
|
|
this.simulateNodeReport(this.reportData.orderNo, this.reportData.nodeCode, 0) |
|
|
|
this.setUp.reportFlag = false |
|
|
|
this.clearInspectionImageList() |
|
|
|
this.$message.success('后端未完成,已在前端演示检验报工') |
|
|
|
}) |
|
|
|
}, |
|
|
|
simulateNodeReport(orderNo, nodeCode, qty) { |
|
|
|
const row = this.dataList.find(item => item.orderNo === orderNo) |
|
|
|
if (!row) return |
|
|
|
const node = row.nodeList.find(item => item.nodeCode === nodeCode) |
|
|
|
if (!node) return |
|
|
|
node.status = '已完成' |
|
|
|
if (this.isMachiningProductionNode(nodeCode)) { |
|
|
|
row.reportQty = Number(row.reportQty || 0) + Number(qty || 0) |
|
|
|
} |
|
|
|
row.status = '进行中' |
|
|
|
const nextNode = row.nodeList.find(item => item.status !== '已完成') |
|
|
|
row.currentNode = nextNode ? nextNode.nodeName : '全部完成' |
|
|
|
@ -1191,6 +1340,52 @@ export default { |
|
|
|
padding: 8px; |
|
|
|
} |
|
|
|
|
|
|
|
.inspection-upload-wrap { |
|
|
|
border: 1px dashed #dcdfe6; |
|
|
|
border-radius: 4px; |
|
|
|
padding: 10px; |
|
|
|
} |
|
|
|
|
|
|
|
.inspection-upload-tip { |
|
|
|
margin-left: 8px; |
|
|
|
color: #909399; |
|
|
|
font-size: 12px; |
|
|
|
} |
|
|
|
|
|
|
|
.inspection-file-input { |
|
|
|
display: none; |
|
|
|
} |
|
|
|
|
|
|
|
.inspection-file-list { |
|
|
|
margin-top: 10px; |
|
|
|
max-height: 150px; |
|
|
|
overflow-y: auto; |
|
|
|
} |
|
|
|
|
|
|
|
.inspection-file-item { |
|
|
|
display: flex; |
|
|
|
align-items: center; |
|
|
|
justify-content: space-between; |
|
|
|
border-bottom: 1px solid #f0f2f5; |
|
|
|
padding: 4px 0; |
|
|
|
} |
|
|
|
|
|
|
|
.inspection-file-name { |
|
|
|
flex: 1; |
|
|
|
padding-right: 10px; |
|
|
|
font-size: 12px; |
|
|
|
color: #606266; |
|
|
|
overflow: hidden; |
|
|
|
text-overflow: ellipsis; |
|
|
|
white-space: nowrap; |
|
|
|
} |
|
|
|
|
|
|
|
.inspection-upload-empty { |
|
|
|
margin-top: 8px; |
|
|
|
font-size: 12px; |
|
|
|
color: #909399; |
|
|
|
} |
|
|
|
|
|
|
|
.media-dialog-body { |
|
|
|
min-height: 360px; |
|
|
|
} |
|
|
|
@ -1259,7 +1454,39 @@ export default { |
|
|
|
} |
|
|
|
|
|
|
|
.file-table { |
|
|
|
margin-top: 5px; |
|
|
|
background-color: #fff; |
|
|
|
border-radius: 4px; |
|
|
|
} |
|
|
|
.file-table >>> .cell { |
|
|
|
line-height: 55px; |
|
|
|
height: 55px; |
|
|
|
} |
|
|
|
.file-table >>> .el-table__header-wrapper th, |
|
|
|
.file-table >>> .el-table__fixed-header-wrapper th { |
|
|
|
background-color: #f5f7fa !important; |
|
|
|
color: #333; |
|
|
|
font-weight: 600; |
|
|
|
border-color: #ebeef5; |
|
|
|
padding: 8px 0; |
|
|
|
} |
|
|
|
|
|
|
|
.file-table >>> .el-table__header-wrapper .cell, |
|
|
|
.file-table >>> .el-table__fixed-header-wrapper .cell{ |
|
|
|
padding: 0 10px; |
|
|
|
overflow: hidden; |
|
|
|
text-overflow: ellipsis; |
|
|
|
white-space: nowrap; |
|
|
|
font-size: 13px !important; |
|
|
|
line-height: 20px; |
|
|
|
height: 20px; |
|
|
|
} |
|
|
|
|
|
|
|
.file-table >>> .el-table__body tr:hover > td { |
|
|
|
background-color: #f5f7fa !important; |
|
|
|
} |
|
|
|
|
|
|
|
.file-table >>> .el-table__body tr.current-row > td { |
|
|
|
background-color: #ecf5ff !important; |
|
|
|
} |
|
|
|
|
|
|
|
.el-icon-check { |
|
|
|
|