refactor: 统一术语BOM为物料清单并补充工位扩展能力

主要变更:
1.  后端:重构工位管理逻辑,新增工位自定义扩展能力,将工位号上限调整为999并支持14+虚拟工位
2.  前后端:全量替换"BOM"术语为"物料清单",包括提示语、注释和文档
3.  WMS模块:新增AGV任务取消接口与前端页面,对接海康RCS取消任务逻辑
4.  权限与种子数据:新增工位新增权限并更新权限配置
5.  修复错误校验逻辑,优化AGV客户端签名与请求处理
This commit is contained in:
SunYF
2026-09-17 19:41:00 +08:00
parent e31b85e87a
commit bf3a408839
20 changed files with 276 additions and 52 deletions
+3 -3
View File
@@ -389,7 +389,7 @@ export const helpInspect = {
export const helpProcessCard = {
title: '生产流程卡(产品型号级全景流程卡)',
overview:
'这个菜单做什么用:按**产品型号**全方位展示该型号的完整制造定义,用于工艺交底、审核与客户查阅;与「工件追溯」页里按单个 SN 打印的流程卡互补——后者记的是“这一件实际怎么做的”,本页讲的是“这个型号应该怎么做”。\n\n内容构成(页面按页签展示,打印时全部输出):①工位流程总览——工位组合、每工位绑定的工艺流程、流程状态、步骤数、检验点数、拧紧步骤数、工艺图纸、该型号的历史作业时间;②工艺流程与步骤——逐工位列出每条步骤的采集方式(手工录入/自动采集/无需采集)、是否需检测确认、是否拧紧采集、考核标准(含上下限);③物料清单——该型号的 BOM 明细,按 BOM 名称分组,含图号/名称/规格/单位/管理方式/装配工位/单台用量/损耗率/需求总量;④检验卡项——把所有「需要检测确认」的步骤汇总成检验项清单,含判定标准。\n\n工位组合从哪来:优先取该产品编号最近一张工单的「工位组合」;若该型号还没有工单,则按全产线工位顺序(升序)展示,页面顶部会标注来源。\n\n打印/导出:点「打印 / 导出 PDF」,系统会在新窗口打开排版好的 A4 卡片版式,用浏览器打印即可(选择“另存为 PDF”即导出电子档)。',
'这个菜单做什么用:按**产品型号**全方位展示该型号的完整制造定义,用于工艺交底、审核与客户查阅;与「工件追溯」页里按单个 SN 打印的流程卡互补——后者记的是“这一件实际怎么做的”,本页讲的是“这个型号应该怎么做”。\n\n内容构成(页面按页签展示,打印时全部输出):①工位流程总览——工位组合、每工位绑定的工艺流程、流程状态、步骤数、检验点数、拧紧步骤数、工艺图纸、该型号的历史作业时间;②工艺流程与步骤——逐工位列出每条步骤的采集方式(手工录入/自动采集/无需采集)、是否需检测确认、是否拧紧采集、考核标准(含上下限);③物料清单——该型号的物料清单明细,按物料清单名称分组,含图号/名称/规格/单位/管理方式/装配工位/单台用量/损耗率/需求总量;④检验卡项——把所有「需要检测确认」的步骤汇总成检验项清单,含判定标准。\n\n工位组合从哪来:优先取该产品编号最近一张工单的「工位组合」;若该型号还没有工单,则按全产线工位顺序(升序)展示,页面顶部会标注来源。\n\n打印/导出:点「打印 / 导出 PDF」,系统会在新窗口打开排版好的 A4 卡片版式,用浏览器打印即可(选择“另存为 PDF”即导出电子档)。',
fields: [
{ name: '产品型号', source: '下拉选择,来自「产品型号」页。', purpose: '要查看哪个型号的全景流程', fill: '选定后自动带出产品编号' },
{ name: '产品编号', source: '自动带出(也可手工填写,直接按编号生成)。', purpose: '匹配工单、物料清单的关键字', fill: '一般无需手填' },
@@ -421,9 +421,9 @@ export const helpQtyReport = {
export const helpQuality = {
title: '质量检验(过程检 / 完工检)',
overview:
'质量检验 = 对物料/产品做检验判定并留痕,分「过程检」与「完工检」两个 Tab。来料检在 WMS 库房侧完成,本页不重复。\n\n录入流程:选图号(输入图号或名称远程检索 BOM 料,自动带出物料名称)→ 填报检单号/生产厂家/检验单号 → 上传附件 → 判定合格/不合格 → 保存。\n\n不合格闭环:结论为「不合格」时可当场选择处置方式(退货/返修/退换)并写处置说明;也可在列表操作列「处置」补录,或点「不合格单」一键导出《不合格单.xlsx》(预填本次检验数据)。\n\n查询:图号 / 物料名称 / 报检单号 / 记录日期范围组合筛选,每个框内模糊匹配、多框取交集;记录按时间倒序,后端真分页。',
'质量检验 = 对物料/产品做检验判定并留痕,分「过程检」与「完工检」两个 Tab。来料检在 WMS 库房侧完成,本页不重复。\n\n录入流程:选图号(输入图号或名称远程检索物料清单,自动带出物料名称)→ 填报检单号/生产厂家/检验单号 → 上传附件 → 判定合格/不合格 → 保存。\n\n不合格闭环:结论为「不合格」时可当场选择处置方式(退货/返修/退换)并写处置说明;也可在列表操作列「处置」补录,或点「不合格单」一键导出《不合格单.xlsx》(预填本次检验数据)。\n\n查询:图号 / 物料名称 / 报检单号 / 记录日期范围组合筛选,每个框内模糊匹配、多框取交集;记录按时间倒序,后端真分页。',
fields: [
{ name: '图号', source: '远程检索 BOM 料/quality/bom-material),也可手填。', purpose: '被检物料的图号(即 WMS 物料档案的图号)', fill: '输入图号或名称关键字选择' },
{ name: '图号', source: '远程检索物料清单/quality/bom-material),也可手填。', purpose: '被检物料的图号(即 WMS 物料档案的图号)', fill: '输入图号或名称关键字选择' },
{ name: '物料名称', source: '选图号后自动带出,可手改。', purpose: '被检物料名称', fill: '一般无需手填' },
{ name: '报检单号', source: '系统自动生成(RQ+时间戳),可改。', purpose: '本次报检的唯一编号,也是附件挂靠键', fill: '一般无需改,须唯一' },
{ name: '生产厂家', source: '手工填写。', purpose: '该物料的生产/供货厂家', fill: '如 XX电气有限公司' },
@@ -107,7 +107,7 @@
<el-tab-pane label="物料清单" name="bom">
<el-empty v-if="!card.bomGroups || !card.bomGroups.length" description="该产品型号暂无物料清单" />
<div v-for="g in card.bomGroups" :key="g.bomName" class="blk">
<div class="blk-t">BOM{{ g.bomName }}</div>
<div class="blk-t">物料清单{{ g.bomName }}</div>
<el-table :data="g.items" border size="small">
<el-table-column prop="materialCode" label="图号" min-width="140" show-overflow-tooltip />
<el-table-column prop="materialName" label="物料名称" min-width="150" show-overflow-tooltip />
@@ -71,7 +71,7 @@
<el-form :model="dialog" label-width="100px">
<el-form-item label="图号">
<el-select v-model="dialog.materialCode" filterable remote reserve-keyword
placeholder="输入图号或名称检索 BOM 料" :remote-method="searchBom" :loading="bomLoading"
placeholder="输入图号或名称检索物料清单" :remote-method="searchBom" :loading="bomLoading"
style="width:100%" @change="onBomPick">
<el-option v-for="b in bomOptions" :key="b.id"
:label="`${b.materialCode} / ${b.materialName}`" :value="b.materialCode" />
+42 -8
View File
@@ -1,11 +1,23 @@
<template>
<el-card>
<el-alert type="info" :closable="false" style="margin-bottom:12px"
title="工位数以数据库为准(仅改名称,不增删工位)。本页为工位总览,每个工位的工艺流程在「关联工位」页维护:在下拉选择启用的工艺流程即绑定到该工位;一个工位只能有一个启用的工艺流程。" />
title="工位数以数据库为准。本页可新增工位(工位号自定义,可扩展到 14、15…)与改名;每个工位的工艺流程在「关联工位」页维护:在下拉选择启用的工艺流程即绑定到该工位;一个工位只能有一个启用的工艺流程。" />
<div style="margin-bottom:12px">
<el-button v-if="can('produce.station:add')" type="primary" @click="openAdd">新增工位</el-button>
<span style="margin-left:12px;color:#909399;font-size:12px">虚拟工位 0 上线位13 下线位类型选线下位不连 PLC仅记录上下线</span>
</div>
<el-table :data="rows" border size="small">
<el-table-column prop="stationNo" label="工位号" width="90" align="center" />
<el-table-column prop="name" label="工位名称" min-width="170" show-overflow-tooltip />
<el-table-column label="类型" width="90" align="center">
<template #default="{ row }">
<el-tag size="small" :type="row.stationType === 'OFFLINE' ? 'info' : 'success'">
{{ row.stationType === 'OFFLINE' ? '线下位' : '线上位' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="绑定工艺流程" min-width="240">
<template #default="{ row }">
<el-select :model-value="row.flowId" size="small" placeholder="未绑定" @change="(v) => bindFlow(row, v)" style="width:100%">
@@ -21,11 +33,23 @@
</el-table-column>
</el-table>
<el-dialog v-model="dlg.show" :title="`工位${dlg.stationNo} - 修改名称`" width="420px">
<el-dialog v-model="dlg.show" :title="dlg.title" width="440px">
<el-form :model="dlg" label-width="90px">
<el-form-item label="工位号" required>
<el-input-number v-model="dlg.stationNo" :min="0" :max="999" :disabled="!dlg.isAdd" style="width:100%" />
<div v-if="dlg.isAdd" style="color:#909399;font-size:12px;line-height:1.5;margin-top:4px">
自定义工位号可与现有工位不连续 14150 为上线位13 为下线位以外的编号均按物理/虚拟位识别
</div>
</el-form-item>
<el-form-item label="工位名称" required>
<el-input v-model="dlg.name" placeholder="如 一号装配工位" />
</el-form-item>
<el-form-item label="工位类型">
<el-radio-group v-model="dlg.stationType">
<el-radio label="LINE">线上位 PLC</el-radio>
<el-radio label="OFFLINE">线下位虚拟不连 PLC</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dlg.show = false">取消</el-button>
@@ -50,32 +74,42 @@ const flows = ref([])
const rows = computed(() => {
return [...stations.value]
.sort((a, b) => (a.stationNo || 0) - (b.stationNo || 0))
.map((s) => ({ id: s.id, stationNo: s.stationNo, name: s.name, flowId: s.flowId, flowName: s.flowName }))
.map((s) => ({ id: s.id, stationNo: s.stationNo, name: s.name, flowId: s.flowId, flowName: s.flowName, stationType: s.stationType }))
})
const dlg = reactive({ show: false, id: 0, stationNo: 1, name: '' })
const dlg = reactive({ show: false, isAdd: false, title: '', id: 0, stationNo: 1, name: '', stationType: 'LINE' })
async function load() {
stations.value = (await request.get('/stations')) || []
}
function openAdd() {
const used = new Set(stations.value.map((s) => s.stationNo))
let next = 14
while (used.has(next)) next++
Object.assign(dlg, { show: true, isAdd: true, title: '新增工位', id: 0, stationNo: next, name: '', stationType: 'LINE' })
}
function openName(row) {
Object.assign(dlg, { show: true, id: row.id, stationNo: row.stationNo, name: row.name })
Object.assign(dlg, { show: true, isAdd: false, title: `工位${row.stationNo} - 修改`, id: row.id, stationNo: row.stationNo, name: row.name, stationType: row.stationType || 'LINE' })
}
async function save() {
if (!dlg.name) return ElMessage.warning('请输入工位名称')
if (dlg.isAdd && stations.value.some((s) => s.stationNo === dlg.stationNo)) {
return ElMessage.warning(`工位${dlg.stationNo} 已存在,请换一个工位号`)
}
await request.post('/stations', {
id: dlg.id, stationNo: dlg.stationNo, name: dlg.name
id: dlg.id, stationNo: dlg.stationNo, name: dlg.name, stationType: dlg.stationType
})
ElMessage.success('已保存')
ElMessage.success(dlg.isAdd ? '工位已新增' : '已保存')
dlg.show = false
load()
}
async function bindFlow(row, fid) {
try {
await request.post('/stations', { id: row.id, stationNo: row.stationNo, name: row.name, flowId: fid })
await request.post('/stations', { id: row.id, stationNo: row.stationNo, name: row.name, stationType: row.stationType || 'LINE', flowId: fid })
ElMessage.success(fid ? '已绑定工艺流程' : '已解除绑定')
} catch {
// 拦截器已提示(如绑定到未启用的流程)