From cb1dfc5a310aa94a70173ccedbdbdd00f1ff63a7 Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Wed, 9 Sep 2026 10:18:10 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 2 +- src/api/receiving-adapter.js | 6 +++++- src/components/ReceivingSessionSummary.vue | 12 ++++++++++++ src/pages/operation/operation.vue | 3 +++ tests/multi-plan-receiving.test.mjs | 11 ++++++++++- 5 files changed, 31 insertions(+), 3 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 163d232..97e5561 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -40,7 +40,7 @@ | 登录、刷新与会话 | `src/pages/login/login.vue`、`src/api/auth.js`、`src/stores/session.js` | 国内 Auth 签发 Token;API 根见 `src/config/runtime.js` | | 海外仓工作台与模块入口 | `src/pages/home/home.vue`、`src/pages/module/module.vue` | `src/data/overseas-workbench.js` 是模块与作业可用性入口;首页通过 `/api/pda/v1/last-leg/receiving-containers/warehouse-profile` 读取 Token 绑定仓的中文主档名称 | | 中英西语言设置 | `src/i18n/index.js`、`src/i18n/locales/`、`src/pages/profile/profile.vue` | 个人中心可选择并持久化 `zh-CN`、`en`、`es`;当前业务翻译资源仍为空,页面业务文案继续使用中文,不得把语言选择入口误报为全量翻译完成 | -| 柜号签收、照片与入仓真实作业 | `src/pages/operation/operation.vue`、`src/api/inbound.js`、`src/api/receiving-adapter.js`、`src/api/overseas.js`、`src/stores/receiving.js` | 三个固定照片角色各一张,支持拍照/相册、鉴权预览及更换失败保留原图;Backend `lastleg-service/inboundplan/pda/controller/ReceivingContainerController.java`、`ReceivingContainerService.java`,公网前缀 `/api/pda/v1/last-leg/receiving-containers/**` | +| 柜号签收、照片与入仓真实作业 | `src/pages/operation/operation.vue`、`src/components/ReceivingSessionSummary.vue`、`src/api/inbound.js`、`src/api/receiving-adapter.js`、`src/api/overseas.js`、`src/stores/receiving.js` | 三个固定照片角色各一张,支持拍照/相册、鉴权预览及更换失败保留原图;多计划同柜直接展示服务端 `assigned/mergeAllowed/urgent/mergeHint`,合托明确为现场可选而非完成门禁;扫码实收确认计划生成阶段已预留的真实托盘和库位,不在设备端重新分配。Backend `lastleg-service/inboundplan/pda/controller/ReceivingContainerController.java`、`ReceivingContainerService.java`,公网前缀 `/api/pda/v1/last-leg/receiving-containers/**` | | 托盘管理真实作业 | `src/pages/pallet/pallet.vue`、`src/api/pallet.js` | 扫码查询托盘内容,按服务端返回的托盘/库存版本执行组托、拆托、换托和解绑;公网前缀 `/api/pda/v1/last-leg/pallet-operations/**` | | 库存盘点真实作业 | `src/pages/stocktake/stocktake.vue`、`src/api/warehouse-operations.js`、`src/stores/warehouse-work.js` | 创建/恢复盘点任务,扫描库位和库存后计数、提交;严格使用服务端版本和幂等键 | | 商品移库真实作业 | `src/pages/move/move.vue`、`src/api/warehouse-operations.js`、`src/stores/warehouse-work.js` | 扫描来源托盘与目标库位执行同仓移库;冲突后重新读取服务端事实 | diff --git a/src/api/receiving-adapter.js b/src/api/receiving-adapter.js index b568bb2..9b73425 100644 --- a/src/api/receiving-adapter.js +++ b/src/api/receiving-adapter.js @@ -42,7 +42,11 @@ export function normalizeReceivingInbound(value = {}) { plannedPalletCount: number(value.plannedPalletCount), totalPackages: number(value.totalPackages), receivedPackages: number(value.receivedPackages), - pendingPackages: number(value.pendingPackages) + pendingPackages: number(value.pendingPackages), + assigned: value.assigned === true, + mergeAllowed: value.mergeAllowed === true, + urgent: value.urgent === true, + mergeHint: text(value.mergeHint) } } diff --git a/src/components/ReceivingSessionSummary.vue b/src/components/ReceivingSessionSummary.vue index 0a96e73..9e80c34 100644 --- a/src/components/ReceivingSessionSummary.vue +++ b/src/components/ReceivingSessionSummary.vue @@ -14,6 +14,16 @@ {{ totalPackages }}计划箱数 {{ pendingPackages }}待收箱数 + + + {{ item.inboundCode || '—' }} + + {{ item.assigned ? '已分配' : '未分配' }} + {{ item.mergeAllowed ? '可合托·非强制' : '不合托' }} + {{ item.urgent ? '加急' : '普通' }} + + + 收货进度{{ receivedPackages }} / {{ totalPackages }} 箱 @@ -22,6 +32,7 @@ export default { props: { session: { type: Object, default: null } }, computed: { + planFlags() { return Array.isArray(this.session?.inbounds) ? this.session.inbounds : [] }, receivingWarehouseLabel() { const code = String(this.session?.overseasWarehouseCode || '').trim(); const name = String(this.session?.overseasWarehouseName || '').trim(); return code && name ? `${code} · ${name}` : '—' }, inboundCodeCount() { return Array.isArray(this.session?.inbounds) ? this.session.inbounds.length : 0 }, plannedPalletCount() { return (this.session?.inbounds || []).reduce((sum, item) => sum + Math.max(0, Number(item?.plannedPalletCount || 0)), 0) }, @@ -42,6 +53,7 @@ .receiving-summary-status{flex-shrink:0;padding:4px 8px;border-radius:99px;color:#1668dc;font-size:9px;font-weight:700;background:#e3efff} .receiving-summary-context{display:flex;flex-direction:column;gap:4px;margin-top:7px;color:#617389;font-size:9px;line-height:14px}.receiving-summary-warehouse{display:block}.receiving-summary-destinations{display:flex;min-width:0;align-items:flex-start}.destination-label{flex-shrink:0}.destination-tags{display:flex;min-width:0;flex-wrap:wrap;gap:3px 4px}.destination-tags text{padding:0 5px;border-radius:99px;color:#42617f;line-height:14px;background:#e8f0fa;white-space:nowrap} .receiving-summary-metrics{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));margin-top:7px;padding:7px 2px;border-radius:9px;background:rgba(255,255,255,.8)}.receiving-summary-metrics>view{display:flex;align-items:center;flex-direction:column;border-right:1px solid #e3eaf2}.receiving-summary-metrics>view:last-child{border-right:0} + .receiving-plan-flags{margin-top:7px;padding:3px 8px;border-radius:9px;background:rgba(255,255,255,.82)}.receiving-plan-row{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:5px 0;border-bottom:1px solid #e9eef5}.receiving-plan-row:last-child{border-bottom:0}.receiving-plan-code{min-width:0;color:#17324d;font-size:10px;font-weight:750;overflow-wrap:anywhere}.receiving-plan-tags{display:flex;flex-shrink:0;gap:3px}.receiving-plan-tags text{padding:2px 5px;border-radius:99px;font-size:8px;line-height:12px}.flag-positive{color:#08705f;background:#e2f6f0}.flag-option{color:#1668dc;background:#e7f0ff}.flag-urgent{color:#b54708;background:#fff0dc}.flag-muted{color:#718095;background:#edf1f5} .metric-value{color:#17324d;font-size:17px;font-weight:850;line-height:21px}.metric-pending{color:#b66a08}.metric-label{margin-top:1px;color:#718095;font-size:8px;line-height:12px;text-align:center} .receiving-summary-progress{margin-top:7px;color:#607287;font-size:9px}.progress-track{height:5px;margin-top:5px;overflow:hidden;border-radius:99px;background:#dfe8f2}.progress-fill{height:100%;border-radius:inherit;background:#1677e8} diff --git a/src/pages/operation/operation.vue b/src/pages/operation/operation.vue index d9764e4..b7f305a 100644 --- a/src/pages/operation/operation.vue +++ b/src/pages/operation/operation.vue @@ -74,6 +74,8 @@ {{ activeInbound.destinationType === 'FBA' ? 'FBA 按箱数收货' : '海外仓逐箱收货' }}{{ destinationLabel(activeInbound) }} {{ activeInbound.inboundCode }} + {{ activeInbound.assigned ? '已分配' : '未分配' }}{{ activeInbound.mergeAllowed ? '可合托(现场自选)' : '不合托' }}{{ activeInbound.urgent ? '加急' : '普通' }} + {{ activeInbound.mergeHint }} 总计 {{ activeInbound.totalPackages }} 箱已收 {{ activeInbound.receivedPackages }} 箱待收 {{ activeInbound.pendingPackages }} 箱 2. 扫描实物托盘码 @@ -199,6 +201,7 @@ .matched-inbound-card{margin:14px 0;padding:12px;border:1px solid #cfe0f2;border-radius:10px;background:#f8fbff} .matched-inbound-card .mode-banner{margin-bottom:10px} .matched-inbound-code{display:block;color:#17324d;font-size:15px;font-weight:800} + .matched-plan-flags{display:flex;flex-wrap:wrap;gap:5px;margin-top:7px}.matched-plan-flags text{padding:3px 7px;border-radius:99px;color:#617389;font-size:9px;background:#edf1f5}.matched-plan-flags .optional{color:#1668dc;background:#e7f0ff}.matched-plan-flags .urgent{color:#b54708;background:#fff0dc}.merge-option-hint{display:block;margin-top:7px;padding:7px 8px;border-radius:7px;color:#1668dc;font-size:10px;line-height:15px;background:#eef5ff} .matched-inbound-progress{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:6px;margin-top:8px;color:#5f7185;font-size:10px} .matched-inbound-progress text{padding:6px 4px;border-radius:6px;text-align:center;background:#edf3f9} .error-card text{overflow-wrap:anywhere;word-break:break-word} diff --git a/tests/multi-plan-receiving.test.mjs b/tests/multi-plan-receiving.test.mjs index 6fa82f5..313eb3d 100644 --- a/tests/multi-plan-receiving.test.mjs +++ b/tests/multi-plan-receiving.test.mjs @@ -15,7 +15,11 @@ const inbounds = Array.from({ length: 15 }, (_, index) => ({ plannedPalletCount: 1, totalPackages: 4, receivedPackages: index === 0 ? 4 : 0, - pendingPackages: index === 0 ? 0 : 4 + pendingPackages: index === 0 ? 0 : 4, + assigned: index % 3 === 0, + mergeAllowed: index % 4 === 0, + urgent: index === 0, + mergeHint: index % 4 === 0 ? '可选择合托,不强制' : '' })) test('one container candidate aggregates fifteen independent one-inbound plans', () => { @@ -85,6 +89,9 @@ test('current recovery retains all independent-plan inbound facts while progress assert.deepEqual(receivingProgress(), { total: 60, received: 4, pending: 56, percent: 7 }) assert.equal(receiving.current.inbounds.length, 15) assert.equal(receiving.current.inbounds[0].inboundCode, 'NYEXX2608270001') + assert.equal(receiving.current.inbounds[0].assigned, true) + assert.equal(receiving.current.inbounds[0].mergeAllowed, true) + assert.equal(receiving.current.inbounds[0].urgent, true) assert.equal(receiving.current.inbounds[14].inboundCode, 'NYEXX2608270015') resetReceivingSession() }) @@ -98,8 +105,10 @@ test('container UI locks and signs once across independent plans, then renders o assert.match(operation, /item\.totalOrderCount[\s\S]*item\.inboundCodeCount[\s\S]*item\.planCount[\s\S]*item\.pendingPlanCount/) assert.match(summary, /inboundCodeCount[\s\S]*plannedPalletCount[\s\S]*totalPackages/) + for (const fact of ['已分配', '未分配', '可合托·非强制', '不合托', '加急', '普通']) assert.match(summary, new RegExp(fact)) assert.doesNotMatch(operation, /v-for="[^"]*inbound[^"]*"/) assert.match(operation, /v-if="activeInbound"[\s\S]*activeInbound\.inboundCode[\s\S]*activeInbound\.pendingPackages/) + assert.match(operation, /activeInbound\.mergeAllowed[\s\S]*现场自选[\s\S]*activeInbound\.urgent/) assert.match(operation, /resolveCurrentCarton\(cartonMark\)/) assert.doesNotMatch(operation, /cartonMark\.split|\.split\([^\n]*['"]#['"]/)