feat(mes): 添加定时同步可生产数量到WMS及BOM项相关标准字段

- 在main.go中添加定时任务每10分钟同步可生产数量到WMS系统
- 为BomItem实体添加relatedStandard字段及相关CRUD方法
- 为InspectionRecord实体添加reportNo、materialCode、materialName等字段
- 更新ent schema确保新字段的验证和默认值设置
- 添加必要的数据库迁移和字段映射逻辑
This commit is contained in:
SunYF
2026-09-17 12:49:07 +08:00
parent b4b274301d
commit 1cc93795ce
191 changed files with 24504 additions and 1250 deletions
+95 -33
View File
@@ -1,8 +1,8 @@
<script setup>
defineOptions({ name: 'Inspection' })
import { computed, nextTick, onMounted, reactive, ref } from 'vue'
import { useRoute } from 'vue-router'
import { ElMessage } from 'element-plus'
import { useRoute, useRouter } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus'
import request from '../utils/request'
import { getRealName } from '../utils/auth'
import { fmtTime } from '../utils/format'
@@ -17,6 +17,7 @@ const activeTab = ref('entry') // entry=质量检验(录入) / records=检验记
// 菜单按业务分开(来料/过程/完工检验):从路由 meta 锁定检验类型,同表同接口按类别过滤
const route = useRoute()
const router = useRouter()
const pageType = String(route.meta.inspectionType || '')
/* ===================== Tab1 质量检验(录入,默认打开) ===================== */
@@ -31,10 +32,14 @@ const passQty = ref(0)
const checkDesc = ref('')
const inspectionNo = ref('')
const reportNo = ref('')
// 新增字段(客户诉求 L107/L150/L494
const ownershipType = ref('none')
const ownershipNo = ref('')
const relatedStandard = ref('')
const manufacturer = ref('')
// 不合格品处置(客户诉求 问题记录 L137-145:退货 / 报废 / 部分入库)
const disposalType = ref('NONE')
const disposalRemark = ref('')
const returnTrackingNo = ref('')
// 待检编号列表(合并单/批量,统一一次提交)
@@ -102,7 +107,7 @@ async function submitInspection() {
return
}
if (!inspector.value.trim()) {
ElMessage.warning('请填写检验员')
ElMessage.warning('请填写记录人')
return
}
// 判定不合格必须选择处置方式(不合格不入库、不上产线)
@@ -131,21 +136,38 @@ async function submitInspection() {
inspector: inspector.value.trim(),
inspectionNo: inspectionNo.value.trim(),
disposalType: status.value === '不合格' ? disposalType.value : 'NONE',
disposalRemark: disposalRemark.value.trim(),
returnTrackingNo: returnTrackingNo.value.trim(),
ownershipType: ownershipType.value,
ownershipNo: ownershipNo.value.trim(),
relatedStandard: relatedStandard.value.trim(),
manufacturer: manufacturer.value.trim(),
})
const n = data?.flipped ?? targetList.value.length
ElMessage.success(`检验已记录:共 ${n} 条 → ${status.value}`)
// 检验合格后引导去入库(方案 C / P1-6):携检测单号跳转,入库页自动回填免手抄
const passedNo = inspectionNo.value.trim()
const goInbound = status.value === '合格'
clearAll()
inspectQty.value = 0
passQty.value = 0
checkDesc.value = ''
reportNo.value = ''
disposalType.value = 'NONE'
disposalRemark.value = ''
returnTrackingNo.value = ''
ownershipType.value = 'none'
ownershipNo.value = ''
relatedStandard.value = ''
manufacturer.value = ''
// 提交成功后主动刷新记录 Tab(用户切过去即可见最新)
await loadRecords()
if (goInbound) {
try {
await ElMessageBox.confirm(
passedNo ? `检验合格,是否携检测单号 ${passedNo} 前往入库?` : '检验合格,是否前往入库?',
'前往入库', { type: 'success', confirmButtonText: '去入库', cancelButtonText: '留在本页' })
router.push({ path: '/inbound', query: passedNo ? { inspectionNo: passedNo } : {} })
} catch { /* 用户选择留在本页 */ }
}
} finally {
submitting.value = false
}
@@ -193,9 +215,11 @@ function defaultStart() {
return `${y}-${m}-${day}`
}
// 筛选独立条件(不再笼统关键字);默认近3个月
// 筛选独立条件(客户诉求 L507-510:三框模糊查询 图号/名称/型号,框内模糊、框间取交集);默认近3个月
const filters = reactive({
targetId: '', materialCode: '', inspectionType: pageType, status: '', inspector: '',
targetId: '', materialCode: '', materialName: '', spec: '',
inspectionType: pageType, status: '', inspector: '',
ownershipNo: '',
startDate: defaultStart(), endDate: ''
})
const loading = ref(false)
@@ -236,6 +260,9 @@ async function loadRecords() {
params: {
targetId: filters.targetId.trim(),
materialCode: filters.materialCode.trim(),
materialName: filters.materialName.trim(),
spec: filters.spec.trim(),
ownershipNo: filters.ownershipNo.trim(),
inspectionType: filters.inspectionType,
status: filters.status,
inspector: filters.inspector.trim(),
@@ -263,6 +290,9 @@ async function exportCsv() {
await exportXlsxFetch('/api/inspection/export', {
targetId: filters.targetId.trim(),
materialCode: filters.materialCode.trim(),
materialName: filters.materialName.trim(),
spec: filters.spec.trim(),
ownershipNo: filters.ownershipNo.trim(),
inspectionType: filters.inspectionType,
status: filters.status,
inspector: filters.inspector.trim(),
@@ -318,17 +348,31 @@ onMounted(() => {
<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>
<el-form-item label="记录人">
<el-input v-model="inspector" placeholder="记录人姓名" clearable style="width:260px" />
</el-form-item>
<el-form-item label="归属">
<div style="display:flex;gap:8px;width:100%;max-width:560px">
<el-select v-model="ownershipType" style="width:150px">
<el-option label="无" value="none" />
<el-option label="工单号" value="workorder" />
<el-option label="科技项目号" value="project" />
</el-select>
<el-input v-if="ownershipType !== 'none'" v-model="ownershipNo"
placeholder="归属编号" clearable />
</div>
</el-form-item>
<el-form-item label="相关标准">
<el-input v-model="relatedStandard" placeholder="检验细则/试验方案号(可空)" clearable style="width:420px" />
</el-form-item>
<el-form-item label="生产厂家">
<el-input v-model="manufacturer" placeholder="生产厂家(可空)" clearable style="width:420px" />
</el-form-item>
<el-form-item label="检测单号">
<el-input v-model="inspectionNo" placeholder="检验报告单号(可空,与入库单关联)" clearable style="width:420px" />
</el-form-item>
@@ -343,7 +387,7 @@ onMounted(() => {
<el-form-item label="合格数量">
<el-input-number v-model="passQty" :min="0" :max="999999" placeholder="合格件数(≤检验数量)" style="width:160px" />
</el-form-item>
<el-form-item label="检测说明">
<el-form-item label="相关标准">
<el-input v-model="checkDesc" placeholder="检验项目/方法/标准(可空)" clearable style="width:420px" />
</el-form-item>
<el-divider content-position="left">待检编号可扫/选多条统一提交</el-divider>
@@ -357,7 +401,7 @@ onMounted(() => {
clearable
:remote-method="loadPicker"
:loading="pickerLoading"
:placeholder="`输入批次号/SN/物料编码搜索(最近100条)`"
:placeholder="`输入批次号/SN/图号搜索(最近100条)`"
style="width:100%"
@change="addFromPicker"
>
@@ -400,11 +444,23 @@ onMounted(() => {
<el-card shadow="never" class="mb12">
<el-form inline @submit.prevent="search">
<el-form-item label="批次/SN">
<el-input v-model="filters.targetId" placeholder="批次号或 SN" clearable style="width:160px"
<el-input v-model="filters.targetId" placeholder="批次号或 SN" clearable style="width:150px"
@keyup.enter="search" />
</el-form-item>
<el-form-item label="物料编码">
<el-input v-model="filters.materialCode" placeholder="物料编码" clearable style="width:150px"
<el-form-item label="图号">
<el-input v-model="filters.materialCode" placeholder="图号(模糊)" clearable style="width:140px"
@keyup.enter="search" />
</el-form-item>
<el-form-item label="名称">
<el-input v-model="filters.materialName" placeholder="物料名称(模糊)" clearable style="width:140px"
@keyup.enter="search" />
</el-form-item>
<el-form-item label="型号">
<el-input v-model="filters.spec" placeholder="规格型号(模糊)" clearable style="width:140px"
@keyup.enter="search" />
</el-form-item>
<el-form-item label="归属单号">
<el-input v-model="filters.ownershipNo" placeholder="工单号/科技项目号" clearable style="width:150px"
@keyup.enter="search" />
</el-form-item>
<el-form-item v-if="!pageType" label="检验类型">
@@ -421,11 +477,11 @@ onMounted(() => {
<el-option label="未检" value="未检" />
</el-select>
</el-form-item>
<el-form-item label="检验员">
<el-input v-model="filters.inspector" placeholder="检验员" clearable style="width:120px"
<el-form-item label="记录人">
<el-input v-model="filters.inspector" placeholder="记录人" clearable style="width:120px"
@keyup.enter="search" />
</el-form-item>
<el-form-item label="检验时间">
<el-form-item label="记录日期">
<el-date-picker v-model="filters.startDate" type="date" value-format="YYYY-MM-DD" placeholder="开始"
style="width:130px" @change="search" />
<span style="margin:0 6px"></span>
@@ -446,7 +502,10 @@ onMounted(() => {
<template #default="{ row }">{{ targetTypeLabel(row.targetType) }}</template>
</el-table-column>
<el-table-column prop="targetId" label="批次/SN" min-width="150" />
<el-table-column prop="materialCode" label="物料编码" min-width="130" />
<el-table-column prop="materialCode" label="图号" min-width="130" />
<el-table-column prop="materialName" label="名称" min-width="120">
<template #default="{ row }">{{ row.materialName || '-' }}</template>
</el-table-column>
<el-table-column label="检验类型" width="90" align="center">
<template #default="{ row }">{{ typeLabel(row.inspectionType) }}</template>
</el-table-column>
@@ -468,10 +527,10 @@ onMounted(() => {
<el-table-column prop="passQty" label="合格数" width="80" align="center">
<template #default="{ row }">{{ row.passQty || 0 }}</template>
</el-table-column>
<el-table-column label="检测说明" min-width="140">
<el-table-column label="相关标准" min-width="140">
<template #default="{ row }">
<el-tooltip v-if="row.checkDesc" :content="row.checkDesc" placement="top">
<span>{{ row.checkDesc }}</span>
<el-tooltip v-if="row.checkDesc || row.relatedStandard" :content="row.relatedStandard || row.checkDesc" placement="top">
<span>{{ row.relatedStandard || row.checkDesc }}</span>
</el-tooltip>
<span v-else>-</span>
</template>
@@ -479,13 +538,19 @@ onMounted(() => {
<el-table-column prop="reportNo" label="报检单号" min-width="130" align="center">
<template #default="{ row }">{{ row.reportNo || '-' }}</template>
</el-table-column>
<el-table-column prop="inspector" label="检验员" width="90" align="center">
<el-table-column label="归属单号" min-width="130" align="center">
<template #default="{ row }">{{ row.ownershipNo || '-' }}</template>
</el-table-column>
<el-table-column prop="inspector" label="记录人" width="90" align="center">
<template #default="{ row }">{{ row.inspector || '-' }}</template>
</el-table-column>
<el-table-column prop="inspectionNo" label="检测单号" min-width="130" align="center">
<template #default="{ row }">{{ row.inspectionNo || '-' }}</template>
</el-table-column>
<el-table-column label="检验时间" width="155" align="center">
<el-table-column prop="manufacturer" label="生产厂家" min-width="120" align="center">
<template #default="{ row }">{{ row.manufacturer || '-' }}</template>
</el-table-column>
<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">
@@ -514,7 +579,7 @@ onMounted(() => {
<span style="margin-left:8px;color:#909399;font-size:12px">按当前时间区间与检验类别统计;每次检验独立计数,不合并记录</span>
</div>
<el-table :data="statsList" border size="small" v-loading="statsLoading" empty-text="统计查看结果">
<el-table-column prop="materialCode" label="物料编码" min-width="160" />
<el-table-column prop="materialCode" label="图号" min-width="160" />
<el-table-column prop="total" label="检验次数" width="110" align="center" />
<el-table-column prop="pass" label="合格次数" width="110" align="center" />
<el-table-column prop="fail" label="不合格次数" width="120" align="center" />
@@ -534,7 +599,7 @@ onMounted(() => {
<el-form-item label="批次/SN">
<el-input :model-value="disposeDialog.row?.targetId || ''" disabled />
</el-form-item>
<el-form-item label="物料编码">
<el-form-item label="图号">
<el-input :model-value="disposeDialog.row?.materialCode || ''" disabled />
</el-form-item>
<el-form-item label="检验数 / 合格数">
@@ -551,9 +616,6 @@ onMounted(() => {
<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>