优化
This commit is contained in:
@@ -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 }
|
||||
|
||||
@@ -150,7 +150,7 @@
|
||||
</view>
|
||||
<view v-if="!loadingStageReady" class="evidence-stage-hint" role="status">当前可先拍照留存;拣货完成并同步装柜状态后,照片会自动上传绑定。</view>
|
||||
<view v-if="remoteError" class="remote-error-row"><text class="remote-error" role="alert">{{ remoteError }}</text><button class="evidence-retry-btn" :disabled="remoteLoading" @tap="refreshTask">刷新任务</button></view>
|
||||
<button class="loading-complete-btn" :disabled="submitting || loadingVersion === null || !evidenceComplete" @tap="completeLoadingTask">确认装柜完成</button>
|
||||
<button class="loading-complete-btn" :disabled="submitting || pickingVersion === null || loadingVersion === null || !evidenceComplete" @tap="completeLoadingTask">确认装柜完成</button>
|
||||
<view v-if="loadingVersion === null" class="remote-error-row">
|
||||
<text class="version-hint" role="status">状态尚未同步,请刷新重试</text>
|
||||
<button class="evidence-retry-btn" :disabled="remoteLoading" @tap="refreshTask">刷新任务</button>
|
||||
@@ -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
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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`)
|
||||
|
||||
Reference in New Issue
Block a user