From c8bb1eecdf784ea735747f180ed117ac7bdce49c Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Mon, 17 Aug 2026 09:41:16 +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 --- api/pda-loading.js | 13 +++++++++++++ pages/picking/picking.vue | 27 ++++++++++++++++++++------- tests/pda-loading-api.test.mjs | 3 +++ tests/picking-evidence-stage.test.mjs | 5 ++++- 4 files changed, 40 insertions(+), 8 deletions(-) diff --git a/api/pda-loading.js b/api/pda-loading.js index 264a626..7b43eed 100644 --- a/api/pda-loading.js +++ b/api/pda-loading.js @@ -358,4 +358,17 @@ export function completeLoading(inboundId, payload, tenantId) { }) } +/** Complete the cabinet PDA task after the operator has captured all evidence. */ +export function completeContainerPickingTask(taskId, payload = {}) { + if (!taskId) throw new Error('缺少拣货任务标识,无法确认装柜完成') + if (payload.expectedTaskVersion == null) throw new Error('缺少拣货任务版本,无法确认装柜完成') + if (!payload.operationId) throw new Error('缺少操作标识,无法确认装柜完成') + return request({ + url: `/first-leg/pda-loading/tasks/${encodeURIComponent(taskId)}/complete`, + method: 'POST', + header: { 'Idempotency-Key': stableIdempotencyKey(payload.operationId) }, + data: payload + }) +} + export { EVIDENCE_SLOTS, EVIDENCE_TYPES } diff --git a/pages/picking/picking.vue b/pages/picking/picking.vue index 63c45c4..736f8ad 100644 --- a/pages/picking/picking.vue +++ b/pages/picking/picking.vue @@ -150,7 +150,7 @@ 当前可先拍照留存;拣货完成并同步装柜状态后,照片会自动上传绑定。 {{ remoteError }} - + 状态尚未同步,请刷新重试 @@ -344,7 +344,7 @@ import { navigateBackOr } from '@/utils/navigation.js' import { startScanBroadcast } from '@/utils/scan-broadcast.js' import { playErrorSound, playSuccessSound } from '@/utils/audio-feedback.js' - import { claimContainerPickingTask, completeLoading, completePicking, downloadEvidencePreview, EVIDENCE_SLOTS, getContainerPickingTask, getEvidence, getEvidencePreviewUrl, getPickingInventorySnapshot, getPickingTask, isRemoteReady, normalizePickingTask, submitContainerPickingBatch, uploadEvidenceFile, upsertEvidence, voidEvidence } from '@/api/pda-loading.js' + import { claimContainerPickingTask, completeContainerPickingTask, completePicking, downloadEvidencePreview, EVIDENCE_SLOTS, getContainerPickingTask, getEvidence, getEvidencePreviewUrl, getPickingInventorySnapshot, getPickingTask, isRemoteReady, normalizePickingTask, submitContainerPickingBatch, uploadEvidenceFile, upsertEvidence, voidEvidence } from '@/api/pda-loading.js' import { createEvidenceUploadQueue } from '@/utils/evidence-upload-queue.js' import { isOnline } from '@/utils/network.js' import { stableOperationId } from '@/utils/idempotency.js' @@ -1140,17 +1140,30 @@ async completeLoadingTask() { if (!this.requireEvidenceStage()) return if (!this.evidenceIdentity?.inboundId || !isRemoteReady(this.evidenceIdentity.inboundId)) { this.showToast('装柜任务尚未同步,请刷新后重试', 'warning'); return } - if (this.loadingVersion === null) { this.showToast(VERSION_SYNC_MESSAGE, 'warning'); return } + if (this.pickingVersion === null || this.loadingVersion === null) { this.showToast(VERSION_SYNC_MESSAGE, 'warning'); return } if (!this.evidenceComplete) { this.showToast('五类有效照片和封签号均为必填', 'warning'); return } const contextId = this.evidenceContextId const identity = { ...this.evidenceIdentity } - const inboundId = identity.inboundId - const tenantId = this.currentBox.tenantId - const loadingVersion = this.loadingVersion + const expectedTaskVersion = Number(this.pickingVersion) + const operationIdentity = stableTaskOperationIdentity(this.draftIdentity()) + const operationId = stableOperationId('complete', operationIdentity, { + taskId: this.remoteTaskId, + expectedTaskVersion, + loadingPlanId: identity.loadingPlanId, + containerBookingId: identity.containerBookingId + }) this.submitting = true try { - await completeLoading(inboundId, { expectedVersion: loadingVersion, remark: 'PDA确认装柜完成' }, tenantId) + const completedTask = await completeContainerPickingTask(this.remoteTaskId, { + expectedTaskVersion, + operationId, + remark: 'PDA确认装柜完成' + }) if (!this.isEvidenceContextActive(contextId, identity)) return + if (completedTask) { + this.remotePick = normalizePickingTask({ ...this.remotePick, ...completedTask }) + this.pickingVersion = this.remotePick.pickingVersion ?? this.remotePick.version ?? this.pickingVersion + } this.showToast('已确认装柜完成', 'success') } catch (error) { if (!this.isEvidenceContextActive(contextId, identity)) return diff --git a/tests/pda-loading-api.test.mjs b/tests/pda-loading-api.test.mjs index 94e3060..9687c7b 100644 --- a/tests/pda-loading-api.test.mjs +++ b/tests/pda-loading-api.test.mjs @@ -21,6 +21,9 @@ test('PDA loading adapter keeps C04 read and write paths', () => { assert.match(source, /\/files\/loading-evidence\//) assert.match(source, /loading\/evidence\/pda\?tenantId=/) assert.match(source, /loading\/evidence\/\$\{encodeURIComponent\(evidenceId\)\}\/void\/pda\?tenantId=/) + assert.match(source, /export function completeContainerPickingTask\(taskId, payload = \{\}\)/) + assert.match(source, /first-leg\/pda-loading\/tasks\/\$\{encodeURIComponent\(taskId\)\}\/complete/) + assert.match(source, /expectedTaskVersion/) }) test('PDA adapter does not treat non-200 response bodies or network failures as success', () => { diff --git a/tests/picking-evidence-stage.test.mjs b/tests/picking-evidence-stage.test.mjs index 2375828..cf6e83f 100644 --- a/tests/picking-evidence-stage.test.mjs +++ b/tests/picking-evidence-stage.test.mjs @@ -24,6 +24,9 @@ test('operators receive actionable refresh guidance without internal version wor assert.doesNotMatch(page, /缺少拣货版本号|提供装柜版本号/) const complete = page.match(/async completeLoadingTask\(\) \{[\s\S]*?\n\t\t\t\},/)?.[0] || '' assert.match(complete, /if \(!this\.requireEvidenceStage\(\)\) return/) + assert.match(complete, /completeContainerPickingTask\(this\.remoteTaskId/) + assert.match(complete, /expectedTaskVersion/) + assert.doesNotMatch(complete, /completeLoading\(inboundId/) }) test('AWAITING_REVIEW alone cannot request or mutate server evidence without a server loading version', () => { @@ -129,7 +132,7 @@ test('evidence save, void and loading completion ignore stale cabinet responses' for (const [method, awaitedCall] of [ ['saveEvidence', 'upsertEvidence'], ['voidEvidencePhoto', 'voidEvidence'], - ['completeLoadingTask', 'completeLoading'] + ['completeLoadingTask', 'completeContainerPickingTask'] ]) { const body = page.match(new RegExp(`async ${method}\\([^)]*\\) \\{[\\s\\S]*?\\n\\t\\t\\t\\},`))?.[0] || '' assert.match(body, /const contextId = this\.evidenceContextId/, `${method} should capture the evidence context`)