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`)