refactor: 全链路统一将物料编码改称图号,优化入库流程
1. 统一全系统UI与文档中的"物料编码"为客户口径"图号",覆盖前端页面、后端导出表头与帮助文档
2. 优化入库模块:
- 新增归属类型"其他"并完善注释
- 入库时自动回写入库单批次号
- 优化库位选择为三级联动下拉选择,替换原手动输入
- 新增入库归属编号录入功能
3. 更新需求文档,同步完成图号全链路改造的验收标记
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
export const helpProductType = {
|
||||
title: '产品型号(档案维护)',
|
||||
overview:
|
||||
'产品型号 = 企业要生产哪些产品的分类档案,是整条产线的源头基础数据。\n\n主数据链条:产品型号 → 工单(选产品型号+物料清单) → 物料清单(给该产品编号配料) → 日排产 → 备料单 → 库房出库 → 产线生产 → 完工成品自动回流 WMS 入库。\n\n关系说明(几对几):\n· 产品型号 ↔ 工单:1 对 N。一个产品型号可以建多个工单(同一型号今天做一批、明天做一批,工单号不同、产品编号相同)。\n· 产品型号 ↔ 物料清单:1 对多(选用制)。同一型号可并存多份物料清单(不同厂家供货/含半成品结构),用「物料清单名称」区分,工单建单时选定一份,备料/装机都按工单锁定的那份算。\n· 产品型号 ↔ 备料单:间接。备料单由「日排产 × 工单锁定的物料清单」算出,与产品型号无直接约束。\n· 强约束:工单/物料清单都挂在产品编号下,产品型号编码变了,下游关联会断,建议建好后不要改。\n· 与 WMS 的关系:两套系统解耦。产品/物料编码主数据以 WMS 为单一来源,MES 实时拉取(WMS 不可达时允许手填、仅提示不强校验);成品完工后由系统自动生成 SN 回流 WMS 入库,无需人工对账。',
|
||||
'产品型号 = 企业要生产哪些产品的分类档案,是整条产线的源头基础数据。\n\n主数据链条:产品型号 → 工单(选产品型号+物料清单) → 物料清单(给该产品编号配料) → 日排产 → 备料单 → 库房出库 → 产线生产 → 完工成品自动回流 WMS 入库。\n\n关系说明(几对几):\n· 产品型号 ↔ 工单:1 对 N。一个产品型号可以建多个工单(同一型号今天做一批、明天做一批,工单号不同、产品编号相同)。\n· 产品型号 ↔ 物料清单:1 对多(选用制)。同一型号可并存多份物料清单(不同厂家供货/含半成品结构),用「物料清单名称」区分,工单建单时选定一份,备料/装机都按工单锁定的那份算。\n· 产品型号 ↔ 备料单:间接。备料单由「日排产 × 工单锁定的物料清单」算出,与产品型号无直接约束。\n· 强约束:工单/物料清单都挂在产品编号下,产品型号编码变了,下游关联会断,建议建好后不要改。\n· 与 WMS 的关系:两套系统解耦。产品/图号主数据以 WMS 为单一来源,MES 实时拉取(WMS 不可达时允许手填、仅提示不强校验);成品完工后由系统自动生成 SN 回流 WMS 入库,无需人工对账。',
|
||||
fields: [
|
||||
{ name: '名称', source: '手工填写。用户自定义,无上游来源。', purpose: '产品型号的中文名,用于识别与下拉展示', fill: '如:智能电表、断路器、配电柜' },
|
||||
{
|
||||
@@ -81,11 +81,11 @@ export const helpDailyPlan = {
|
||||
export const helpBom = {
|
||||
title: '物料清单',
|
||||
overview:
|
||||
'物料清单 = 生产 1 台该产品需要哪些物料、各用多少。\n按「产品编号 + 物料清单名称」维护:同一产品编号可并存多份物料清单(选用制)——不同厂家供货、含半成品的结构差异,都用不同的物料清单名称区分(缺省叫「默认」,不用 V1/V2 版本号)。\n工单建单时选定其中一份,之后该工单的备料算料、装机绑定校验都锁定这份物料清单,不同厂家的料单互不串。\n\n与 WMS 的关系:本页(MES)只维护「产品由哪几种料、各用几个」;每种物料叫什么、规格、是电气件(SN)还是结构件(批次)、库存有多少,统一在 WMS(库房)的「物料档案」里维护,两套系统互相解耦、通过物料编码对齐。\n\n编码对应规则:本页的「物料编码」必须与 WMS 物料档案里的「编码」完全一致;「管理方式」也要对应(电气件=SN 逐件,结构件=批次)。编码对不上,WMS 将无法为对应工单备料/出库。\n\n保护规则:某份物料清单一旦被工单选用过,就不允许再从中移除物料(防止历史工单追溯断链);新建/改名的物料清单名称在本页「物料清单名称」下拉直接输入即可创建。\n\n数据链路:先建「产品型号」→ 到本页选产品编号 + 命名物料清单,逐条添加物料 → 建工单时选定物料清单 → 仓库才能为对应工单领料。',
|
||||
'物料清单 = 生产 1 台该产品需要哪些物料、各用多少。\n按「产品编号 + 物料清单名称」维护:同一产品编号可并存多份物料清单(选用制)——不同厂家供货、含半成品的结构差异,都用不同的物料清单名称区分(缺省叫「默认」,不用 V1/V2 版本号)。\n工单建单时选定其中一份,之后该工单的备料算料、装机绑定校验都锁定这份物料清单,不同厂家的料单互不串。\n\n与 WMS 的关系:本页(MES)只维护「产品由哪几种料、各用几个」;每种物料叫什么、规格、是电气件(SN)还是结构件(批次)、库存有多少,统一在 WMS(库房)的「物料档案」里维护,两套系统互相解耦、通过图号对齐。\n\n编码对应规则:本页的「图号」必须与 WMS 物料档案里的「图号」完全一致;「管理方式」也要对应(电气件=SN 逐件,结构件=批次)。编码对不上,WMS 将无法为对应工单备料/出库。\n\n保护规则:某份物料清单一旦被工单选用过,就不允许再从中移除物料(防止历史工单追溯断链);新建/改名的物料清单名称在本页「物料清单名称」下拉直接输入即可创建。\n\n数据链路:先建「产品型号」→ 到本页选产品编号 + 命名物料清单,逐条添加物料 → 建工单时选定物料清单 → 仓库才能为对应工单领料。',
|
||||
fields: [
|
||||
{ name: '物料清单名称', source: '下拉选择/输入(可搜索,输入新名即创建)。缺省「默认」。', purpose: '同一产品编号下多份物料清单的区分名,工单建单时按此选定', fill: '如「默认」「A厂料单」「含半成品」;同一型号下名称唯一' },
|
||||
{ name: '产品编号', source: '下拉选择。来自「产品型号」里建的编码。', purpose: '要为哪个产品配置物料清单', fill: '在查询区或「添加物料」弹窗里选择产品编号' },
|
||||
{ name: '物料编码', source: '手工填写。必须与 WMS(库房) 物料档案的编码一致。', purpose: '该物料在库房的全库唯一编码,两系统靠它关联', fill: '如 螺丝=GJ-LUOSI-M16;编码对不上 WMS 将无法备料' },
|
||||
{ name: '图号', source: '手工填写。必须与 WMS(库房) 物料档案的图号一致。', purpose: '该物料在库房的全库唯一编码,两系统靠它关联', fill: '如 螺丝=GJ-LUOSI-M16;图号对不上 WMS 将无法备料' },
|
||||
{ name: '物料名称', source: '手工填写。', purpose: '物料中文名,便于识别', fill: '如 十字螺丝、主控芯片' },
|
||||
{ name: '规格', source: '手工填写。', purpose: '物料规格型号', fill: '如 M16×80 / SMT-48脚' },
|
||||
{ name: '单位', source: '手工填写。', purpose: '计量单位,用于算量', fill: '个 / 件 / 套 / kg' },
|
||||
@@ -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内容构成(页面按页签展示,打印时全部输出):①工位流程总览——工位组合、每工位绑定的工艺流程、流程状态、步骤数、检验点数、拧紧步骤数、工艺图纸、该型号的历史作业时间;②工艺流程与步骤——逐工位列出每条步骤的采集方式(手工录入/自动采集/无需采集)、是否需检测确认、是否拧紧采集、考核标准(含上下限);③物料清单——该型号的 BOM 明细,按 BOM 名称分组,含图号/名称/规格/单位/管理方式/装配工位/单台用量/损耗率/需求总量;④检验卡项——把所有「需要检测确认」的步骤汇总成检验项清单,含判定标准。\n\n工位组合从哪来:优先取该产品编号最近一张工单的「工位组合」;若该型号还没有工单,则按全产线工位顺序(升序)展示,页面顶部会标注来源。\n\n打印/导出:点「打印 / 导出 PDF」,系统会在新窗口打开排版好的 A4 卡片版式,用浏览器打印即可(选择“另存为 PDF”即导出电子档)。',
|
||||
fields: [
|
||||
{ name: '产品型号', source: '下拉选择,来自「产品型号」页。', purpose: '要查看哪个型号的全景流程', fill: '选定后自动带出产品编号' },
|
||||
{ name: '产品编号', source: '自动带出(也可手工填写,直接按编号生成)。', purpose: '匹配工单、物料清单的关键字', fill: '一般无需手填' },
|
||||
@@ -409,7 +409,7 @@ export const helpQtyReport = {
|
||||
{ name: '工位', source: '工位终端上报自动带入;本页手录时手动选择(工位数以数据库为准)。', purpose: '差异发生在哪个工位', fill: '选工位号' },
|
||||
{ name: '工单号', source: '工位终端上报自动带入;来自【工单管理】。', purpose: '定位是哪张工单的物料', fill: '搜索/输入工单号' },
|
||||
{ name: '产品序列号', source: '工位终端上报自动带入(当前作业件)。', purpose: '追溯到具体那一件', fill: '扫描/输入 SN' },
|
||||
{ name: '物料编码/名称', source: '工位终端上报自动带入(当前收料物料)。', purpose: '是哪一种物料数量不符', fill: '选择/输入物料' },
|
||||
{ name: '图号/名称', source: '工位终端上报自动带入(当前收料物料)。', purpose: '是哪一种物料数量不符', fill: '选择/输入物料' },
|
||||
{ name: '应发数量', source: '手动/自动带入备料单应发数。', purpose: '作为差异计算基准', fill: '填应发数' },
|
||||
{ name: '实收数量', source: '手动填写(现场实际清点结果)。', purpose: '作为差异计算基准', fill: '填实收数' },
|
||||
{ name: '差异', source: '系统自动计算(实收−应发)。', purpose: '一眼看出多收/少收', fill: '只读' },
|
||||
@@ -423,7 +423,7 @@ export const helpQuality = {
|
||||
overview:
|
||||
'质量检验 = 对物料/产品做检验判定并留痕,分「过程检」与「完工检」两个 Tab。来料检在 WMS 库房侧完成,本页不重复。\n\n录入流程:选图号(输入图号或名称远程检索 BOM 料,自动带出物料名称)→ 填报检单号/生产厂家/检验单号 → 上传附件 → 判定合格/不合格 → 保存。\n\n不合格闭环:结论为「不合格」时可当场选择处置方式(退货/返修/退换)并写处置说明;也可在列表操作列「处置」补录,或点「不合格单」一键导出《不合格单.xlsx》(预填本次检验数据)。\n\n查询:图号 / 物料名称 / 报检单号 / 记录日期范围组合筛选,每个框内模糊匹配、多框取交集;记录按时间倒序,后端真分页。',
|
||||
fields: [
|
||||
{ name: '图号', source: '远程检索 BOM 料(/quality/bom-material),也可手填。', purpose: '被检物料的图号(即物料编码)', fill: '输入图号或名称关键字选择' },
|
||||
{ name: '图号', source: '远程检索 BOM 料(/quality/bom-material),也可手填。', purpose: '被检物料的图号(即 WMS 物料档案的图号)', fill: '输入图号或名称关键字选择' },
|
||||
{ name: '物料名称', source: '选图号后自动带出,可手改。', purpose: '被检物料名称', fill: '一般无需手填' },
|
||||
{ name: '报检单号', source: '系统自动生成(RQ+时间戳),可改。', purpose: '本次报检的唯一编号,也是附件挂靠键', fill: '一般无需改,须唯一' },
|
||||
{ name: '生产厂家', source: '手工填写。', purpose: '该物料的生产/供货厂家', fill: '如 XX电气有限公司' },
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="业务ID">
|
||||
<el-input v-model="form.bizId" placeholder="如 工单号 / 物料编码" style="width:220px" />
|
||||
<el-input v-model="form.bizId" placeholder="如 工单号 / 图号" style="width:220px" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-upload action="#" :show-file-list="false" :http-request="doUpload">
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<el-button type="primary" @click="load">查询</el-button>
|
||||
<el-button v-if="can('produce.bom:edit')" @click="openAdd">添加物料</el-button>
|
||||
<el-tooltip v-if="can('produce.bom:edit')" placement="bottom"
|
||||
content="模板列(首行表头):产品编号 | 物料清单名称 | 物料编码 | 名称 | 规格 | 单位 | 单台用量 | 损耗率 | 装配工位 | 相关标准 | 备注。按(产品编号+物料清单名称)分组覆盖式导入。">
|
||||
content="模板列(首行表头):产品编号 | 物料清单名称 | 图号 | 名称 | 规格 | 单位 | 单台用量 | 损耗率 | 装配工位 | 相关标准 | 备注。按(产品编号+物料清单名称)分组覆盖式导入。">
|
||||
<el-upload action="#" :show-file-list="false" :http-request="doImport" accept=".xlsx,.xls" style="display:inline-block;margin-left:10px">
|
||||
<el-button type="warning">导入 Excel</el-button>
|
||||
</el-upload>
|
||||
@@ -32,7 +32,7 @@
|
||||
<el-tag size="small" effect="plain">{{ row.bomName }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="materialCode" label="物料编码" min-width="120" />
|
||||
<el-table-column prop="materialCode" label="图号" min-width="120" />
|
||||
<el-table-column prop="materialName" label="物料名称" min-width="120" />
|
||||
<el-table-column prop="spec" label="规格" />
|
||||
<el-table-column prop="unit" label="单位" width="70" />
|
||||
@@ -65,14 +65,14 @@
|
||||
|
||||
<el-dialog v-model="add.show" title="添加物料清单" width="520px">
|
||||
<el-alert type="info" :closable="false" style="margin-bottom:12px"
|
||||
title="物料编码须与 WMS(库房)「物料档案」的编码完全一致,才能备料/出库。管理方式:电气件按 SN 逐件、结构件按批次。" />
|
||||
title="图号须与 WMS(库房)「物料档案」的图号完全一致,才能备料/出库。管理方式:电气件按 SN 逐件、结构件按批次。" />
|
||||
<el-form :model="add" label-width="90px">
|
||||
<el-form-item label="产品编号" required>
|
||||
<el-select v-model="add.productCode" filterable placeholder="选择产品编号" style="width:100%">
|
||||
<el-option v-for="p in products" :key="p.code" :label="`${p.code} ${p.name || ''}`" :value="p.code" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="物料编码" required><el-input v-model="add.materialCode" placeholder="须与 WMS 物料档案编码一致" /></el-form-item>
|
||||
<el-form-item label="图号" required><el-input v-model="add.materialCode" placeholder="须与 WMS 物料档案图号一致" /></el-form-item>
|
||||
<el-form-item label="物料名称"><el-input v-model="add.materialName" /></el-form-item>
|
||||
<el-form-item label="规格"><el-input v-model="add.spec" /></el-form-item>
|
||||
<el-form-item label="单位"><el-input v-model="add.unit" /></el-form-item>
|
||||
@@ -149,7 +149,7 @@ function openAdd() {
|
||||
}
|
||||
async function save() {
|
||||
if (!add.productCode) return ElMessage.warning('请选择产品编号')
|
||||
if (!add.materialCode) return ElMessage.warning('请填写物料编码')
|
||||
if (!add.materialCode) return ElMessage.warning('请填写图号')
|
||||
const { requiredQty, ...item } = add
|
||||
if (!item.processCode) item.processCode = 0
|
||||
await request.put('/bom', { productCode: add.productCode, bomName: bomName.value, items: [{ ...item }] })
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<el-date-picker v-model="query.planDate" type="date" value-format="YYYY-MM-DD" @change="load" />
|
||||
</el-form-item>
|
||||
<el-form-item label="工单号"><el-input v-model="query.orderNo" clearable style="width:160px" @keyup.enter="load" /></el-form-item>
|
||||
<el-form-item label="物料编码"><el-input v-model="query.materialCode" clearable style="width:150px" placeholder="不区分大小写" @keyup.enter="load" /></el-form-item>
|
||||
<el-form-item label="图号"><el-input v-model="query.materialCode" clearable style="width:150px" placeholder="不区分大小写" @keyup.enter="load" /></el-form-item>
|
||||
<el-form-item label="物料名称"><el-input v-model="query.materialName" clearable style="width:150px" @keyup.enter="load" /></el-form-item>
|
||||
<el-form-item label="接驳台"><el-input v-model="query.targetDock" clearable style="width:120px" placeholder="如 DOCK05" @keyup.enter="load" /></el-form-item>
|
||||
<el-form-item label="状态">
|
||||
@@ -27,7 +27,7 @@
|
||||
<el-table-column prop="requestNo" label="备料单号" min-width="180" />
|
||||
<el-table-column prop="orderNo" label="工单号" min-width="130" />
|
||||
<el-table-column prop="planDate" label="排产日期" width="110" />
|
||||
<el-table-column prop="materialCode" label="物料编码" min-width="110" />
|
||||
<el-table-column prop="materialCode" label="图号" min-width="110" />
|
||||
<el-table-column prop="materialName" label="物料名称" min-width="110" />
|
||||
<el-table-column prop="unit" label="单位" width="70" />
|
||||
<el-table-column prop="manageMode" label="管理方式" width="110">
|
||||
|
||||
@@ -109,7 +109,7 @@
|
||||
<div v-for="g in card.bomGroups" :key="g.bomName" class="blk">
|
||||
<div class="blk-t">BOM:{{ 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="materialCode" label="图号" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column prop="materialName" label="物料名称" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column prop="spec" label="规格" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="unit" label="单位" width="70" align="center" />
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="工单号"><el-input v-model="query.orderNo" clearable style="width:160px" @keyup.enter="load" /></el-form-item>
|
||||
<el-form-item label="物料编码"><el-input v-model="query.materialCode" clearable style="width:150px" placeholder="不区分大小写" @keyup.enter="load" /></el-form-item>
|
||||
<el-form-item label="图号"><el-input v-model="query.materialCode" clearable style="width:150px" placeholder="不区分大小写" @keyup.enter="load" /></el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="load">查询</el-button>
|
||||
</el-form-item>
|
||||
@@ -21,7 +21,7 @@
|
||||
<el-table-column prop="stationNo" label="工位" width="70" align="center" />
|
||||
<el-table-column prop="orderNo" label="工单号" min-width="130" />
|
||||
<el-table-column prop="sn" label="产品序列号" min-width="140" />
|
||||
<el-table-column prop="materialCode" label="物料编码" min-width="110" />
|
||||
<el-table-column prop="materialCode" label="图号" min-width="110" />
|
||||
<el-table-column prop="materialName" label="物料名称" min-width="110" />
|
||||
<el-table-column prop="planQty" label="应发" width="70" align="center" />
|
||||
<el-table-column prop="actualQty" label="实收" width="70" align="center" />
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
title="该产品未在当前装配工位配置需绑定物料(物料清单「装配工位」为空),不校验" />
|
||||
</div>
|
||||
<el-table v-if="bindPanel.required && bindPanel.required.length" :data="bindPanel.required" border size="small">
|
||||
<el-table-column prop="materialCode" label="物料编码" min-width="110" />
|
||||
<el-table-column prop="materialCode" label="图号" min-width="110" />
|
||||
<el-table-column prop="materialName" label="物料名称" min-width="120" />
|
||||
<el-table-column prop="manageMode" label="管理方式" width="120">
|
||||
<template #default="{row}">{{ row.manageMode === '2' ? '电气件(SN)' : '结构件(批次)' }}</template>
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
:title="data.bindTrace?.complete ? '装配物料已全部绑齐 ✓' : ('装配物料未绑齐:' + (data.bindTrace?.missing || ''))" show-icon />
|
||||
<el-table :data="data.bindTrace?.binds||[]" border size="small">
|
||||
<el-table-column prop="processCode" label="工序" width="70" />
|
||||
<el-table-column prop="materialCode" label="物料编码" min-width="110" />
|
||||
<el-table-column prop="materialCode" label="图号" min-width="110" />
|
||||
<el-table-column prop="materialName" label="物料名称" min-width="110" />
|
||||
<el-table-column prop="spec" label="规格" min-width="90" />
|
||||
<el-table-column prop="bindType" label="类型" width="70">
|
||||
|
||||
@@ -135,7 +135,7 @@ func UnqualifiedReportHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
f := excelize.NewFile()
|
||||
sheet := "不合格单"
|
||||
f.SetSheetName("Sheet1", sheet)
|
||||
headers := []string{"报检单号", "物料编码(图号)", "物料名称", "生产厂家", "检验单号", "结论", "处置方式", "处置说明", "记录时间"}
|
||||
headers := []string{"报检单号", "图号", "物料名称", "生产厂家", "检验单号", "结论", "处置方式", "处置说明", "记录时间"}
|
||||
for c, h := range headers {
|
||||
cell, _ := excelize.CoordinatesToCellName(c+1, 1)
|
||||
_ = f.SetCellValue(sheet, cell, h)
|
||||
|
||||
@@ -531,7 +531,7 @@ func renderModelCard(c *modelCard) (string, error) {
|
||||
<table>
|
||||
<thead>
|
||||
<tr><th colspan="9" class="st-name">BOM:{{.BomName}}</th></tr>
|
||||
<tr><th>物料编码</th><th>物料名称</th><th>规格</th><th>单位</th><th>管理方式</th><th>装配工位</th><th>单台用量</th><th>损耗率(%)</th><th>需求总量</th></tr>
|
||||
<tr><th>图号</th><th>物料名称</th><th>规格</th><th>单位</th><th>管理方式</th><th>装配工位</th><th>单台用量</th><th>损耗率(%)</th><th>需求总量</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range .Items}}
|
||||
|
||||
@@ -61,7 +61,7 @@ export const helpInbound = {
|
||||
export const helpOutbound = {
|
||||
title: '出库管理(统一主表)',
|
||||
overview:
|
||||
'所有出库都落在同一张「出库主表」(OutboundOrder),用"类型"区分,装箱信息(箱号/SN清单/合同号)也直接记在主表上,不再单独建装箱表。\n两个出库页签 + 一个记录页签:\n· 工单备料出库(MES):对接 MES 工单台账领料,强约束"单物料累计出库 ≤ 工单需求总量";未启用 MES 或工单未同步时,工单下拉可能为空,可直接手输工单号(需台账已同步)。\n· 通用出库:不依赖工单的手动出库(退料/样品/报废/发货),支持结构件批次与电气件 SN,填箱号即一并记录装箱;MES 不可用或无需挂靠工单时请用本页。\n· 出库记录:统一主表的全量查询与导出(一键导出 CSV,含装箱信息)。\n选用原则:有 MES 工单要按 物料清单 领料 → 工单备料出库;无工单的自由出库/装箱发货 → 通用出库。\n\n说明:出库列表中的"图号"即物料编码(客户口径统一称"图号");通用出库涵盖退料/样品/报废/发货等场景,无固定分类下拉,具体去向记在"备注"。质量门禁:质量状态为「不合格/待退」的批次禁止领料上线,须先完成不合格处置。',
|
||||
'所有出库都落在同一张「出库主表」(OutboundOrder),用"类型"区分,装箱信息(箱号/SN清单/合同号)也直接记在主表上,不再单独建装箱表。\n两个出库页签 + 一个记录页签:\n· 工单备料出库(MES):对接 MES 工单台账领料,强约束"单物料累计出库 ≤ 工单需求总量";未启用 MES 或工单未同步时,工单下拉可能为空,可直接手输工单号(需台账已同步)。\n· 通用出库:不依赖工单的手动出库(退料/样品/报废/发货),支持结构件批次与电气件 SN,填箱号即一并记录装箱;MES 不可用或无需挂靠工单时请用本页。\n· 出库记录:统一主表的全量查询与导出(一键导出 CSV,含装箱信息)。\n选用原则:有 MES 工单要按 物料清单 领料 → 工单备料出库;无工单的自由出库/装箱发货 → 通用出库。\n\n说明:出库列表统一以"图号"展示物料(客户口径统一称"图号");通用出库涵盖退料/样品/报废/发货等场景,无固定分类下拉,具体去向记在"备注"。质量门禁:质量状态为「不合格/待退」的批次禁止领料上线,须先完成不合格处置。',
|
||||
fields: [
|
||||
{ name: '备料出库-工单号', source: '手动输入。数据来自 MES 已建的工单及导入的物料台账。', purpose: '出库挂靠的工单,决定可领料总量与需求缺口', fill: '输入工单号回车,查询该工单的物料台账' },
|
||||
{ name: '备料出库-目标工位', source: '下拉选择 DOCK01~DOCK20(选填,可过滤搜索)。', purpose: 'AGV 配送送达的接驳台(toDock 参数)', fill: '选择如 DOCK05;不选默认运到产线入口 DOCK01' },
|
||||
@@ -216,9 +216,9 @@ export const helpZone = {
|
||||
export const helpMaterial = {
|
||||
title: '物料档案',
|
||||
overview:
|
||||
'定义每一种物料的字典:叫什么、什么规格、什么品类(原材料/半成品/成品)、是【结构件(批次)】还是【电气件(SN)】。\n此处维护的物料会作为下拉选项出现在入库/出库/装箱等页面;其中"类型"直接决定后续入库方式——结构件走批次(一批多件)、电气件走 SN 逐件扫码(单件追溯)。\n\n品类说明:1=原材料(默认) / 2=半成品 / 3=成品。编码主数据单一来源在本表:产品型号档案即本表中"成品"品类的行(MES 实时拉取,不双录);库存主表 inventories 的品类与其呼应。列表支持按品类筛选,导出含品类列。\n\n物料档案是库房的另一道地基:先建好物料,入库时才能选到;类型选错会导致入库方式与追溯粒度不符。\n\n命名说明:物料编码在出库/看板等页面统一显示为"图号"(客户口径)。安全库存在本档案维护,可用量低于它时会在「库房看板」首页触发缺货预警。',
|
||||
'定义每一种物料的字典:叫什么、什么规格、什么品类(原材料/半成品/成品)、是【结构件(批次)】还是【电气件(SN)】。\n此处维护的物料会作为下拉选项出现在入库/出库/装箱等页面;其中"类型"直接决定后续入库方式——结构件走批次(一批多件)、电气件走 SN 逐件扫码(单件追溯)。\n\n品类说明:1=原材料(默认) / 2=半成品 / 3=成品。编码主数据单一来源在本表:产品型号档案即本表中"成品"品类的行(MES 实时拉取,不双录);库存主表 inventories 的品类与其呼应。列表支持按品类筛选,导出含品类列。\n\n物料档案是库房的另一道地基:先建好物料,入库时才能选到;类型选错会导致入库方式与追溯粒度不符。\n\n命名说明:本档案维护的物料在出库/看板等页面统一以"图号"展示(客户口径)。安全库存在本档案维护,可用量低于它时会在「库房看板」首页触发缺货预警。',
|
||||
fields: [
|
||||
{ name: '物料编码', source: '手工填写,仓库自定义(新增后不可改)。在出库/看板等页面显示为"图号"。', purpose: '物料唯一编号(即图号)', fill: '如 SCREW-M3;新增后不可改编码' },
|
||||
{ name: '图号', source: '手工填写,仓库自定义(新增后不可改)。在出库/看板等页面显示为"图号"。', purpose: '物料唯一编号(即图号)', fill: '如 SCREW-M3;新增后不可改编码' },
|
||||
{ name: '名称', source: '手工填写。', purpose: '物料名称', fill: '如 螺丝 M3' },
|
||||
{ name: '简称', source: '手工填写(可空)。', purpose: '简写', fill: '可空' },
|
||||
{ name: '规格', source: '手工填写(可空)。', purpose: '规格型号', fill: '可空' },
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup>
|
||||
defineOptions({ name: 'BaseData' })
|
||||
import { computed, reactive, ref, watch } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import request from '../utils/request'
|
||||
import { fmtTime } from '../utils/format'
|
||||
import { can } from '../utils/perm'
|
||||
@@ -50,7 +50,7 @@ async function loadZones() {
|
||||
try {
|
||||
const data = await request.get('/zone/list', {
|
||||
params: {
|
||||
zoneCode: zoneFilter.zoneCode.trim(),
|
||||
zoneCode: zoneFilter.zoneCode.trim().toUpperCase(),
|
||||
zoneName: zoneFilter.zoneName.trim(),
|
||||
status: zoneFilter.status,
|
||||
page: zonePage.current,
|
||||
@@ -89,6 +89,15 @@ async function saveZone() {
|
||||
ElMessage.warning('区域编码和区域名称必填')
|
||||
return
|
||||
}
|
||||
try {
|
||||
await ElMessageBox.confirm('确认保存该区域信息吗?保存后将写入数据库。', '操作确认', {
|
||||
confirmButtonText: '确认保存',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
})
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
zoneSaving.value = true
|
||||
try {
|
||||
if (zoneEditingId.value) {
|
||||
@@ -188,6 +197,15 @@ async function saveMaterial() {
|
||||
ElMessage.warning('「其他」类物料必须填写规格型号')
|
||||
return
|
||||
}
|
||||
try {
|
||||
await ElMessageBox.confirm('确认保存该物料档案吗?保存后将写入数据库。', '操作确认', {
|
||||
confirmButtonText: '确认保存',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
})
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
mSaving.value = true
|
||||
try {
|
||||
if (mEditingId.value) {
|
||||
@@ -199,18 +217,21 @@ async function saveMaterial() {
|
||||
manageMode: mForm.manageMode
|
||||
})
|
||||
ElMessage.success('物料修改成功')
|
||||
matDialog.value = false
|
||||
} else {
|
||||
await request.post('/material/create', { ...mForm, itemType: Number(mForm.itemType), safetyStock: Number(mForm.safetyStock) || 0 })
|
||||
ElMessage.success('物料新增成功')
|
||||
const created = await request.post('/material/create', { ...mForm, itemType: Number(mForm.itemType), safetyStock: Number(mForm.safetyStock) || 0 })
|
||||
ElMessage.success('物料新增成功,可继续上传图纸/附件')
|
||||
// 创建成功后切到编辑态:保持弹窗打开,便于立即上传附件
|
||||
mEditingId.value = created?.id || 0
|
||||
if (created?.code) mForm.code = created.code
|
||||
}
|
||||
matDialog.value = false
|
||||
await loadMaterials()
|
||||
} finally {
|
||||
mSaving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 物料附件(bizType=material,bizId=物料编码):零件图纸 PDF 等
|
||||
// 物料附件(bizType=material,bizId=图号):零件图纸 PDF 等
|
||||
const attachDrawer = reactive({ visible: false, code: '', name: '' })
|
||||
function openMatAttach(row) {
|
||||
Object.assign(attachDrawer, { visible: true, code: row.code, name: row.name })
|
||||
@@ -308,7 +329,7 @@ async function onMaterialFile(e) {
|
||||
<el-form inline style="margin-bottom:14px" @submit.prevent="searchZones">
|
||||
<el-form-item label="区域编码">
|
||||
<el-input v-model="zoneFilter.zoneCode" placeholder="区域编码" clearable style="width:140px"
|
||||
@keyup.enter="searchZones" />
|
||||
@keyup.enter="searchZones" @input="zoneFilter.zoneCode = (zoneFilter.zoneCode || '').toUpperCase()" />
|
||||
</el-form-item>
|
||||
<el-form-item label="区域名称">
|
||||
<el-input v-model="zoneFilter.zoneName" placeholder="区域名称" clearable style="width:150px"
|
||||
@@ -368,7 +389,7 @@ async function onMaterialFile(e) {
|
||||
append-to-body destroy-on-close>
|
||||
<el-form label-width="90px">
|
||||
<el-form-item label="区域编码" required>
|
||||
<el-input v-model="zoneForm.zoneCode" placeholder="如 Z05" :disabled="!!zoneEditingId" clearable />
|
||||
<el-input v-model="zoneForm.zoneCode" placeholder="如 Z05" :disabled="!!zoneEditingId" clearable @input="zoneForm.zoneCode = (zoneForm.zoneCode || '').toUpperCase()" />
|
||||
</el-form-item>
|
||||
<el-form-item label="区域名称" required>
|
||||
<el-input v-model="zoneForm.zoneName" placeholder="如 待检区" clearable />
|
||||
@@ -486,8 +507,8 @@ async function onMaterialFile(e) {
|
||||
<el-dialog v-model="matDialog" :title="mEditingId ? '编辑物料' : '新增物料'" width="560px"
|
||||
append-to-body destroy-on-close>
|
||||
<el-form label-width="90px">
|
||||
<el-form-item label="物料编码/图号" required>
|
||||
<el-input v-model="mForm.code" placeholder="图号即物料编码,如 SCREW-M3" :disabled="!!mEditingId" clearable />
|
||||
<el-form-item label="图号" required>
|
||||
<el-input v-model="mForm.code" placeholder="图号,如 SCREW-M3" :disabled="!!mEditingId" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="名称" required>
|
||||
<el-input v-model="mForm.name" placeholder="如 螺丝 M3" clearable />
|
||||
@@ -523,6 +544,7 @@ async function onMaterialFile(e) {
|
||||
<el-input v-model="mForm.description" type="textarea" :rows="2" placeholder="备注(可空)" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<AttachmentPanel v-if="mEditingId" biz-type="material" :biz-id="mForm.code" title="附件(零件图纸 / 资料)" />
|
||||
<template #footer>
|
||||
<el-button @click="matDialog = false">取消</el-button>
|
||||
<el-button type="primary" :loading="mSaving" @click="saveMaterial">保存</el-button>
|
||||
|
||||
@@ -28,6 +28,27 @@ async function loadDicts() {
|
||||
zones.value = Array.isArray(zs) ? zs : zs?.list || []
|
||||
}
|
||||
|
||||
// 库位三级联动:基于已加载的 zones(每行是完整四级货位 zoneCode+shelfNo+layerNo+positionNo)派生可选项
|
||||
function zoneShelves(zoneCode) {
|
||||
const set = new Set()
|
||||
zones.value.forEach(z => { if (z.zoneCode === zoneCode && z.shelfNo) set.add(z.shelfNo) })
|
||||
return [...set].sort()
|
||||
}
|
||||
function zoneLayers(zoneCode, shelfNo) {
|
||||
const set = new Set()
|
||||
zones.value.forEach(z => { if (z.zoneCode === zoneCode && z.shelfNo === shelfNo && z.layerNo) set.add(z.layerNo) })
|
||||
return [...set].sort()
|
||||
}
|
||||
function zonePositions(zoneCode, shelfNo, layerNo) {
|
||||
const set = new Set()
|
||||
zones.value.forEach(z => { if (z.zoneCode === zoneCode && z.shelfNo === shelfNo && z.layerNo === layerNo && z.positionNo) set.add(z.positionNo) })
|
||||
return [...set].sort()
|
||||
}
|
||||
// 切换上级库位时清空下级,避免残留无效值(用户诉求:库位逐级联动选择,禁止手填)
|
||||
function onZoneChange(form) { form.shelfNo = ''; form.layerNo = ''; form.positionNo = '' }
|
||||
function onShelfChange(form) { form.layerNo = ''; form.positionNo = '' }
|
||||
function onLayerChange(form) { form.positionNo = '' }
|
||||
|
||||
/* ===================== Tab1 结构件入库(批次,支持多批到货追加) ===================== */
|
||||
const batchRef = ref()
|
||||
const submitting1 = ref(false)
|
||||
@@ -35,6 +56,7 @@ const lastInboundNo = ref('')
|
||||
const form1 = reactive({
|
||||
materialCode: '', batchNo: '', quantity: 1,
|
||||
zoneCode: '', shelfNo: '', layerNo: '', positionNo: '',
|
||||
ownershipType: 'none', ownershipNo: '',
|
||||
productionDate: '', supplier: '', remark: '', inspectionNo: ''
|
||||
})
|
||||
const rules1 = {
|
||||
@@ -71,6 +93,8 @@ async function submitBatch() {
|
||||
positionNo: form1.positionNo.trim(),
|
||||
productionDate: form1.productionDate,
|
||||
supplier: form1.supplier.trim(),
|
||||
ownershipType: form1.ownershipType,
|
||||
ownershipNo: form1.ownershipNo.trim(),
|
||||
remark: form1.remark.trim(),
|
||||
inspectionNo: form1.inspectionNo.trim(),
|
||||
operator
|
||||
@@ -90,7 +114,7 @@ async function submitBatch() {
|
||||
const snInput = ref('')
|
||||
const submitting2 = ref(false)
|
||||
const snList = ref([])
|
||||
const form2 = reactive({ materialCode: '', zoneCode: '', shelfNo: '', layerNo: '', positionNo: '', remark: '', inspectionNo: '' })
|
||||
const form2 = reactive({ materialCode: '', zoneCode: '', shelfNo: '', layerNo: '', positionNo: '', ownershipType: 'none', ownershipNo: '', remark: '', inspectionNo: '' })
|
||||
|
||||
// 支持多种分隔符:中英文逗号、中英文句号、分号、顿号、空格、回车、制表符等
|
||||
const SN_SEP = /[,,.。;;、\s]+/
|
||||
@@ -142,6 +166,8 @@ async function submitSn() {
|
||||
layerNo: form2.layerNo.trim(),
|
||||
positionNo: form2.positionNo.trim(),
|
||||
snList: [...snList.value],
|
||||
ownershipType: form2.ownershipType,
|
||||
ownershipNo: form2.ownershipNo.trim(),
|
||||
remark: form2.remark.trim(),
|
||||
inspectionNo: form2.inspectionNo.trim(),
|
||||
operator
|
||||
@@ -476,16 +502,22 @@ onActivated(() => {
|
||||
<el-input-number v-model="form1.quantity" :min="1" :step="1" step-strictly style="width:100%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="区域" prop="zoneCode">
|
||||
<el-select v-model="form1.zoneCode" placeholder="请选择入库区域(必填)" clearable style="width:100%">
|
||||
<el-select v-model="form1.zoneCode" placeholder="请选择入库区域(必填)" clearable style="width:100%" @change="onZoneChange(form1)">
|
||||
<el-option v-for="z in zones" :key="z.id" :value="z.zoneCode"
|
||||
:label="`${z.zoneCode} ${z.zoneName || ''}`" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="货架/层/位">
|
||||
<div style="display:flex;gap:6px;width:100%">
|
||||
<el-input v-model="form1.shelfNo" placeholder="货架 如A" clearable />
|
||||
<el-input v-model="form1.layerNo" placeholder="层 如3" clearable />
|
||||
<el-input v-model="form1.positionNo" placeholder="位置 如12" clearable />
|
||||
<el-form-item label="货架 / 层 / 位置">
|
||||
<div style="display:flex;gap:6px;flex-wrap:wrap;align-items:center">
|
||||
<el-select v-model="form1.shelfNo" placeholder="货架" clearable style="width:120px" @change="onShelfChange(form1)">
|
||||
<el-option v-for="s in zoneShelves(form1.zoneCode)" :key="s" :label="s + ' 架'" :value="s" />
|
||||
</el-select>
|
||||
<el-select v-model="form1.layerNo" placeholder="层" clearable style="width:100px" :disabled="!form1.shelfNo" @change="onLayerChange(form1)">
|
||||
<el-option v-for="l in zoneLayers(form1.zoneCode, form1.shelfNo)" :key="l" :label="l + ' 层'" :value="l" />
|
||||
</el-select>
|
||||
<el-select v-model="form1.positionNo" placeholder="位置" clearable style="width:110px" :disabled="!form1.shelfNo">
|
||||
<el-option v-for="p in zonePositions(form1.zoneCode, form1.shelfNo, form1.layerNo)" :key="p" :label="p + ' 位'" :value="p" />
|
||||
</el-select>
|
||||
</div>
|
||||
<el-button link type="primary" size="small" style="margin-top:2px" @click="openRecommend('form1')">推荐库位</el-button>
|
||||
</el-form-item>
|
||||
@@ -499,6 +531,17 @@ onActivated(() => {
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="form1.remark" placeholder="备注(可空)" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="归属">
|
||||
<el-select v-model="form1.ownershipType" style="width:160px;margin-right:8px">
|
||||
<el-option label="无" value="none" />
|
||||
<el-option label="工单号" value="workorder" />
|
||||
<el-option label="科技项目号" value="project" />
|
||||
<el-option label="其他" value="other" />
|
||||
<el-option label="其他" value="other" />
|
||||
</el-select>
|
||||
<el-input v-if="form1.ownershipType !== 'none'" v-model="form1.ownershipNo"
|
||||
placeholder="请填写归属编号" clearable style="width:260px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="检测单号" required>
|
||||
<el-input v-model="form1.inspectionNo" placeholder="入库检报告单号(必填)" clearable />
|
||||
</el-form-item>
|
||||
@@ -534,16 +577,22 @@ onActivated(() => {
|
||||
<MaterialSelect v-model="form2.materialCode" :manage-mode="2" placeholder="请选择电气件物料(必填)" />
|
||||
</el-form-item>
|
||||
<el-form-item label="区域" required>
|
||||
<el-select v-model="form2.zoneCode" placeholder="请选择入库区域(必填)" clearable style="width:100%">
|
||||
<el-select v-model="form2.zoneCode" placeholder="请选择入库区域(必填)" clearable style="width:100%" @change="onZoneChange(form2)">
|
||||
<el-option v-for="z in zones" :key="z.id" :value="z.zoneCode"
|
||||
:label="`${z.zoneCode} ${z.zoneName || ''}`" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="货架/层/位">
|
||||
<div style="display:flex;gap:6px;width:100%">
|
||||
<el-input v-model="form2.shelfNo" placeholder="货架 如A" clearable />
|
||||
<el-input v-model="form2.layerNo" placeholder="层 如3" clearable />
|
||||
<el-input v-model="form2.positionNo" placeholder="位置 如12" clearable />
|
||||
<el-form-item label="货架 / 层 / 位置">
|
||||
<div style="display:flex;gap:6px;flex-wrap:wrap;align-items:center">
|
||||
<el-select v-model="form2.shelfNo" placeholder="货架" clearable style="width:120px" @change="onShelfChange(form2)">
|
||||
<el-option v-for="s in zoneShelves(form2.zoneCode)" :key="s" :label="s + ' 架'" :value="s" />
|
||||
</el-select>
|
||||
<el-select v-model="form2.layerNo" placeholder="层" clearable style="width:100px" :disabled="!form2.shelfNo" @change="onLayerChange(form2)">
|
||||
<el-option v-for="l in zoneLayers(form2.zoneCode, form2.shelfNo)" :key="l" :label="l + ' 层'" :value="l" />
|
||||
</el-select>
|
||||
<el-select v-model="form2.positionNo" placeholder="位置" clearable style="width:110px" :disabled="!form2.shelfNo">
|
||||
<el-option v-for="p in zonePositions(form2.zoneCode, form2.shelfNo, form2.layerNo)" :key="p" :label="p + ' 位'" :value="p" />
|
||||
</el-select>
|
||||
</div>
|
||||
<el-button link type="primary" size="small" style="margin-top:2px" @click="openRecommend('form2')">推荐库位</el-button>
|
||||
</el-form-item>
|
||||
@@ -559,6 +608,17 @@ onActivated(() => {
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="form2.remark" type="textarea" :rows="2" placeholder="本批 SN 的统一备注(可空)" />
|
||||
</el-form-item>
|
||||
<el-form-item label="归属">
|
||||
<el-select v-model="form2.ownershipType" style="width:160px;margin-right:8px">
|
||||
<el-option label="无" value="none" />
|
||||
<el-option label="工单号" value="workorder" />
|
||||
<el-option label="科技项目号" value="project" />
|
||||
<el-option label="其他" value="other" />
|
||||
<el-option label="其他" value="other" />
|
||||
</el-select>
|
||||
<el-input v-if="form2.ownershipType !== 'none'" v-model="form2.ownershipNo"
|
||||
placeholder="请填写归属编号" clearable style="width:260px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="检测单号" required>
|
||||
<el-input v-model="form2.inspectionNo" placeholder="入库检报告单号(必填)" clearable />
|
||||
</el-form-item>
|
||||
@@ -620,16 +680,22 @@ onActivated(() => {
|
||||
<el-input-number v-model="form3.quantity" :min="1" :step="1" step-strictly style="width:100%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="区域" prop="zoneCode">
|
||||
<el-select v-model="form3.zoneCode" placeholder="请选择入库区域(必填)" clearable style="width:100%">
|
||||
<el-select v-model="form3.zoneCode" placeholder="请选择入库区域(必填)" clearable style="width:100%" @change="onZoneChange(form3)">
|
||||
<el-option v-for="z in zones" :key="z.id" :value="z.zoneCode"
|
||||
:label="`${z.zoneCode} ${z.zoneName || ''}`" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="货架/层/位">
|
||||
<div style="display:flex;gap:6px;width:100%">
|
||||
<el-input v-model="form3.shelfNo" placeholder="货架 如A" clearable />
|
||||
<el-input v-model="form3.layerNo" placeholder="层 如3" clearable />
|
||||
<el-input v-model="form3.positionNo" placeholder="位置 如12" clearable />
|
||||
<el-form-item label="货架 / 层 / 位置">
|
||||
<div style="display:flex;gap:6px;flex-wrap:wrap;align-items:center">
|
||||
<el-select v-model="form3.shelfNo" placeholder="货架" clearable style="width:120px" @change="onShelfChange(form3)">
|
||||
<el-option v-for="s in zoneShelves(form3.zoneCode)" :key="s" :label="s + ' 架'" :value="s" />
|
||||
</el-select>
|
||||
<el-select v-model="form3.layerNo" placeholder="层" clearable style="width:100px" :disabled="!form3.shelfNo" @change="onLayerChange(form3)">
|
||||
<el-option v-for="l in zoneLayers(form3.zoneCode, form3.shelfNo)" :key="l" :label="l + ' 层'" :value="l" />
|
||||
</el-select>
|
||||
<el-select v-model="form3.positionNo" placeholder="位置" clearable style="width:110px" :disabled="!form3.shelfNo">
|
||||
<el-option v-for="p in zonePositions(form3.zoneCode, form3.shelfNo, form3.layerNo)" :key="p" :label="p + ' 位'" :value="p" />
|
||||
</el-select>
|
||||
</div>
|
||||
<el-button link type="primary" size="small" style="margin-top:2px" @click="openRecommend('form3')">推荐库位</el-button>
|
||||
</el-form-item>
|
||||
@@ -638,6 +704,7 @@ onActivated(() => {
|
||||
<el-option label="无" value="none" />
|
||||
<el-option label="工单号" value="workorder" />
|
||||
<el-option label="科技项目号" value="project" />
|
||||
<el-option label="其他" value="other" />
|
||||
</el-select>
|
||||
<el-input v-if="form3.ownershipType !== 'none'" v-model="form3.ownershipNo"
|
||||
placeholder="请填写归属编号" clearable style="width:260px" />
|
||||
|
||||
@@ -56,7 +56,7 @@ const inputPlaceholder = computed(() =>
|
||||
)
|
||||
|
||||
// 库存拾取器:直接选已存在的批次号/SN,避免手输。
|
||||
// 规范:库存量不确定 → 只加载最近 100 条(created_at desc),输入走远程模糊搜索(keyword 匹配 物料编码/批次号/SN)
|
||||
// 规范:库存量不确定 → 只加载最近 100 条(created_at desc),输入走远程模糊搜索(keyword 匹配 图号/批次号/SN)
|
||||
const pickerOptions = ref([])
|
||||
const pickerLoading = ref(false)
|
||||
async function loadPicker(kw = '') {
|
||||
|
||||
@@ -72,7 +72,7 @@ func createInboundHandler(ctx *svc.ServiceContext) http.HandlerFunc {
|
||||
Supplier string `json:"supplier"`
|
||||
Remark string `json:"remark"`
|
||||
InspectionNo string `json:"inspectionNo"`
|
||||
OwnershipType string `json:"ownershipType"` // workorder/project/none
|
||||
OwnershipType string `json:"ownershipType"` // workorder/project/other/none
|
||||
OwnershipNo string `json:"ownershipNo"`
|
||||
}
|
||||
if err := parseJSON(r, &req); err != nil {
|
||||
@@ -183,6 +183,11 @@ func createInboundHandler(ctx *svc.ServiceContext) http.HandlerFunc {
|
||||
if batchNo == "" {
|
||||
batchNo = genBatchNo(ctx, req.MaterialCode)
|
||||
}
|
||||
// 回写入库单头:便于「入库记录」列表直接展示批次号(库存行/明细已含批次,此处补齐表头)
|
||||
if _, e := tx.InboundOrder.UpdateOneID(ib.ID).SetBatchNo(batchNo).Save(ctx0()); e != nil {
|
||||
fail(w, http.StatusInternalServerError, "回写入库单批次号失败: "+e.Error())
|
||||
return
|
||||
}
|
||||
qty := req.Quantity
|
||||
if qty <= 0 {
|
||||
fail(w, http.StatusBadRequest, "数量必填且大于 0,请输入本次入库数量")
|
||||
@@ -476,10 +481,10 @@ func exportInboundHandler(ctx *svc.ServiceContext) http.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
// ownershipTypeOrNone 归属类型归一化(工单号/科技项目号/无)
|
||||
// ownershipTypeOrNone 归属类型归一化(工单号/科技项目号/其他/无)
|
||||
func ownershipTypeOrNone(v string) string {
|
||||
switch v {
|
||||
case "workorder", "project":
|
||||
case "workorder", "project", "other":
|
||||
return v
|
||||
default:
|
||||
return "none"
|
||||
|
||||
@@ -606,7 +606,7 @@ func exportStockHandler(ctx *svc.ServiceContext) http.HandlerFunc {
|
||||
fail(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
headers := []string{"ID", "物料编码", "物料名称", "规格", "类型", "总数量", "锁定量", "可用量", "合格", "未检", "不合格", "分布区域", "批次数", "SN数", "最近入库单", "物料创建时间", "最近入库时间"}
|
||||
headers := []string{"ID", "图号", "物料名称", "规格", "类型", "总数量", "锁定量", "可用量", "合格", "未检", "不合格", "分布区域", "批次数", "SN数", "最近入库单", "物料创建时间", "最近入库时间"}
|
||||
matrix := make([][]any, 0, len(rows))
|
||||
for _, rw := range rows {
|
||||
id := any("")
|
||||
@@ -645,7 +645,7 @@ func exportStockHandler(ctx *svc.ServiceContext) http.HandlerFunc {
|
||||
fail(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
headers := []string{"ID", "物料编码", "物料名称", "规格", "类型", "区域", "质量状态", "总数量", "锁定量", "可用量", "批次数", "SN数", "最近入库单", "最近入库时间"}
|
||||
headers := []string{"ID", "图号", "物料名称", "规格", "类型", "区域", "质量状态", "总数量", "锁定量", "可用量", "批次数", "SN数", "最近入库单", "最近入库时间"}
|
||||
matrix := make([][]any, 0, len(rows))
|
||||
for _, rw := range rows {
|
||||
matrix = append(matrix, []any{
|
||||
|
||||
@@ -161,7 +161,7 @@ export const helpMaterial = {
|
||||
overview: '选本工位接驳台,填写缺料明细提交,AGV 将物料送达接驳台。',
|
||||
fields: [
|
||||
{ name: '接驳台', source: '产线点位配置。本工位的 AGV 接驳点位。', purpose: '确定物料送达位置', fill: '下拉选本工位接驳台' },
|
||||
{ name: '叫料明细', source: '手动录入 / 按缺料一键填充。', purpose: '列明需补的物料编码、名称、单位、数量', fill: '点「按缺料填充」自动生成,或「添加一行」手填' },
|
||||
{ name: '叫料明细', source: '手动录入 / 按缺料一键填充。', purpose: '列明需补的图号、名称、单位、数量', fill: '点「按缺料填充」自动生成,或「添加一行」手填' },
|
||||
{ name: '提交叫料', source: '按钮操作。数据上报到 MES/库管。', purpose: '发起补料请求,通知 AGV/库管送料', fill: '选接驳台 + 有效明细后点击提交' }
|
||||
]
|
||||
}
|
||||
@@ -221,7 +221,7 @@ export const helpInspect = {
|
||||
fields: [
|
||||
{ name: '应急呼叫', source: '按钮操作。实时通知班长。', purpose: '设备故障/缺料/安全等紧急上报', fill: '填写说明后点「发送应急呼叫」' },
|
||||
{ name: '数量不符上报', source: '按钮操作。通知库管。', purpose: '应发与实收不一致时上报差异', fill: '填应发/实收数量,差异非 0 时可提交' },
|
||||
{ name: '工位退料', source: '按钮操作。待库管收货。', purpose: '多余物料退回库房', fill: '填物料编码/数量后提交,等库管确认收货' }
|
||||
{ name: '工位退料', source: '按钮操作。待库管收货。', purpose: '多余物料退回库房', fill: '填图号/数量后提交,等库管确认收货' }
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -99,7 +99,7 @@
|
||||
<!-- 6. 数量不符上报 -->
|
||||
<el-collapse-item name="qty" title="⑥ 数量不符上报">
|
||||
<el-form label-width="92px" size="large">
|
||||
<el-form-item label="物料编码">
|
||||
<el-form-item label="图号">
|
||||
<el-input v-model="qty.materialCode" />
|
||||
</el-form-item>
|
||||
<el-form-item label="物料名称">
|
||||
@@ -124,7 +124,7 @@
|
||||
<!-- 7. 工位退料 -->
|
||||
<el-collapse-item name="return" title="⑦ 工位退料(回库房)">
|
||||
<el-form label-width="92px" size="large">
|
||||
<el-form-item label="物料编码">
|
||||
<el-form-item label="图号">
|
||||
<el-input v-model="ret.materialCode" />
|
||||
</el-form-item>
|
||||
<el-form-item label="物料名称">
|
||||
|
||||
@@ -61,9 +61,9 @@
|
||||
</div>
|
||||
|
||||
<el-table :data="items" border size="large" class="call-table">
|
||||
<el-table-column label="物料编码" min-width="150">
|
||||
<el-table-column label="图号" min-width="150">
|
||||
<template #default="{ row }">
|
||||
<el-input v-model.trim="row.materialCode" size="large" placeholder="物料编码" />
|
||||
<el-input v-model.trim="row.materialCode" size="large" placeholder="图号" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="物料名称" min-width="150">
|
||||
@@ -186,7 +186,7 @@ async function doCall() {
|
||||
}
|
||||
const list = items.value.filter((i) => i.materialCode && Number(i.qty) > 0)
|
||||
if (!list.length) {
|
||||
ElMessage.warning('请添加有效的叫料明细(物料编码 + 数量)')
|
||||
ElMessage.warning('请添加有效的叫料明细(图号 + 数量)')
|
||||
return
|
||||
}
|
||||
calling.value = true
|
||||
|
||||
@@ -90,11 +90,11 @@
|
||||
| 10 | 退库单 WMS 确认收货闭环 | L490 | 库房确认收货 | ❌ 无退库单确认 | ❌ |
|
||||
| 11 | 工程编号 = 合同→工程编号→产品序号 | 附一#1 | 你已答 合同→工程编号→产品序号 | ❌ 无该字段 | ❌ |
|
||||
| 12 | 自动排产(引导式:平均/固定日产+选日期) | 附一#6 | 你已给引导式方案 | ⚠️ 待核 | ⚠️ |
|
||||
| 13 | 其他入库(螺栓/工装/试验设备,规格必填) | L50-54 | 增加 其他入库 tab | ⚠️ 待核 | ⚠️ |
|
||||
| 13 | 其他入库(螺栓/工装/试验设备,规格必填) | L50-54 | 增加 其他入库 tab | ✅ 已实现 | ✅ 其他入库 tab + 规格强制 + 归属含"其他"(form1/form2/form3 均带归属) |
|
||||
| 14 | 安全库存/缺货预警进 WMS 首页 | L473 | 做,闭环 | ❌ WMS 无预警 | ❌ |
|
||||
| 15 | 盘点=人工按物料矫正数量+货架位置 | 头脑风暴B16 | 人工矫正,非扫码 | ⚠️ 待核 | ⚠️ |
|
||||
| 16 | 半成品/成品入库命名与闭环 | L222 | 半成品/成品入库 | ⚠️ Semi.vue 在,闭环待核 | ⚠️ |
|
||||
| 17 | 图号=物料编码合并 + 检测单号必填 | L16-18 | 合并,检测单号必填 | ⚠️ 待核 | ⚠️ |
|
||||
| 17 | 图号=物料编码合并 + 检测单号必填 | L16-18 | 合并,检测单号必填 | ✅ 已实现 | ✅ 图号全链路:WMS/MES/工位终端 全部 UI 改为"图号"(含用户"全部叫图号"口径),后端导出/打印表头同步改"图号" |
|
||||
| 18 | 产品型号级全景生产流程卡 | 第二批#5 | 你强调 要闭环 | ⚠️ 仅 SN 级 | ⚠️ |
|
||||
|
||||
> 标 ⚠️ 待核的,本轮未逐文件打开验证;其余已用 grep/读码确认。需要我继续深挖的,你在文末"待决策"里点名即可。
|
||||
|
||||
Reference in New Issue
Block a user