diff --git a/AGENTS.md b/AGENTS.md index 5c58c4c..8cd6d74 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -55,7 +55,7 @@ | 登录与会话 | `pages/login/`、`stores/session.js` | 远程鉴权适配和会话清理 | | 工作台、仓库选择与模块入口 | `pages/home/`、`components/PortalHeader.vue`、`stores/session.js`、`pages/module/`、`pages/operation/` | 左上角只提供启用国内仓选择,选择结果持久化;`data/demo-data.js` 的 `visible` 控制展示、`available` 控制可操作;隐藏能力不得删除源码或路由 | | 待处理任务、服务端就绪与领取 | `pages/tasks/tasks.vue` | `api/pda-loading.js` 的 `listLoadingOrders`、`getContainerPickingTask`、`claimContainerPickingTask`;合并任务和缺少柜号/仓库/版本的任务不可进入 | -| 一件代发 | `pages/dropship-tasks/dropship-tasks.vue`、`pages/dropship-palletization/dropship-palletization.vue` | `api/pda-dropship.js`;作为入库作业,测量审核按预计托数预生成 DP 托盘码,PDA 可打开每托一页、含 SKU 明细的 A4 托盘信息单,但不直接驱动打印机;PDA 明确执行逐箱清点并支持先扫箱或先扫托、归托和逐托封托;预计托数与已生成托盘数不一致时失败关闭;公网前缀 `/api/platform/v1/first-leg/pda-dropship/**` | +| 一件代发 | `pages/dropship-tasks/dropship-tasks.vue`、`pages/dropship-palletization/dropship-palletization.vue` | `api/pda-dropship.js`;作为入库作业,测量审核按预计托数预生成 DP 托盘码,PDA 可打开每托一页、含 SKU 明细的 A4 托盘信息单,但不直接驱动打印机;PDA 明确执行逐箱清点并支持先扫箱或先扫托、归托和逐托封托;同一国内作业仓的多票可继续装入同一开放托盘,单票全部归托不等于托盘装满,实物装满后才封托;预计托数与已生成托盘数不一致时失败关闭;公网前缀 `/api/platform/v1/first-leg/pda-dropship/**` | | 业务编号展示与错误脱敏 | `utils/user-facing.js`、`tests/user-facing-identifiers.test.mjs` | 任务和作业页优先显示真实集装箱号,缺失时显示柜次号,再缺失显示“集装箱号 —”;不得展示 taskId、inboundId、orderId、loadingPlanId、containerBookingId 或长整型内部 ID,错误仍保留 code、requestId、traceId | | 拣货任务与箱码扫码 | `pages/picking/`、`stores/picking.js` | `api/pda-loading.js`、`utils/scan-broadcast.js`、`components/ScanTargetInput.vue`;服务端契约见 `../../baiyue-firstleg/firstleg/src/main/resources/openapi/phase3-pda-convergence-v1.yaml` | | 装柜取证与完成 | `pages/picking/picking.vue` 的 `evidence` 阶段 | 正式边界是 `api/pda-loading.js` 的证据查询、预览、上传、作废和 `completeContainerPickingTask` 全部按当前任务与仓库进入;不得保留按入仓单/客户作用域的旧证据调用。上传队列 `utils/evidence-upload-queue.js`,合同 `tests/pda-loading-api.test.mjs`、`tests/picking-evidence-stage.test.mjs` | diff --git a/pages/dropship-palletization/dropship-palletization.vue b/pages/dropship-palletization/dropship-palletization.vue index 9ddb365..4087725 100644 --- a/pages/dropship-palletization/dropship-palletization.vue +++ b/pages/dropship-palletization/dropship-palletization.vue @@ -48,6 +48,10 @@ 托盘码未准备完成,当前不能开始箱码清点。 + + 本票已全部归托。托盘未装满时先继续装其他票,实物装满后再封托。 + + 2. 托盘码打开 A4 托盘信息单,装完后封托 @@ -75,14 +79,14 @@ export default { components: { PortalHeader, ScanTargetInput }, - data() { return { user: session.currentUser, taskId: '', warehouseId: '', task: null, palletCode: '', selectedPalletId: '', boxCode: '', carton: null, skuCounts: [], activeScan: 'boxCode', loading: false, submitting: false, printing: false, error: '', notice: '' } }, + data() { return { user: session.currentUser, taskId: '', warehouseId: '', task: null, palletCode: '', selectedPalletId: '', selectedPalletNo: '', boxCode: '', carton: null, skuCounts: [], activeScan: 'boxCode', loading: false, submitting: false, printing: false, error: '', notice: '' } }, computed: { statusLabel() { return this.task?.taskStatus === 'COMPLETED' ? '已完成' : (this.task?.taskStatus === 'CLAIMED' ? '作业中' : '待领取') }, palletSetupReady() { return this.task?.palletSetupReady === true }, palletSetupMessage() { return this.task?.palletSetupMessage || `预计 ${this.task?.expectedPalletCount || 0} 托,当前仅生成 ${this.task?.generatedPalletCount || 0} 个托盘码,请联系后台处理后刷新` }, canResolveCarton() { return Boolean(this.palletSetupReady && this.task?.taskStatus === 'CLAIMED' && String(this.boxCode || '').trim() && !this.loading && !this.submitting) }, canCount() { return Boolean(this.carton?.canCount && this.skuCounts.length && this.skuCounts.every((line) => Number.isInteger(Number(line.actualQuantity)) && Number(line.actualQuantity) >= 0 && (!this.lineDiffers(line) || String(line.discrepancyRemark || '').trim()))) }, - currentPallet() { return (this.task?.pallets || []).find((item) => String(item.palletId) === String(this.selectedPalletId) && item.status === 'OPEN') || null } + currentPallet() { return (this.task?.pallets || []).find((item) => item.palletNo === this.selectedPalletNo && item.status === 'OPEN') || (this.selectedPalletNo ? { palletNo: this.selectedPalletNo, status: 'OPEN' } : null) } }, onLoad(options) { this.taskId = String(options.taskId || ''); this.warehouseId = String(options.warehouseId || '') }, onShow() { if (!requireSession()) return; this.user = session.currentUser; this.startScanner(); this.loadTask() }, @@ -118,13 +122,13 @@ async selectPallet(value) { if (!this.palletSetupReady) { this.error = this.palletSetupMessage; return } const code = String(value || this.palletCode || '').trim().toUpperCase(); const pallet = (this.task?.pallets || []).find((item) => item.palletNo === code) - if (!pallet) { this.error = '托盘码不属于当前一件代发任务'; return } - if (pallet.status !== 'OPEN') { this.error = '托盘已封托,不能继续归托'; return } - this.palletCode = code; this.selectedPalletId = String(pallet.palletId); this.activeScan = 'boxCode' + if (pallet && pallet.status !== 'OPEN') { this.error = '托盘已封托,不能继续归托'; return } + this.palletCode = code; this.selectedPalletId = pallet ? String(pallet.palletId) : ''; this.selectedPalletNo = code; this.activeScan = 'boxCode' if (Number(this.task?.unboundCartons || 0) > 0) await this.runMutation(() => bindDropshipPendingCartons(this.taskId, this.warehouseId, code), `待归托箱已绑定至 ${code}`) - else this.notice = `当前托盘:${code}` + else this.notice = pallet ? `当前托盘:${code}` : `已选择仓库开放托盘 ${code},清点箱码时将校验并归托` }, - async sealPallet(pallet) { const result = await this.runMutation(() => sealDropshipPallet(this.taskId, this.warehouseId, pallet), `${pallet.palletNo} 已封托`); if (result && String(this.selectedPalletId) === String(pallet.palletId)) { this.selectedPalletId = ''; this.palletCode = '' } }, + continueOtherTask() { uni.redirectTo({ url: `/pages/dropship-tasks/dropship-tasks?warehouseId=${encodeURIComponent(this.warehouseId)}` }) }, + async sealPallet(pallet) { const result = await this.runMutation(() => sealDropshipPallet(this.taskId, this.warehouseId, pallet), `${pallet.palletNo} 已封托`); if (result && this.selectedPalletNo === pallet.palletNo) { this.selectedPalletId = ''; this.selectedPalletNo = ''; this.palletCode = '' } }, async printPallet(pallet) { if (this.printing) return this.printing = true; this.resetMessages() @@ -163,6 +167,7 @@ .state, .message { margin-bottom: 10px; padding: 12px; border-radius: 9px; color: #667085; font-size: 12px; line-height: 18px; text-align: center; background: #fff; } .message.error { color: #b42318; background: #fff1f0; }.message.success { color: #067647; background: #ecfdf3; } .task-summary-card, .section-card { margin: 0; padding: 11px 12px; border: 0; border-bottom: 6px solid #eef2f7; border-radius: 0; background: #fff; box-shadow: none; } + .continue-strip { padding: 10px 12px 12px; border-bottom: 6px solid #eef2f7; color: #475467; font-size: 13px; line-height: 19px; background: #f8fbff; }.continue-strip .secondary-button { margin-top: 7px; } .task-summary-card { display: block; } .summary-head, .section-heading, .pallet-head, .carton-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; } .inbound-code { min-width: 0; color: #12233f; font-size: 17px; font-weight: 700; overflow-wrap: anywhere; } diff --git a/tests/dropship-palletization.test.mjs b/tests/dropship-palletization.test.mjs index 902a275..e4d4804 100644 --- a/tests/dropship-palletization.test.mjs +++ b/tests/dropship-palletization.test.mjs @@ -64,6 +64,9 @@ test('palletization UI accepts carton and pre-generated pallet scans in either o assert.match(work, /lineDiffers[\s\S]*?ACCEPT_VARIANCE/) assert.match(work, /bindDropshipPendingCartons/) assert.match(work, /palletNo: this\.currentPallet/) + assert.match(work, /继续装其他入仓单/) + assert.match(work, /selectedPalletNo/) + assert.match(work, /已选择仓库开放托盘/) assert.match(work, /downloadDropshipPalletLabel/) assert.match(work, /formatApiError/) assert.doesNotMatch(work, /Number\(this\.warehouseId\)/)