feat: 完成多模块迭代更新

- 新增过程巡检按步骤上报、数量不符上报、工位退料功能
- 增加工单工程编号三级归属字段
- 新增物料安全库存与缺货预警
- 新增附件管理、退库单管理模块
- 优化生产看板展示逻辑与前端页面文案
- 清理冗余的工艺路线相关代码与备份文件
This commit is contained in:
SunYF
2026-09-15 16:37:39 +08:00
parent 951f3dfecd
commit 4a5e2d7bac
494 changed files with 24702 additions and 139223 deletions
@@ -9,6 +9,7 @@ import { fmtTime } from '../utils/format'
import { exportXlsxFetch } from '../utils/export'
import { can } from '../utils/perm'
import PageHelp from '../components/PageHelp.vue'
import AttachmentPanel from '../components/AttachmentPanel.vue'
import { helpInspection } from '../help'
// ===== 多 Tab:录入在前默认打开,记录在后点击才见(参照出库管理) =====
@@ -31,6 +32,11 @@ const checkDesc = ref('')
const inspectionNo = ref('')
const reportNo = ref('')
// 不合格品处置(客户诉求 问题记录 L137-145:退货 / 报废 / 部分入库)
const disposalType = ref('NONE')
const disposalRemark = ref('')
const returnTrackingNo = ref('')
// 待检编号列表(合并单/批量,统一一次提交)
const targetList = ref([])
const scanInput = ref('')
@@ -99,6 +105,19 @@ async function submitInspection() {
ElMessage.warning('请填写检验员')
return
}
// 判定不合格必须选择处置方式(不合格不入库、不上产线)
if (status.value === '不合格' && disposalType.value === 'NONE') {
ElMessage.warning('判定不合格时必须选择处置方式(退货 / 报废 / 部分入库)')
return
}
if (status.value === '不合格' && disposalType.value === 'RETURN' && !returnTrackingNo.value.trim()) {
ElMessage.warning('退货处置必须填写货运单号')
return
}
if (status.value === '不合格' && disposalType.value === 'PARTIAL' && targetList.value.length > 1) {
ElMessage.warning('「部分入库」一次只能处理 1 个批次,请分开提交')
return
}
submitting.value = true
try {
const data = await request.post('/inspection/batch-flip', {
@@ -111,6 +130,9 @@ async function submitInspection() {
reportNo: reportNo.value.trim(),
inspector: inspector.value.trim(),
inspectionNo: inspectionNo.value.trim(),
disposalType: status.value === '不合格' ? disposalType.value : 'NONE',
disposalRemark: disposalRemark.value.trim(),
returnTrackingNo: returnTrackingNo.value.trim(),
})
const n = data?.flipped ?? targetList.value.length
ElMessage.success(`检验已记录:共 ${n} 条 → ${status.value}`)
@@ -119,6 +141,9 @@ async function submitInspection() {
passQty.value = 0
checkDesc.value = ''
reportNo.value = ''
disposalType.value = 'NONE'
disposalRemark.value = ''
returnTrackingNo.value = ''
// 提交成功后主动刷新记录 Tab(用户切过去即可见最新)
await loadRecords()
} finally {
@@ -126,6 +151,37 @@ async function submitInspection() {
}
}
/* ===================== 不合格处置(对已判定不合格的记录做退货/报废/部分入库) ===================== */
const disposeDialog = reactive({ visible: false, row: null, type: 'RETURN', remark: '', trackingNo: '' })
function openDispose(row) {
if (row.status !== '不合格') {
ElMessage.warning('仅「不合格」记录可执行处置')
return
}
Object.assign(disposeDialog, { visible: true, row, type: 'RETURN', remark: '', trackingNo: '' })
}
async function submitDispose() {
if (disposeDialog.type === 'RETURN' && !disposeDialog.trackingNo.trim()) {
ElMessage.warning('退货必须填写货运单号')
return
}
await request.post('/inspection/dispose', {
recordId: disposeDialog.row.id,
disposalType: disposeDialog.type,
disposalRemark: disposeDialog.remark.trim(),
returnTrackingNo: disposeDialog.trackingNo.trim(),
})
ElMessage.success('处置完成')
disposeDialog.visible = false
await loadRecords()
}
// 附件抽屉(bizType=inspectionbizId=检验记录ID
const attachDrawer = reactive({ visible: false, id: '', targetId: '' })
function openAttach(row) {
Object.assign(attachDrawer, { visible: true, id: String(row.id), targetId: row.targetId })
}
/* ===================== Tab2 检验记录(点击才加载) ===================== */
// 默认近3个月起始日期
function defaultStart() {
@@ -167,6 +223,11 @@ function typeLabel(t) {
function targetTypeLabel(t) {
return t === 'SN' ? 'SN' : t === 'BATCH' ? '批次' : (t || '-')
}
// 不合格处置方式显示(NONE=未处置)
function disposalLabel(t) {
const m = { RETURN: '退货', SCRAP: '报废', PARTIAL: '部分入库' }
return m[t] || '未处置'
}
async function loadRecords() {
loading.value = true
@@ -244,6 +305,27 @@ onMounted(() => {
<el-radio value="不合格">不合格</el-radio>
</el-radio-group>
</el-form-item>
<!-- 不合格品处置判定不合格时才出现退货/报废/部分入库 -->
<template v-if="status === '不合格'">
<el-form-item label="处置方式">
<el-radio-group v-model="disposalType">
<el-radio-button value="RETURN">退货</el-radio-button>
<el-radio-button value="SCRAP">报废</el-radio-button>
<el-radio-button value="PARTIAL">部分入库</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item v-if="disposalType === 'RETURN'" label="货运单号">
<el-input v-model="returnTrackingNo" placeholder="退货运单号(必填)" clearable style="width:280px" />
</el-form-item>
<el-form-item label="处置说明">
<el-input v-model="disposalRemark" placeholder="报废 / 退货原因(可空)" clearable style="width:420px" />
</el-form-item>
<el-form-item label=" ">
<el-alert type="warning" :closable="false" style="max-width:560px"
title="不合格品自动隔离:库存质量状态置「不合格/待退」,禁止领料上线;退货必填货运单号。" />
</el-form-item>
</template>
<el-form-item label="检验员">
<el-input v-model="inspector" placeholder="检验员姓名" clearable style="width:260px" />
</el-form-item>
@@ -374,6 +456,12 @@ onMounted(() => {
size="small">{{ row.status || '未检' }}</el-tag>
</template>
</el-table-column>
<el-table-column label="处置方式" width="100" align="center">
<template #default="{ row }">
<span v-if="row.status === '不合格'">{{ disposalLabel(row.disposalType) }}</span>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column prop="inspectQty" label="检验数" width="80" align="center">
<template #default="{ row }">{{ row.inspectQty || 0 }}</template>
</el-table-column>
@@ -400,6 +488,13 @@ onMounted(() => {
<el-table-column label="检验时间" width="155" align="center">
<template #default="{ row }">{{ fmtTime(row.createdAt) }}</template>
</el-table-column>
<el-table-column label="操作" width="130" align="center" fixed="right">
<template #default="{ row }">
<el-button v-if="row.status === '不合格'" link type="warning" size="small"
@click="openDispose(row)">处置</el-button>
<el-button link type="success" size="small" @click="openAttach(row)">附件</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
@@ -432,6 +527,46 @@ onMounted(() => {
</el-card>
</el-tab-pane>
</el-tabs>
<!-- 不合格处置弹窗 -->
<el-dialog v-model="disposeDialog.visible" title="不合格品处置" width="480px">
<el-form label-width="96px">
<el-form-item label="批次/SN">
<el-input :model-value="disposeDialog.row?.targetId || ''" disabled />
</el-form-item>
<el-form-item label="物料编码">
<el-input :model-value="disposeDialog.row?.materialCode || ''" disabled />
</el-form-item>
<el-form-item label="检验数 / 合格数">
<el-input :model-value="(disposeDialog.row?.inspectQty || 0) + ' / ' + (disposeDialog.row?.passQty || 0)"
disabled />
</el-form-item>
<el-form-item label="处置方式" required>
<el-radio-group v-model="disposeDialog.type">
<el-radio-button value="RETURN">退货</el-radio-button>
<el-radio-button value="SCRAP">报废</el-radio-button>
<el-radio-button value="PARTIAL">部分入库</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item v-if="disposeDialog.type === 'RETURN'" label="货运单号" required>
<el-input v-model="disposeDialog.trackingNo" placeholder="退货运单号" clearable />
</el-form-item>
<el-form-item label="处置说明">
<el-input v-model="disposeDialog.remark" type="textarea" :rows="2" placeholder="原因可空" />
</el-form-item>
<el-alert v-if="disposeDialog.type === 'PARTIAL'" type="info" :closable="false"
title="部分入库合格数拆分批次合格部分入库差额部分隔离待处置" />
</el-form>
<template #footer>
<el-button @click="disposeDialog.visible = false">取消</el-button>
<el-button type="primary" @click="submitDispose">确认处置</el-button>
</template>
</el-dialog>
<!-- 检验记录附件抽屉 -->
<el-drawer v-model="attachDrawer.visible" :title="`检验附件:${attachDrawer.targetId}`" size="620px">
<AttachmentPanel biz-type="inspection" :biz-id="attachDrawer.id" title="附件检验报告 / 照片" />
</el-drawer>
</div>
<PageHelp :data="helpInspection" page="Inspection" />
</template>