feat(pda): connect online picking workflow
This commit is contained in:
+40
-18
@@ -1,4 +1,5 @@
|
||||
import { session } from '@/stores/session.js'
|
||||
import { requireOnline } from '@/utils/network.js'
|
||||
|
||||
const EVIDENCE_TYPES = Object.freeze([
|
||||
'PHOTO_STEP_1', 'PHOTO_STEP_2', 'PHOTO_STEP_3', 'PHOTO_STEP_4', 'SEAL'
|
||||
@@ -19,6 +20,7 @@ const EVIDENCE_SLOTS = Object.freeze([
|
||||
// server success when any required identifier or version is absent.
|
||||
|
||||
function request(options) {
|
||||
requireOnline()
|
||||
const baseUrl = session.apiBase || '/api/platform/v1'
|
||||
const headers = { ...(options.header || {}) }
|
||||
if (session.token) headers.Authorization = `Bearer ${session.token}`
|
||||
@@ -57,7 +59,14 @@ function idempotencyKey(operation, inboundId) {
|
||||
}
|
||||
|
||||
function stableIdempotencyKey(operationId, suffix = '') {
|
||||
return `pda-${operationId}${suffix ? `-${suffix}` : ''}`
|
||||
const raw = `pda-${operationId}${suffix ? `-${suffix}` : ''}`
|
||||
let hash = 2166136261
|
||||
for (let index = 0; index < raw.length; index += 1) {
|
||||
hash ^= raw.charCodeAt(index)
|
||||
hash = Math.imul(hash, 16777619)
|
||||
}
|
||||
const prefix = raw.replace(/[^A-Za-z0-9._:-]/g, '-').slice(0, 96)
|
||||
return `${prefix}-${(hash >>> 0).toString(36)}-${raw.length}`.slice(0, 128)
|
||||
}
|
||||
|
||||
function tenantQuery(tenantId) {
|
||||
@@ -67,14 +76,14 @@ function tenantQuery(tenantId) {
|
||||
}
|
||||
|
||||
export function isRemoteReady(inboundId) {
|
||||
return Boolean(session.token && session.tenantId && inboundId)
|
||||
return Boolean(session.token && (session.tenantId || inboundId))
|
||||
}
|
||||
|
||||
export function getContainerPickingTask(containerNo, warehouseId) {
|
||||
if (!containerNo) throw new Error('缺少柜次号,无法请求现场任务')
|
||||
const query = [`containerNo=${encodeURIComponent(containerNo)}`]
|
||||
if (warehouseId) query.push(`warehouseId=${encodeURIComponent(warehouseId)}`)
|
||||
query.push(`tenantId=${encodeURIComponent(tenantQuery())}`)
|
||||
if (session.tenantId) query.push(`tenantId=${encodeURIComponent(session.tenantId)}`)
|
||||
return request({ url: `/first-leg/pda-loading/tasks?${query.join('&')}`, method: 'GET' }).then((data) => {
|
||||
if (Array.isArray(data)) return data[0] || null
|
||||
if (data?.records) return data.records[0] || null
|
||||
@@ -118,20 +127,26 @@ export function skipContainerItem(taskId, itemId, payload = {}) {
|
||||
export async function submitContainerPickingBatch(task, actions, { warehouseId, operationId } = {}) {
|
||||
if (!task?.taskId) throw new Error('缺少拣货任务标识,无法提交')
|
||||
const submitOperationId = operationId || `submit-${task.taskId}-${task.containerNo || ''}-${task.version ?? task.pickingVersion ?? ''}`
|
||||
const results = []
|
||||
let version = task.version ?? task.pickingVersion
|
||||
for (const action of actions || []) {
|
||||
const common = { warehouseId, locationIds: action.locationIds || [], version: action.version ?? version, operationId: `${submitOperationId}-${action.itemId}` }
|
||||
let result
|
||||
if (action.type === 'PICK') result = await pickContainerItem(task.taskId, action.itemId, { ...common, pickedPackageCount: action.pickedPackageCount })
|
||||
else if (action.type === 'PARTIAL') result = await partialContainerItem(task.taskId, action.itemId, { ...common, finalPickedPackageCount: action.finalPickedPackageCount })
|
||||
else if (action.type === 'SKIP') result = await skipContainerItem(task.taskId, action.itemId, { ...common, reason: action.reason })
|
||||
if (result !== undefined) {
|
||||
results.push(result)
|
||||
version = result?.version ?? result?.taskVersion ?? result?.pickingVersion ?? version
|
||||
}
|
||||
}
|
||||
return results
|
||||
const expectedVersion = task.version ?? task.pickingVersion
|
||||
if (expectedVersion == null) throw new Error('缺少拣货任务版本,无法整批提交')
|
||||
if (!warehouseId) throw new Error('缺少作业仓库标识,无法整批提交')
|
||||
if (!task.containerNo) throw new Error('缺少柜号,无法整批提交')
|
||||
const items = (actions || []).map((action) => ({
|
||||
itemId: action.itemId,
|
||||
action: action.type,
|
||||
pickedPackageCount: action.type === 'PICK' ? action.pickedPackageCount : null,
|
||||
finalPickedPackageCount: action.type === 'PARTIAL' ? action.finalPickedPackageCount : null,
|
||||
releaseMode: action.type === 'SKIP' ? null : (action.releaseMode || 'AUTO'),
|
||||
locationIds: action.type === 'SKIP' ? [] : (action.locationIds || []),
|
||||
version: action.version,
|
||||
reason: action.type === 'SKIP' ? action.reason : null
|
||||
}))
|
||||
return request({
|
||||
url: `/first-leg/pda-loading/tasks/${encodeURIComponent(task.taskId)}/batch`,
|
||||
method: 'POST',
|
||||
header: { 'Idempotency-Key': stableIdempotencyKey(submitOperationId) },
|
||||
data: { warehouseId, containerNo: task.containerNo, expectedVersion, items }
|
||||
})
|
||||
}
|
||||
|
||||
export function listLoadingOrders({ page = 1, size = 50, keyword = '' } = {}) {
|
||||
@@ -150,7 +165,13 @@ export function normalizePdaOrder(row = {}) {
|
||||
...row,
|
||||
inboundId: row.inboundId ?? row.inbound?.id ?? null,
|
||||
inboundVersion: row.inboundVersion ?? row.inbound?.version ?? null,
|
||||
pickingVersion: row.pickingVersion ?? row.loading?.pickingVersion ?? null
|
||||
tenantId: row.tenantId ?? row.accountId ?? row.inbound?.tenantId ?? null,
|
||||
tenantId: row.tenantId ?? row.accountId ?? row.inbound?.tenantId ?? null,
|
||||
pickingVersion: row.pickingVersion ?? row.loading?.pickingVersion ?? null,
|
||||
taskId: row.taskId ?? row.pdaTaskId ?? row.loadingTaskId ?? row.loading?.taskId ?? null,
|
||||
containerNo: row.containerNo ?? row.container?.containerNo ?? row.loadingPlan?.containerNo ?? row.loading?.containerNo ?? '',
|
||||
warehouseId: row.warehouseId ?? row.warehouse?.id ?? row.loadingPlan?.warehouseId ?? null,
|
||||
displayNo: row.orderNo ?? row.inboundCode ?? row.order?.orderNo ?? row.id
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,6 +239,7 @@ export function voidEvidence(inboundId, evidenceId, payload = {}, tenantId) {
|
||||
|
||||
/** Upload a local photo to the file service and return its durable fileId. */
|
||||
export function uploadEvidenceFile(inboundId, evidenceType, localPath, tenantId, onProgress) {
|
||||
requireOnline('当前网络不可用,无法上传装柜照片')
|
||||
if (!EVIDENCE_TYPES.includes(evidenceType)) throw new Error('不支持的装柜证据类型')
|
||||
if (!localPath) throw new Error('缺少待上传的照片')
|
||||
const tenant = tenantQuery(tenantId)
|
||||
|
||||
Reference in New Issue
Block a user