From f39303ea24fa75b7cbd0c0de664ee37f8ef6e644 Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Fri, 23 Oct 2026 18:45:48 +0800 Subject: [PATCH] fix(pda): drop stale sealed pallets from task workspace --- .../dropship-palletization.vue | 16 ++++++++++------ tests/dropship-workspace.test.mjs | 16 +++++++++++++++- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/pages/dropship-palletization/dropship-palletization.vue b/pages/dropship-palletization/dropship-palletization.vue index b64044a..7df77a2 100644 --- a/pages/dropship-palletization/dropship-palletization.vue +++ b/pages/dropship-palletization/dropship-palletization.vue @@ -88,7 +88,7 @@ import ScanTargetInput from '@/components/ScanTargetInput.vue' import { bindDropshipPendingCartons, claimDropshipPalletizationTask, countDropshipCarton, getDropshipPalletizationTask, moveDropshipCarton, resolveDropshipWorkspaceCarton, resolveDropshipWorkspacePallet, sealDropshipPallet, unsealDropshipPallet } from '@/api/pda-dropship.js' import { session, requireSession } from '@/stores/session.js' - import { formatApiError } from '@/utils/user-facing.js' + import { sanitizeUserMessage } from '@/utils/user-facing.js' import { stableOperationId } from '@/utils/idempotency.js' import { isOnline } from '@/utils/network.js' import { clearDropshipPalletizingDraft, restoreDropshipPalletizingDraft, saveDropshipPalletizingDraft } from '@/stores/dropship-palletizing.js' @@ -119,6 +119,10 @@ onHide() { this.stopScanner() }, onUnload() { this.stopScanner() }, beforeUnmount() { this.stopScanner() }, onBackPress() { if (!this.unsealOpen) return false; this.closeUnseal(); return true }, methods: { + formatOperationError(error, fallback) { + console.error('[PDA dropship inbound]', { code: error?.code, requestId: error?.requestId, traceId: error?.traceId }) + return sanitizeUserMessage(error?.message, fallback) + }, startScanner() { this.stopScanner(); this._stopScanner = startScanBroadcast({ onScan: (value) => this.handleScan(value), onError: () => { this.error = '扫码头连接失败,可使用输入框继续作业' } }) }, stopScanner() { if (this._stopScanner) this._stopScanner(); this._stopScanner = null }, handleScan(value) { if (this.loading || this.submitting) return; if (!this.palletSetupReady) { this.error = this.palletSetupMessage; return } const plan = planDropshipScan(createDropshipScanState({ online: isOnline() }), value, { unboundCartons: this.task?.unboundCartons }); if ([DROPSHIP_SCAN_INTENT.BLOCKED, DROPSHIP_SCAN_INTENT.INVALID].includes(plan.intent)) { this.error = plan.message; return } const code = plan.palletNo || plan.boxCode; if (plan.palletNo) { this.palletCode = code; this.selectPallet(code) } else { this.boxCode = code; this.resolveCarton() } }, @@ -127,7 +131,7 @@ if (!this.taskId || !this.warehouseId) { this.error = '缺少合托任务或仓库信息,请返回任务列表'; return } this.loading = true; this.resetMessages() try { this.task = await getDropshipPalletizationTask(this.taskId, this.warehouseId); await this.refreshPallet({ restorePallet }); if (this.task.taskStatus === 'COMPLETED') clearDropshipPalletizingDraft(this.draftIdentity()) } - catch (error) { this.error = formatApiError(error, '一件代发入库任务加载失败') } + catch (error) { this.error = this.formatOperationError(error, '一件代发入库任务加载失败') } finally { this.loading = false } }, async claimTask() { await this.runMutation(() => claimDropshipPalletizationTask(this.taskId, this.warehouseId), '任务领取成功') }, @@ -137,7 +141,7 @@ const code = String(value || this.boxCode || '').trim().toUpperCase(); if (!code || this.loading) return this.boxCode = code; this.loading = true; this.resetMessages(); this.carton = null try { const resolved = await resolveDropshipWorkspaceCarton(this.warehouseId, code); this.scanTask = resolved.task; this.carton = resolved.carton; this.skuCounts = (this.carton.skuLines || []).map((line) => ({ ...line, actualQuantity: String(line.actualQuantity ?? line.expectedQuantity ?? ''), discrepancyRemark: line.discrepancyRemark || '' })) } - catch (error) { this.error = formatApiError(error, '箱码解析失败') } + catch (error) { this.error = this.formatOperationError(error, '箱码解析失败') } finally { this.loading = false } }, async countCarton() { @@ -167,7 +171,7 @@ this.palletCode = code this.loading = true; this.resetMessages(); this.workspace = null try { this.workspace = await resolveDropshipWorkspacePallet(this.warehouseId, code); this.selectedPalletNo = code; this.palletCode = code; this.selectedPalletId = String(this.workspace.pallet.palletId); this.activeScan = 'boxCode'; saveDropshipPalletizingDraft(this.draftIdentity(), { activePalletNo: code }) } - catch (error) { this.selectedPalletNo = ''; this.error = formatApiError(error, '托盘校验失败'); return } + catch (error) { this.selectedPalletNo = ''; this.error = this.formatOperationError(error, '托盘校验失败'); return } finally { this.loading = false } const pallet = this.currentPallet if (pallet?.status === 'SEALED') { this.notice = `托盘 ${code} 已封托,可在托盘操作区解除封托`; return } @@ -235,10 +239,10 @@ ? result : await getDropshipPalletizationTask(this.taskId, this.warehouseId) this.notice = message - try { await this.refreshPallet() } catch (error) { this.error = formatApiError(error, '操作已成功,托盘汇总刷新失败,请刷新后继续') } + try { await this.refreshPallet() } catch (error) { this.error = this.formatOperationError(error, '操作已成功,托盘汇总刷新失败,请刷新后继续') } return result } - catch (error) { this.error = formatApiError(error, '操作失败'); return null } + catch (error) { this.error = this.formatOperationError(error, '操作失败'); return null } finally { this.submitting = false } } } diff --git a/tests/dropship-workspace.test.mjs b/tests/dropship-workspace.test.mjs index 723ed11..d3fb563 100644 --- a/tests/dropship-workspace.test.mjs +++ b/tests/dropship-workspace.test.mjs @@ -2,6 +2,7 @@ import test from 'node:test' import assert from 'node:assert/strict' import { readFile } from 'node:fs/promises' import vm from 'node:vm' +import { sanitizeUserMessage } from '../utils/user-facing.js' const source = await readFile(new URL('../pages/dropship-palletization/dropship-palletization.vue', import.meta.url), 'utf8') const script = source.match(/