This commit is contained in:
2026-09-11 18:16:42 +08:00
parent e41f708570
commit ec62a8a397
3 changed files with 68 additions and 2 deletions
+59 -1
View File
@@ -1127,7 +1127,7 @@
const serverPartialQuantities = {}
this.currentBox.items.forEach((item) => {
if (item.itemStatus === 'PICKED') serverStates[item.id] = ITEM_STATUS.PICKED
else if (item.itemStatus === 'PARTIAL') {
else if (item.itemStatus === 'PICKING' || item.itemStatus === 'PARTIAL') {
serverStates[item.id] = ITEM_STATUS.PARTIAL
serverPartialQuantities[item.id] = Number(item.pickedPackageCount ?? item.finalPickedPackageCount ?? 0)
} else if (item.itemStatus === 'SKIPPED') serverStates[item.id] = ITEM_STATUS.SKIPPED
@@ -1529,6 +1529,64 @@
this.showToast(`已部分拣货:${value}/${item.qty} ${item.unit}`, 'success')
this.persistSession()
},
async submitImmediateItemAction(item, action, payload = {}) {
if (this.submitting) { this.showToast('上一笔拣货正在提交,请稍候', 'warning'); return false }
if (!this.remoteTaskId || !this.currentBox.warehouseId || this.pickingVersion === null) {
this.showToast('任务状态尚未同步,请刷新重试', 'warning')
return false
}
const itemId = item.taskItemId ?? item.remoteItemId ?? item.id
const identity = stableTaskOperationIdentity(this.draftIdentity())
const operationId = stableOperationId(`item-${action.toLowerCase()}`, identity, {
itemId, itemVersion: item.version, taskVersion: this.pickingVersion, ...payload
})
const common = { warehouseId: this.currentBox.warehouseId, version: Number(item.version || 0), operationId }
this.submitting = true
this.remoteError = ''
try {
let task
if (action === 'PICK') task = await pickContainerItem(this.remoteTaskId, itemId, { ...common, pickedPackageCount: payload.pickedPackageCount })
else if (action === 'PARTIAL') task = await partialContainerItem(this.remoteTaskId, itemId, { ...common, finalPickedPackageCount: payload.finalPickedPackageCount })
else if (action === 'SKIP') task = await skipContainerItem(this.remoteTaskId, itemId, { ...common, reason: payload.reason })
else task = await pickContainerPallet(this.remoteTaskId, { warehouseId: this.currentBox.warehouseId, palletNo: payload.palletNo, expectedTaskVersion: Number(this.pickingVersion), operationId })
this.applyImmediateTaskResult(task)
return true
} catch (error) {
if (this.isTaskConflict(error)) await this.refreshAfterTaskConflict(sanitizeUserMessage(error.message, '装柜明细已变化,请刷新后重试'))
else {
this.remoteError = error.network ? '网络失败,本次拣货未提交' : this.remoteFailureMessage(error, '拣货提交失败')
this.showToast('拣货未提交,请重试', 'error')
}
return false
} finally {
this.submitting = false
}
},
applyImmediateTaskResult(result) {
if (!result) return
const task = normalizePickingTask({ ...this.remotePick, ...result })
this.remotePick = task
this.pickingVersion = task.pickingVersion ?? task.version ?? this.pickingVersion
const remoteItems = Array.isArray(result.items) ? result.items : []
const byId = new Map(remoteItems.map((item) => [String(item.id ?? item.itemId), item]))
this.currentBox.items = this.currentBox.items.map((item) => {
const remote = byId.get(String(item.taskItemId ?? item.remoteItemId ?? item.id))
return remote ? { ...item, ...remote, id: item.id, taskItemId: remote.id ?? remote.itemId ?? item.taskItemId } : item
})
const nextStates = { ...this.itemStates }
const nextPartial = { ...this.partialQuantities }
this.currentBox.items.forEach((item) => {
const count = Number(item.pickedPackageCount || 0)
if (item.itemStatus === 'PICKED') nextStates[item.id] = ITEM_STATUS.PICKED
else if (item.itemStatus === 'PICKING' || item.itemStatus === 'PARTIAL') {
nextStates[item.id] = ITEM_STATUS.PARTIAL
nextPartial[item.id] = count
} else if (item.itemStatus === 'SKIPPED') nextStates[item.id] = ITEM_STATUS.SKIPPED
})
this.itemStates = nextStates
this.partialQuantities = nextPartial
this.evidenceIdentity = this.captureEvidenceIdentity()
},
availablePickQuantity(item) {
if (!item) return null
if (item.locationAvailable === false || item.locationStatus === 'DISABLED') return 0
+1 -1
View File
@@ -268,7 +268,7 @@ test('picking scan results use the shared success and error audio feedback', asy
assert.match(audio, /export function playErrorSound\(\)/)
assert.match(picking, /import \{ playErrorSound, playSuccessSound \} from '@\/utils\/audio-feedback\.js'/)
assert.match(picking, /result\.type === 'unknown'[\s\S]*?playErrorSound\(\)/)
assert.match(picking, /playSuccessSound\(\)[\s\S]*?this\.pickScannedBox\(result\.item, value\)/)
assert.match(picking, /await this\.pickScannedBox\(result\.item, value\)[\s\S]*?if \(submitted\) playSuccessSound\(\)/)
})
test('reusable scan target input separates field selection from page-level broadcast handling', async () => {
+8
View File
@@ -109,6 +109,8 @@ test('PDA cabinet picking adapter claims online and submits one atomic batch', (
assert.doesNotMatch(source, /query\.push\(`cabinetNo=/)
assert.doesNotMatch(source, /query\.push\(`loadingPlanId=/)
assert.match(source, /tasks\/\$\{encodeURIComponent\(taskId\)\}\/claim/)
assert.match(source, /tasks\/\$\{encodeURIComponent\(taskId\)\}\/items\/\$\{encodeURIComponent\(itemId\)\}\/\$\{path\}/)
assert.match(source, /tasks\/\$\{encodeURIComponent\(taskId\)\}\/pallets\/pick/)
assert.match(source, /tasks\/\$\{encodeURIComponent\(task\.taskId\)\}\/batch/)
assert.match(source, /tasks\/\$\{encodeURIComponent\(taskId\)\}\/inventory-snapshot\?warehouseId=/)
assert.match(source, /submitContainerPickingBatch/)
@@ -116,6 +118,12 @@ test('PDA cabinet picking adapter claims online and submits one atomic batch', (
assert.match(source, /Math\.imul\(hash, 16777619\)/)
assert.match(source, /slice\(0, 128\)/)
assert.match(page, /claimContainerPickingTask\(/)
assert.match(page, /submitImmediateItemAction\(item, 'PICK'/)
assert.match(page, /submitImmediateItemAction\(item, 'PARTIAL'/)
assert.match(page, /submitImmediateItemAction\(item, 'SKIP'/)
assert.match(page, /submitImmediateItemAction\(item, 'PICK_PALLET'/)
assert.match(page, /item\.itemStatus === 'PICKING' \|\| item\.itemStatus === 'PARTIAL'/)
assert.doesNotMatch(page, /@tap="undoItem\(item\.id\)"/)
assert.match(page, /submitContainerPickingBatch\(/)
assert.match(source, /palletNo: action\.type === 'PICK_PALLET' \? action\.palletNo : null/)
assert.match(page, /type: 'PICK_PALLET'[\s\S]*?palletNo: item\.palletNo/)