优化
This commit is contained in:
@@ -183,6 +183,9 @@ export function normalizePdaOrder(row = {}) {
|
||||
taskId: row.taskId ?? row.pdaTaskId ?? row.loadingTaskId ?? row.loading?.taskId ?? null,
|
||||
loadingPlanId: row.loadingPlanId ?? row.loading?.loadingPlanId ?? row.loadingPlan?.id ?? null,
|
||||
containerBookingId: row.containerBookingId ?? row.loading?.containerBookingId ?? row.container?.id ?? null,
|
||||
sourceType: row.sourceType ?? row.container?.sourceType ?? null,
|
||||
recordStatus: row.recordStatus ?? row.container?.recordStatus ?? null,
|
||||
mergedIntoContainerBookingId: row.mergedIntoContainerBookingId ?? row.container?.mergedIntoContainerBookingId ?? null,
|
||||
cabinetNo: row.cabinetNo ?? row.loading?.cabinetNo ?? row.container?.cabinetNo ?? row.containerNo ?? '',
|
||||
currentContainerNo: row.currentContainerNo ?? row.containerNo ?? row.container?.containerNo ?? row.loadingPlan?.containerNo ?? row.loading?.containerNo ?? null,
|
||||
warehouseId: row.warehouseId ?? row.warehouse?.id ?? row.loadingPlan?.warehouseId ?? null,
|
||||
@@ -197,6 +200,9 @@ export function normalizePickingTask(task = {}) {
|
||||
taskId: task.taskId ?? task.id ?? null,
|
||||
loadingPlanId: task.loadingPlanId ?? task.loading?.loadingPlanId ?? null,
|
||||
containerBookingId: task.containerBookingId ?? task.loading?.containerBookingId ?? null,
|
||||
sourceType: task.sourceType ?? task.container?.sourceType ?? null,
|
||||
recordStatus: task.recordStatus ?? task.container?.recordStatus ?? null,
|
||||
mergedIntoContainerBookingId: task.mergedIntoContainerBookingId ?? task.container?.mergedIntoContainerBookingId ?? null,
|
||||
cabinetNo: task.cabinetNo ?? task.loading?.cabinetNo ?? task.containerNo ?? '',
|
||||
currentContainerNo: task.currentContainerNo ?? task.containerNo ?? null,
|
||||
pickingVersion: task.pickingVersion ?? task.version ?? null,
|
||||
|
||||
@@ -574,6 +574,52 @@
|
||||
isSameEvidenceIdentity(left, right) {
|
||||
return ['taskId', 'inboundId', 'loadingPlanId', 'containerBookingId'].every((key) => String(left?.[key] ?? '') === String(right?.[key] ?? ''))
|
||||
},
|
||||
isMergedContainerRecord(task) {
|
||||
return task?.recordStatus === 'MERGED' || Boolean(task?.mergedIntoContainerBookingId)
|
||||
},
|
||||
clearMergedTask(task, previousIdentity) {
|
||||
const operatorId = session.currentUser?.id || session.currentUser?.userId || session.currentUser?.accountId
|
||||
const staleIdentity = {
|
||||
operatorId,
|
||||
taskId: previousIdentity?.taskId ?? task?.taskId ?? task?.id,
|
||||
loadingPlanId: previousIdentity?.loadingPlanId ?? task?.loadingPlanId,
|
||||
containerBookingId: previousIdentity?.containerBookingId ?? task?.containerBookingId,
|
||||
warehouseId: previousIdentity?.warehouseId ?? this.currentBox.warehouseId
|
||||
}
|
||||
clearPickingDraft(staleIdentity)
|
||||
const evidenceKey = this.evidenceDraftKey({
|
||||
taskId: staleIdentity.taskId,
|
||||
inboundId: previousIdentity?.inboundId ?? task?.inboundId,
|
||||
loadingPlanId: staleIdentity.loadingPlanId,
|
||||
containerBookingId: staleIdentity.containerBookingId
|
||||
})
|
||||
this.evidenceDrafts = Object.fromEntries(Object.entries(this.evidenceDrafts).filter(([key]) => key !== evidenceKey))
|
||||
this.evidencePreviewCache = Object.fromEntries(Object.entries(this.evidencePreviewCache).filter(([key]) => !key.startsWith(`${evidenceKey}|`)))
|
||||
this.taskRequestId += 1
|
||||
this.remoteLoading = false
|
||||
this.submitting = false
|
||||
this.remoteError = '当前柜记录已合并,请返回任务列表刷新'
|
||||
this.remoteTaskId = null
|
||||
this.remotePick = null
|
||||
this.pickingVersion = null
|
||||
this.loadingVersion = null
|
||||
this.assignmentVersion = null
|
||||
this.itemStates = {}
|
||||
this.partialQuantities = {}
|
||||
this.scannedBoxCodes = {}
|
||||
this.boxDrafts = {}
|
||||
this.completedBoxes = {}
|
||||
this.scanHistory = []
|
||||
this.currentBox.taskId = null
|
||||
this.currentBox.inboundId = null
|
||||
this.currentBox.loadingPlanId = null
|
||||
this.currentBox.containerBookingId = null
|
||||
this.currentBox.cabinetNo = ''
|
||||
this.currentBox.boxNo = ''
|
||||
this.currentBox.currentContainerNo = null
|
||||
this.currentBox.items = []
|
||||
this.resetEvidenceState()
|
||||
},
|
||||
isEvidenceContextActive(contextId, identity) {
|
||||
return contextId === this.evidenceContextId
|
||||
&& this.isSameEvidenceIdentity(this.evidenceIdentity, identity)
|
||||
@@ -647,7 +693,7 @@
|
||||
},
|
||||
draftIdentity() {
|
||||
const operatorId = session.currentUser?.id || session.currentUser?.userId || session.currentUser?.accountId
|
||||
return { operatorId, taskId: this.remoteTaskId || this.currentBox.taskId, loadingPlanId: this.currentBox.loadingPlanId || this.remotePick?.loadingPlanId, containerBookingId: this.currentBox.containerBookingId || this.remotePick?.containerBookingId, warehouseId: this.currentBox.warehouseId || session.currentUser?.warehouseId || session.currentUser?.warehouse }
|
||||
return { operatorId, taskId: this.remoteTaskId || this.currentBox.taskId, inboundId: this.currentBox.inboundId || this.remotePick?.inboundId, loadingPlanId: this.currentBox.loadingPlanId || this.remotePick?.loadingPlanId, containerBookingId: this.currentBox.containerBookingId || this.remotePick?.containerBookingId, warehouseId: this.currentBox.warehouseId || session.currentUser?.warehouseId || session.currentUser?.warehouse }
|
||||
},
|
||||
restoreRemoteDraft() {
|
||||
const restored = restorePickingSession(this.draftIdentity())
|
||||
@@ -864,6 +910,7 @@
|
||||
const requestedTaskId = this.remoteTaskId || this.currentBox.taskId
|
||||
if (!requestedTaskId) { this.remoteError = '当前没有可进入的拣货任务,请返回任务列表刷新'; return }
|
||||
this.rememberEvidenceDraft()
|
||||
const previousIdentity = this.draftIdentity()
|
||||
const requestId = ++this.taskRequestId
|
||||
const selector = { taskId: requestedTaskId }
|
||||
this.currentBox.inboundId = null
|
||||
@@ -879,6 +926,12 @@
|
||||
const task = await getContainerPickingTask(selector, warehouseId)
|
||||
if (requestId !== this.taskRequestId) return
|
||||
if (!task) throw new Error('服务端未返回当前拣货任务')
|
||||
if (this.isMergedContainerRecord(task)) {
|
||||
this.clearMergedTask(task, previousIdentity)
|
||||
this.showToast('当前柜记录已合并,请返回任务列表刷新', 'warning')
|
||||
return
|
||||
}
|
||||
if (task.recordStatus !== 'ACTIVE') throw new Error('当前柜记录尚未就绪,请返回任务列表刷新')
|
||||
const detailTaskId = task.taskId ?? task.id
|
||||
if (String(detailTaskId || '') !== String(requestedTaskId)) throw new Error('服务端返回的拣货任务与当前选择不一致,请返回刷新')
|
||||
if (!task.inboundId || !task.loadingPlanId || !task.containerBookingId) throw new Error('任务尚未同步完整作业关联,请返回任务列表刷新')
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
import PortalHeader from '@/components/PortalHeader.vue'
|
||||
import { listLoadingOrders } from '@/api/pda-loading.js'
|
||||
import { session, requireSession } from '@/stores/session.js'
|
||||
import { clearPickingDraft } from '@/stores/picking.js'
|
||||
import { isOnline, watchNetwork } from '@/utils/network.js'
|
||||
|
||||
export default {
|
||||
@@ -36,13 +37,19 @@
|
||||
onUnload() { if (this._stopNetwork) this._stopNetwork(); clearTimeout(this._toastTimer) },
|
||||
methods: {
|
||||
statusLabel(status) { return status === 'IN_PROGRESS' ? '拣货中' : '待领取' },
|
||||
isTaskReady(task) { return Boolean(task.taskId && task.loadingPlanId && task.containerBookingId && task.cabinetNo && task.currentContainerNo && task.warehouseId && task.version !== null && task.version !== undefined && Number.isInteger(Number(task.version)) && task.assignmentVersion !== null && task.assignmentVersion !== undefined && Number.isInteger(Number(task.assignmentVersion)) && ['UNASSIGNED', 'IN_PROGRESS'].includes(task.workflowStatus)) },
|
||||
isMergedTask(task) { return task?.recordStatus === 'MERGED' || Boolean(task?.mergedIntoContainerBookingId) },
|
||||
clearMergedTaskDraft(task) {
|
||||
const operatorId = session.currentUser?.id || session.currentUser?.userId || session.currentUser?.accountId
|
||||
clearPickingDraft({ operatorId, taskId: task.taskId, loadingPlanId: task.loadingPlanId, containerBookingId: task.containerBookingId, warehouseId: task.warehouseId })
|
||||
},
|
||||
isTaskReady(task) { return Boolean(task.recordStatus === 'ACTIVE' && !task.mergedIntoContainerBookingId && task.taskId && task.loadingPlanId && task.containerBookingId && task.cabinetNo && task.currentContainerNo && task.warehouseId && task.version !== null && task.version !== undefined && Number.isInteger(Number(task.version)) && task.assignmentVersion !== null && task.assignmentVersion !== undefined && Number.isInteger(Number(task.assignmentVersion)) && ['UNASSIGNED', 'IN_PROGRESS'].includes(task.workflowStatus)) },
|
||||
async loadTasks() {
|
||||
if (!isOnline()) { this.error = '当前网络不可用,请联网后刷新'; return }
|
||||
this.loading = true; this.error = ''; this.notice = ''
|
||||
try {
|
||||
const result = await listLoadingOrders({ page: 1, size: 50 })
|
||||
const records = result.records || []
|
||||
records.filter((task) => this.isMergedTask(task)).forEach((task) => this.clearMergedTaskDraft(task))
|
||||
this.tasks = records.filter((task) => this.isTaskReady(task))
|
||||
if (records.length > this.tasks.length) this.notice = '部分任务尚未完成服务端就绪校验,本机已隐藏,请稍后刷新'
|
||||
} catch (error) {
|
||||
|
||||
@@ -47,6 +47,9 @@ test('picking task list uses remote loading orders and never falls back to demo
|
||||
assert.match(tasks, /集装箱号 \{\{ task\.currentContainerNo \}\}/)
|
||||
assert.match(tasks, /柜次号 \{\{ task\.cabinetNo \}\}/)
|
||||
assert.match(tasks, /records\.filter\(\(task\) => this\.isTaskReady\(task\)\)/)
|
||||
assert.match(tasks, /isTaskReady\(task\)[\s\S]*?task\.recordStatus === 'ACTIVE'[\s\S]*?!task\.mergedIntoContainerBookingId/)
|
||||
assert.match(tasks, /records\.filter\(\(task\) => this\.isMergedTask\(task\)\)\.forEach\(\(task\) => this\.clearMergedTaskDraft\(task\)\)/)
|
||||
assert.match(tasks, /clearMergedTaskDraft\(task\)[\s\S]*?clearPickingDraft\(\{ operatorId, taskId: task\.taskId, loadingPlanId: task\.loadingPlanId, containerBookingId: task\.containerBookingId/)
|
||||
assert.match(tasks, /task\.taskId[\s\S]*?task\.loadingPlanId[\s\S]*?task\.containerBookingId[\s\S]*?task\.currentContainerNo[\s\S]*?task\.version[\s\S]*?task\.assignmentVersion/)
|
||||
assert.doesNotMatch(tasks, /task\.taskStatus === 'LOADING'/)
|
||||
assert.doesNotMatch(tasks, /demo-data|DEMO_/)
|
||||
|
||||
@@ -48,6 +48,13 @@ test('C02 identifiers and separate picking/inbound versions are normalized expli
|
||||
assert.match(page, /状态尚未同步,请刷新重试/)
|
||||
})
|
||||
|
||||
test('PDA normalizes the frozen active and merged container identity fields', () => {
|
||||
for (const field of ['sourceType', 'recordStatus', 'mergedIntoContainerBookingId']) {
|
||||
assert.match(source, new RegExp(`${field}: row\\.${field} \\?\\? row\\.container\\?\\.${field}`))
|
||||
assert.match(source, new RegExp(`${field}: task\\.${field} \\?\\? task\\.container\\?\\.${field}`))
|
||||
}
|
||||
})
|
||||
|
||||
test('picking page exposes five evidence slots, retry and remote completion states', () => {
|
||||
for (const text of ['PHOTO_STEP_1', 'PHOTO_STEP_4', 'SEAL', '网络失败', '确认装柜完成', 'loadingVersion']) {
|
||||
assert.match(`${source}\n${page}`, new RegExp(text))
|
||||
|
||||
@@ -59,6 +59,20 @@ test('evidence identity is bound to task, inbound, loading plan and container bo
|
||||
assert.match(page, /this\.evidenceIdentity = this\.captureEvidenceIdentity\(\)/)
|
||||
})
|
||||
|
||||
test('merged or non-active cabinet details are rejected before claim and clear stale local state', () => {
|
||||
const sync = page.match(/async syncRemoteTask\(\) \{[\s\S]*?\n\t\t\t\},/)?.[0] || ''
|
||||
assert.match(sync, /if \(this\.isMergedContainerRecord\(task\)\)[\s\S]*?this\.clearMergedTask\(task, previousIdentity\)[\s\S]*?return[\s\S]*?task\.recordStatus !== 'ACTIVE'[\s\S]*?claimContainerPickingTask/)
|
||||
const clear = page.match(/clearMergedTask\(task, previousIdentity\) \{[\s\S]*?\n\t\t\t\},/)?.[0] || ''
|
||||
assert.match(clear, /taskId: previousIdentity\?\.taskId \?\? task\?\.taskId/)
|
||||
assert.match(clear, /inboundId: previousIdentity\?\.inboundId \?\? task\?\.inboundId/)
|
||||
assert.match(clear, /loadingPlanId: previousIdentity\?\.loadingPlanId \?\? task\?\.loadingPlanId/)
|
||||
assert.match(clear, /containerBookingId: previousIdentity\?\.containerBookingId \?\? task\?\.containerBookingId/)
|
||||
assert.match(clear, /clearPickingDraft\(staleIdentity\)/)
|
||||
assert.match(clear, /this\.evidenceDrafts = Object\.fromEntries/)
|
||||
assert.match(clear, /this\.evidencePreviewCache = Object\.fromEntries/)
|
||||
for (const reset of ['remoteLoading = false', 'submitting = false', 'remoteTaskId = null', 'loadingVersion = null', 'scanHistory = []', 'currentBox.containerBookingId = null', 'currentBox.currentContainerNo = null', 'resetEvidenceState()']) assert.match(clear, new RegExp(reset.replace(/[.()[\]]/g, '\\$&')))
|
||||
})
|
||||
|
||||
test('server evidence mutations remain closed before the loading stage is ready', () => {
|
||||
assert.match(page, /requireEvidenceStage\(\)[\s\S]*?if \(!this\.pickingCompleted\)[\s\S]*?请先提交拣货完成[\s\S]*?if \(!this\.loadingStageReady\)/)
|
||||
const queue = page.match(/async queueEvidenceUpload\([^)]*\) \{[\s\S]*?\n\t\t\t\},/)?.[0] || ''
|
||||
|
||||
Reference in New Issue
Block a user