This commit is contained in:
2026-10-26 13:43:39 +08:00
parent 9c2f0a54ec
commit 034b41c39c
7 changed files with 155 additions and 9 deletions
+1 -1
View File
@@ -147,7 +147,7 @@ export default {
await this.recoverCommand()
} catch (cause) { this.setError(cause, '理货审核未提交') }
},
async recoverCommand() { if (!this.pendingCommand || this.busy || this.loading) return; const command = JSON.parse(JSON.stringify(this.pendingCommand)), scope = fulfillment.scopeKey, previousAttempt = command.attempted; await this.run(async () => { const ensureScope = () => { if (scope !== fulfillment.scopeKey) throw new Error('作业身份已变化,请重新进入任务') }; let result = command.result; if (!result && previousAttempt) { try { result = await getDispatchCommand(command.dispatchNo, command.body.idempotencyKey) } catch (cause) { if (!['404', 'A0404', 'HTTP_404'].includes(String(cause.code))) throw cause } ensureScope() } if (!result) { patchDispatchCommand({ attempted: true }); try { const { dispatchItemId, ...body } = command.body; result = command.action === 'PICK' ? await pickDispatchItem(command.dispatchNo, dispatchItemId, body) : command.action === 'LOADING_COMPLETE' ? await completeDispatchLoading(command.dispatchNo, body) : command.action === 'LOADING_REVIEW' ? await reviewDispatchLoading(command.dispatchNo, body) : null } catch (cause) { ensureScope(); if (!previousAttempt && !cause.network && /^(?:A0|HTTP_)?(?:400|401|403|404|409|410|422)$/.test(String(cause.code))) patchDispatchCommand({ rejected: true }); throw cause } } ensureScope(); if (!result || result.dispatchNo !== command.dispatchNo || result.action !== command.action || result.idempotencyKey !== command.body.idempotencyKey || !result.result || command.action === 'LOADING_COMPLETE' && !['AFTER_LOADING', 'DELIVERING'].includes(result.stage) || command.action === 'LOADING_REVIEW' && result.stage !== (command.body.decision === 'APPROVED' ? 'LOADING' : 'BEFORE_LOADING')) throw new Error('返回结果未确认原排车动作,请保留原请求核对'); requireDispatchVersion(result.recordVersion); patchDispatchCommand({ result }); const latest = await getDispatchTask(command.dispatchNo); ensureScope(); if (latest.dispatchNo !== command.dispatchNo) throw new Error('恢复详情与原排车单不一致'); if (!dispatchVersionAtLeast(latest.recordVersion, result.recordVersion)) throw new Error('任务版本早于已确认动作,请重读原请求'); this.current = latest; updateFulfillmentDraft('dispatch', { currentNo: latest.dispatchNo, dispatchItemId: '', palletCode: '', cartonCode: '', packages: 1 }); finishDispatchCommand(command.body.idempotencyKey); this.loadingPreview = null; if (command.action === 'LOADING_REVIEW') this.reviewReason = ''; this.successMessage = command.action === 'PICK' ? '本次真实拣货已记录' : command.action === 'LOADING_COMPLETE' ? latest.stage === 'DELIVERING' ? '装车完成、资料齐全,已进入派送中' : '装车完成已确认,请补齐装车资料' : command.action === 'LOADING_REVIEW' ? command.body.decision === 'APPROVED' ? '理货审核已通过,可以开始真实拣货' : '已退回客服处理(车辆信息),等待修改并重新提交' : '排车操作已确认'; playSuccessSound() }) },
async recoverCommand() { if (!this.pendingCommand || this.busy || this.loading) return; const command = JSON.parse(JSON.stringify(this.pendingCommand)), scope = fulfillment.scopeKey, previousAttempt = command.attempted; await this.run(async () => { const ensureScope = () => { if (scope !== fulfillment.scopeKey) throw new Error('作业身份已变化,请重新进入任务') }; let result = command.result; if (!result && previousAttempt) { try { result = await getDispatchCommand(command.dispatchNo, command.body.idempotencyKey) } catch (cause) { if (!['404', 'A0404', 'HTTP_404'].includes(String(cause.code))) throw cause } ensureScope() } if (!result) { patchDispatchCommand({ attempted: true }); try { const { dispatchItemId, ...body } = command.body; result = command.action === 'PICK' ? await pickDispatchItem(command.dispatchNo, dispatchItemId, body) : command.action === 'LOADING_COMPLETE' ? await completeDispatchLoading(command.dispatchNo, body) : command.action === 'LOADING_REVIEW' ? await reviewDispatchLoading(command.dispatchNo, body) : null } catch (cause) { ensureScope(); if (!previousAttempt && !cause.network && !cause.storageRentPreparation && /^(?:A0|HTTP_)?(?:400|401|403|404|409|410|422)$/.test(String(cause.code))) patchDispatchCommand({ rejected: true }); throw cause } } ensureScope(); if (!result || result.dispatchNo !== command.dispatchNo || result.action !== command.action || result.idempotencyKey !== command.body.idempotencyKey || !result.result || command.action === 'LOADING_COMPLETE' && !['AFTER_LOADING', 'DELIVERING'].includes(result.stage) || command.action === 'LOADING_REVIEW' && result.stage !== (command.body.decision === 'APPROVED' ? 'LOADING' : 'BEFORE_LOADING')) throw new Error('返回结果未确认原排车动作,请保留原请求核对'); requireDispatchVersion(result.recordVersion); patchDispatchCommand({ result }); const latest = await getDispatchTask(command.dispatchNo); ensureScope(); if (latest.dispatchNo !== command.dispatchNo) throw new Error('恢复详情与原排车单不一致'); if (!dispatchVersionAtLeast(latest.recordVersion, result.recordVersion)) throw new Error('任务版本早于已确认动作,请重读原请求'); this.current = latest; updateFulfillmentDraft('dispatch', { currentNo: latest.dispatchNo, dispatchItemId: '', palletCode: '', cartonCode: '', packages: 1 }); finishDispatchCommand(command.body.idempotencyKey); this.loadingPreview = null; if (command.action === 'LOADING_REVIEW') this.reviewReason = ''; this.successMessage = command.action === 'PICK' ? '本次真实拣货已记录' : command.action === 'LOADING_COMPLETE' ? latest.stage === 'DELIVERING' ? '装车完成、资料齐全,已进入派送中' : '装车完成已确认,请补齐装车资料' : command.action === 'LOADING_REVIEW' ? command.body.decision === 'APPROVED' ? '理货审核已通过,可以开始真实拣货' : '已退回客服处理(车辆信息),等待修改并重新提交' : '排车操作已确认'; playSuccessSound() }) },
discardRejected() { if (this.busy || !this.pendingCommand?.rejected) return; discardRejectedDispatchCommand(); this.retry() },
async retry() { if (this.pendingCommand) await this.recoverCommand(); else if (this.current?.dispatchNo) await this.openTask(this.current.dispatchNo); else await this.loadTasks() },
async run(work, loading = false) { if (this.busy || this.loading) return; this.error = null; this.successMessage = ''; if (loading) this.loading = true; else this.busy = true; try { await work() } catch (cause) { this.setError(cause, '排车操作失败') } finally { this.loading = false; this.busy = false } },
+1 -1
View File
@@ -196,7 +196,7 @@
// 未填写、负数、小数或超出预留数均阻止提交,保留输入供仓库修正。
validDiscardActual(sku) { const raw = this.discardActuals[sku.itemId]; if (raw == null || String(raw).trim() === '') return false; const actual = Number(raw); return Number.isSafeInteger(actual) && actual >= 0 && (sku.actualQuantityEditable === true ? actual <= sku.plannedPackages : actual === sku.plannedPackages) },
updateDiscardActual(itemId, event) { this.discardActuals = { ...this.discardActuals, [itemId]: event.detail.value } },
async confirmDiscard() { if (this.discardBusy || this.discardLoading || !this.canConfirmDiscard || !this.discardTask || !this.discardCurrent) return; this.discardBusy = true; try { const lines = this.discardTask.skus.map((sku) => ({ itemId: sku.itemId, actualPackages: Number(this.discardActuals[sku.itemId]) })); await confirmCargoDiscard(this.discardCurrent.discardNo, this.discardTask.palletNo, lines, this.discardPhotos.map(photo => String(photo.fileId)), this.discardScannedMarks, this.discardAllCartons); this.successMessage = '废弃出库作业及照片已提交,已完成'; this.resetDiscardPhotos(); this.discardPalletCode = ''; this.discardTask = null; this.discardTasks = await listCargoDiscardTasks(); playSuccessSound() } catch (error) { this.setError(error, '废弃出库失败') } finally { this.discardBusy = false } },
async confirmDiscard() { if (this.discardBusy || this.discardLoading || !this.canConfirmDiscard || !this.discardTask || !this.discardCurrent) return; this.error = null; this.successMessage = ''; this.discardBusy = true; try { const lines = this.discardTask.skus.map((sku) => ({ itemId: sku.itemId, actualPackages: Number(this.discardActuals[sku.itemId]) })); const photoFileIds = this.discardPhotos.map(photo => String(photo.fileId)); const key = fulfillmentIdempotencyKey('outbound', 'DISCARD', { discardNo: this.discardCurrent.discardNo, palletNo: this.discardTask.palletNo, lines, photoFileIds, scannedCartonMarks: this.discardScannedMarks, confirmAllCartons: this.discardAllCartons }); await confirmCargoDiscard(this.discardCurrent.discardNo, this.discardTask.palletNo, lines, photoFileIds, this.discardScannedMarks, this.discardAllCartons, { idempotencyKey: key.value }); releaseFulfillmentKey('outbound', key.signature); this.resetDiscardPhotos(); this.discardPalletCode = ''; this.discardTask = null; this.discardTasks = await listCargoDiscardTasks(); this.successMessage = '废弃出库作业及照片已提交,已完成'; playSuccessSound() } catch (error) { this.setError(error, '废弃出库失败') } finally { this.discardBusy = false } },
handleScan(raw) {
const code = String(raw?.detail?.value ?? raw ?? this.scanCode).trim(); if (!code || this.busy || this.loading || this.discardBusy || this.discardLoading) return
console.info('[Overseas PDA outbound] scan', { code, task: this.current?.taskNo || '', discard: this.discardCurrent?.discardNo || '' }); this.scanCode = code; updateFulfillmentDraft('outbound', { scanCode: code })