From 0b982d192c4451fbfdb42305a34c1e8b5753e92e Mon Sep 17 00:00:00 2001 From: duxingchen Date: Wed, 23 Sep 2026 15:18:06 +0800 Subject: [PATCH] =?UTF-8?q?feat(mobile):=20=E7=A7=BB=E5=8A=A8=E7=AB=AF?= =?UTF-8?q?=E8=A1=A5=E9=BD=90=E5=87=BA=E5=BA=93=E5=8D=95=E6=8D=AE=E9=A1=B5?= =?UTF-8?q?=EF=BC=88=E9=A2=86=E6=96=99=20/=20=E6=8A=A5=E5=BA=9F=20/=20?= =?UTF-8?q?=E5=88=A0=E9=99=A4=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PC 端早就能挂出库物料,但移动端一直没有入口 —— 只能看,一线的人(生产领料、 测试补料)反而够不着。 - 产品详情页的产品信息卡底部加「出库单据 N 张单 / M 条料」入口,常显不隐藏: 以前没内容时整块消失,用户根本不知道有这功能。 - 新增「出库单据」页与「选择 MOM 出库物料」页,与 PC 端同一套数据源、 同一套接口、同一形态(按出库单号分组 + 点开展开明细)。 - 挂载不需要先选任务:任务只是溯源(记 added_by),展示/报废/删除一律按设备走。 - 代挂确认:勾了不是自己领的单时先拦一道。★ 这是**提示**不是权限 —— 料的归属是设备不是人,代挂是合理操作(测试替生产补挂、库管代录)。 ⚠️ 判据是 MOM 的 consumer_name 与登录人姓名比对,比错也只是多让用户勾一下。 - navigateTo / navigateBack 失败在 uni 里是**静默**的(只留一行 warning), 用户看到的就是「点了没反应」。全部补 fail 回调弹窗。 --- track-uniapp/src/api/material.js | 74 ++++ track-uniapp/src/api/scrap.js | 48 ++ track-uniapp/src/pages.json | 16 + track-uniapp/src/pages/material/index.vue | 505 ++++++++++++++++++++++ track-uniapp/src/pages/material/pick.vue | 354 +++++++++++++++ track-uniapp/src/pages/scan/detail.vue | 172 +++++++- track-uniapp/src/utils/request.js | 3 + 7 files changed, 1168 insertions(+), 4 deletions(-) create mode 100644 track-uniapp/src/api/material.js create mode 100644 track-uniapp/src/api/scrap.js create mode 100644 track-uniapp/src/pages/material/index.vue create mode 100644 track-uniapp/src/pages/material/pick.vue diff --git a/track-uniapp/src/api/material.js b/track-uniapp/src/api/material.js new file mode 100644 index 0000000..c32c5bf --- /dev/null +++ b/track-uniapp/src/api/material.js @@ -0,0 +1,74 @@ +/** + * 领用物料 API —— 「产品详情 → 领用物料 → + 领料」 + * + * 背景:PC 端早就能挂出库物料(`MomOutboundPicker`),但**移动端一直没有入口** —— + * 只能在别处挂好、这边看。一线的人(生产领料、测试补料)反而够不着, + * 所以补上这条链。 + * + * ⚠️ 挂载粒度是**任务**,不是产品: + * `product_outbound_materials` 才带 `mom_line_id`(MOM trans_outbound.id), + * 而产品级那张 `product_outbounds` 是单据级、没有行 id。 + * 报废必须靠 mom_line_id 定位到具体哪条出库明细,所以这里的入口一律挂任务。 + * 这也贴合业务:生产领生产任务的料,测试领测试任务的料,各挂各的。 + */ +import { get, post, del } from "../utils/request"; + +/** + * 搜索 MOM 出库单(按单据分页,带回每张单的明细)。 + * + * ⚠️ 可见范围(公司隔离 + 跨部门例外)由后端钉死,这里传什么都放不大 —— + * 界面筛选只能收窄。 + */ +export function searchMomOutbounds(params = {}) { + return get("/mom-outbounds", params); +} + +/** 本部门出库单里出现过的领用人姓名(后端已按可见范围过滤) */ +export function listMomOutboundConsumers() { + return get("/mom-outbounds/consumers"); +} + +/** + * 把选中的 MOM 出库**明细行**挂到设备上。 + * + * @param {string} productId + * @param {number[]} momLineIds - MOM trans_outbound.id 列表 + * (勾的是一整张出库单,提交时展开成该单的全部明细行 id) + * @param {string} [taskId] - 可选,仅作溯源 + * ⚠️ 只传 id,物料名/数量由后端现查 MOM —— 前端传快照会被后端拒绝。 + * 幂等:已挂过的明细会被后端跳过。返回该设备当前**全部**出库明细。 + */ +export function mountProductOutboundMaterials(productId, momLineIds, taskId) { + return post(`/products/${productId}/outbound-materials`, { + mom_line_ids: momLineIds, + // 任务只是**溯源信息**(这条料挂在哪条任务上),可以不给 —— + // 展示、报废、删除一律按设备走 + task_id: taskId || null, + }); +} + +/** + * 从设备上摘掉一条出库明细(挂错了要能撤)。 + * + * @param {string} productId + * @param {number} materialId - **Track 侧那条记录的 id**(不是 mom_line_id) + * ⚠️ 别传错:`id` 是本表主键、只在 Track 库里有;`mom_line_id` 是 MOM + * 那边的出库明细行 id。传反了会删掉另一条料。 + * + * 只摘掉 Track 这边的挂载关系,**不动 MOM 里的出库单本身**。 + * 已提交的报废记录也不受影响(它的物料信息是快照,独立存在)。 + * ⚠️ MOM 回调自动存档的行删不掉(后端 409),那是系统事实。 + */ +export function removeProductOutboundMaterial(productId, materialId) { + return del(`/products/${productId}/outbound-materials/${materialId}`); +} + +/** + * 整张出库单一起摘掉(挂错了要能一次撤)。 + * + * 界面上更容易碰到的是「这一单整个挂错了」,逐条删要删好几下。 + * 规则与逐条删**一致**:含 MOM 回调自动存档记录的单整单删不掉(后端 409)。 + */ +export function removeProductOutboundOrder(productId, outboundNo) { + return del(`/products/${productId}/outbound-materials/by-order/${encodeURIComponent(outboundNo)}`); +} diff --git a/track-uniapp/src/api/scrap.js b/track-uniapp/src/api/scrap.js new file mode 100644 index 0000000..d13d81f --- /dev/null +++ b/track-uniapp/src/api/scrap.js @@ -0,0 +1,48 @@ +/** + * 生产报废 API —— 「产品详情 → 领用物料 → 报废」 + * + * 链路:本文件 → Track 后端 /products/{id}/scraps + * → MOM 内部接口(退回不良品 + 建报废申请) + * → MOM 里主管审批 → 库管扫码执行 → 才算得出损失金额 + * + * ⚠️ 金额与状态是**后端实时回查 MOM** 的,前端不要缓存、不要自己算: + * `mom_executed=false` 时 `total_loss` 是 null(还没执行), + * 而不是 0 —— 显示成「损失 0 元」会让用户以为东西没价值。 + */ +import { get, post } from "../utils/request"; + +/** 列出该产品的生产报废记录(按提交时间倒序,含实时 MOM 状态与金额) */ +export function listProductScraps(productId) { + return get(`/products/${productId}/scraps`); +} + +/** + * 提交一条生产报废。 + * + * @param {string} productId + * @param {object} payload + * @param {number} payload.mom_line_id - 报废对象:MOM 出库明细行 id + * (就是任务挂载的 outbound_materials[].mom_line_id) + * @param {number} payload.quantity - 报废数量 + * @param {string} payload.track_ref - 幂等锚点,**弹层打开时生成一次**, + * 重试复用同一个;换了它就会在 MOM 里多出一张报废单 + * @param {string} [payload.reason] - 原因说明 + */ +export function submitProductScrap(productId, payload) { + return post(`/products/${productId}/scraps`, payload); +} + +/** + * 生成幂等锚点。 + * + * ⚠️ 必须在**打开弹层时**生成一次并保存在弹层状态里,提交失败重试要复用同一个 —— + * 每次提交都新生成的话,用户重试就会在 MOM 里多报一张单(重复报废)。 + * 用时间戳 + 随机串,在单机范围内足够唯一,且人能看懂大概是什么时候报的。 + */ +export function makeTrackRef() { + const d = new Date(); + const p = (n) => String(n).padStart(2, "0"); + const ts = `${d.getFullYear()}${p(d.getMonth() + 1)}${p(d.getDate())}${p(d.getHours())}${p(d.getMinutes())}${p(d.getSeconds())}`; + const rand = Math.random().toString(36).slice(2, 8); + return `SCRAP-${ts}-${rand}`; +} diff --git a/track-uniapp/src/pages.json b/track-uniapp/src/pages.json index c42b0f1..3e8766c 100644 --- a/track-uniapp/src/pages.json +++ b/track-uniapp/src/pages.json @@ -31,6 +31,22 @@ "navigationBarTextStyle": "white" } }, + { + "path": "pages/material/index", + "style": { + "navigationBarTitleText": "领用物料", + "navigationBarBackgroundColor": "#2563EB", + "navigationBarTextStyle": "white" + } + }, + { + "path": "pages/material/pick", + "style": { + "navigationBarTitleText": "选择 MOM 出库物料", + "navigationBarBackgroundColor": "#2563EB", + "navigationBarTextStyle": "white" + } + }, { "path": "pages/tasks/index", "style": { diff --git a/track-uniapp/src/pages/material/index.vue b/track-uniapp/src/pages/material/index.vue new file mode 100644 index 0000000..42363d5 --- /dev/null +++ b/track-uniapp/src/pages/material/index.vue @@ -0,0 +1,505 @@ +