refactor: 统一术语BOM为物料清单并补充工位扩展能力
主要变更: 1. 后端:重构工位管理逻辑,新增工位自定义扩展能力,将工位号上限调整为999并支持14+虚拟工位 2. 前后端:全量替换"BOM"术语为"物料清单",包括提示语、注释和文档 3. WMS模块:新增AGV任务取消接口与前端页面,对接海康RCS取消任务逻辑 4. 权限与种子数据:新增工位新增权限并更新权限配置 5. 修复错误校验逻辑,优化AGV客户端签名与请求处理
This commit is contained in:
@@ -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" />
|
||||
|
||||
@@ -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">
|
||||
自定义工位号,可与现有工位不连续(如 14、15…);0 为上线位、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 {
|
||||
// 拦截器已提示(如绑定到未启用的流程)
|
||||
|
||||
Reference in New Issue
Block a user