优化
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 }
|
export { EVIDENCE_SLOTS, EVIDENCE_TYPES }
|
||||||
|
|||||||
@@ -150,7 +150,7 @@
|
|||||||
</view>
|
</view>
|
||||||
<view v-if="!loadingStageReady" class="evidence-stage-hint" role="status">当前可先拍照留存;拣货完成并同步装柜状态后,照片会自动上传绑定。</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>
|
<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">
|
<view v-if="loadingVersion === null" class="remote-error-row">
|
||||||
<text class="version-hint" role="status">状态尚未同步,请刷新重试</text>
|
<text class="version-hint" role="status">状态尚未同步,请刷新重试</text>
|
||||||
<button class="evidence-retry-btn" :disabled="remoteLoading" @tap="refreshTask">刷新任务</button>
|
<button class="evidence-retry-btn" :disabled="remoteLoading" @tap="refreshTask">刷新任务</button>
|
||||||
@@ -344,7 +344,7 @@
|
|||||||
import { navigateBackOr } from '@/utils/navigation.js'
|
import { navigateBackOr } from '@/utils/navigation.js'
|
||||||
import { startScanBroadcast } from '@/utils/scan-broadcast.js'
|
import { startScanBroadcast } from '@/utils/scan-broadcast.js'
|
||||||
import { playErrorSound, playSuccessSound } from '@/utils/audio-feedback.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 { createEvidenceUploadQueue } from '@/utils/evidence-upload-queue.js'
|
||||||
import { isOnline } from '@/utils/network.js'
|
import { isOnline } from '@/utils/network.js'
|
||||||
import { stableOperationId } from '@/utils/idempotency.js'
|
import { stableOperationId } from '@/utils/idempotency.js'
|
||||||
@@ -1140,17 +1140,30 @@
|
|||||||
async completeLoadingTask() {
|
async completeLoadingTask() {
|
||||||
if (!this.requireEvidenceStage()) return
|
if (!this.requireEvidenceStage()) return
|
||||||
if (!this.evidenceIdentity?.inboundId || !isRemoteReady(this.evidenceIdentity.inboundId)) { this.showToast('装柜任务尚未同步,请刷新后重试', 'warning'); 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 }
|
if (!this.evidenceComplete) { this.showToast('五类有效照片和封签号均为必填', 'warning'); return }
|
||||||
const contextId = this.evidenceContextId
|
const contextId = this.evidenceContextId
|
||||||
const identity = { ...this.evidenceIdentity }
|
const identity = { ...this.evidenceIdentity }
|
||||||
const inboundId = identity.inboundId
|
const expectedTaskVersion = Number(this.pickingVersion)
|
||||||
const tenantId = this.currentBox.tenantId
|
const operationIdentity = stableTaskOperationIdentity(this.draftIdentity())
|
||||||
const loadingVersion = this.loadingVersion
|
const operationId = stableOperationId('complete', operationIdentity, {
|
||||||
|
taskId: this.remoteTaskId,
|
||||||
|
expectedTaskVersion,
|
||||||
|
loadingPlanId: identity.loadingPlanId,
|
||||||
|
containerBookingId: identity.containerBookingId
|
||||||
|
})
|
||||||
this.submitting = true
|
this.submitting = true
|
||||||
try {
|
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 (!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')
|
this.showToast('已确认装柜完成', 'success')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (!this.isEvidenceContextActive(contextId, identity)) return
|
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, /\/files\/loading-evidence\//)
|
||||||
assert.match(source, /loading\/evidence\/pda\?tenantId=/)
|
assert.match(source, /loading\/evidence\/pda\?tenantId=/)
|
||||||
assert.match(source, /loading\/evidence\/\$\{encodeURIComponent\(evidenceId\)\}\/void\/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', () => {
|
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, /缺少拣货版本号|提供装柜版本号/)
|
assert.doesNotMatch(page, /缺少拣货版本号|提供装柜版本号/)
|
||||||
const complete = page.match(/async completeLoadingTask\(\) \{[\s\S]*?\n\t\t\t\},/)?.[0] || ''
|
const complete = page.match(/async completeLoadingTask\(\) \{[\s\S]*?\n\t\t\t\},/)?.[0] || ''
|
||||||
assert.match(complete, /if \(!this\.requireEvidenceStage\(\)\) return/)
|
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', () => {
|
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 [
|
for (const [method, awaitedCall] of [
|
||||||
['saveEvidence', 'upsertEvidence'],
|
['saveEvidence', 'upsertEvidence'],
|
||||||
['voidEvidencePhoto', 'voidEvidence'],
|
['voidEvidencePhoto', 'voidEvidence'],
|
||||||
['completeLoadingTask', 'completeLoading']
|
['completeLoadingTask', 'completeContainerPickingTask']
|
||||||
]) {
|
]) {
|
||||||
const body = page.match(new RegExp(`async ${method}\\([^)]*\\) \\{[\\s\\S]*?\\n\\t\\t\\t\\},`))?.[0] || ''
|
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`)
|
assert.match(body, /const contextId = this\.evidenceContextId/, `${method} should capture the evidence context`)
|
||||||
|
|||||||
Reference in New Issue
Block a user