优化
This commit is contained in:
@@ -347,7 +347,7 @@
|
|||||||
import { ITEM_STATUS, classifyScan, createItemStates, getPickSummary, submitStates } from '@/utils/picking-state.js'
|
import { ITEM_STATUS, classifyScan, createItemStates, getPickSummary, submitStates } from '@/utils/picking-state.js'
|
||||||
import { SKIP_REASONS, INCOMPLETE_REASONS } from '@/data/demo-data.js'
|
import { SKIP_REASONS, INCOMPLETE_REASONS } from '@/data/demo-data.js'
|
||||||
import { session, requireSession } from '@/stores/session.js'
|
import { session, requireSession } from '@/stores/session.js'
|
||||||
import { clearPickingDraft, getPickingSession, restorePickingSession, savePickingSession } from '@/stores/picking.js'
|
import { clearLoadingCompletionAttempt, clearPickingDraft, getPickingSession, restoreLoadingCompletionAttempt, restorePickingSession, saveLoadingCompletionAttempt, savePickingSession } from '@/stores/picking.js'
|
||||||
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'
|
||||||
@@ -356,6 +356,7 @@
|
|||||||
import { isOnline } from '@/utils/network.js'
|
import { isOnline } from '@/utils/network.js'
|
||||||
import { stableOperationId } from '@/utils/idempotency.js'
|
import { stableOperationId } from '@/utils/idempotency.js'
|
||||||
import { stableTaskOperationIdentity } from '@/utils/task-identity.js'
|
import { stableTaskOperationIdentity } from '@/utils/task-identity.js'
|
||||||
|
import { COMPLETION_PHASE, resolveCompletionPhase, shouldReconcileCompletion } from '@/utils/loading-completion-state.js'
|
||||||
import ScanTargetInput from '@/components/ScanTargetInput.vue'
|
import ScanTargetInput from '@/components/ScanTargetInput.vue'
|
||||||
import PopupHeader from '@/components/PopupHeader.vue'
|
import PopupHeader from '@/components/PopupHeader.vue'
|
||||||
|
|
||||||
@@ -365,7 +366,11 @@
|
|||||||
const workflowStatus = task?.workflowStatus
|
const workflowStatus = task?.workflowStatus
|
||||||
const taskStatus = task?.taskStatus ?? task?.status
|
const taskStatus = task?.taskStatus ?? task?.status
|
||||||
return workflowStatus === 'AWAITING_REVIEW'
|
return workflowStatus === 'AWAITING_REVIEW'
|
||||||
|
|| workflowStatus === 'PROCESSING'
|
||||||
|
|| workflowStatus === 'COMPLETION_FAILED'
|
||||||
|| workflowStatus === 'COMPLETED'
|
|| workflowStatus === 'COMPLETED'
|
||||||
|
|| taskStatus === 'COMPLETING'
|
||||||
|
|| taskStatus === 'COMPLETION_FAILED'
|
||||||
|| taskStatus === 'COMPLETED'
|
|| taskStatus === 'COMPLETED'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1390,29 +1395,46 @@
|
|||||||
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 expectedTaskVersion = Number(this.pickingVersion)
|
const savedAttempt = restoreLoadingCompletionAttempt(this.draftIdentity())
|
||||||
|
const expectedTaskVersion = savedAttempt?.expectedTaskVersion ?? Number(this.pickingVersion)
|
||||||
const operationIdentity = stableTaskOperationIdentity(this.draftIdentity())
|
const operationIdentity = stableTaskOperationIdentity(this.draftIdentity())
|
||||||
const operationId = stableOperationId('complete', operationIdentity, {
|
const operationId = savedAttempt?.operationId || stableOperationId('complete', operationIdentity, {
|
||||||
taskId: this.remoteTaskId,
|
taskId: this.remoteTaskId,
|
||||||
expectedTaskVersion,
|
expectedTaskVersion,
|
||||||
loadingPlanId: identity.loadingPlanId,
|
loadingPlanId: identity.loadingPlanId,
|
||||||
containerBookingId: identity.containerBookingId
|
containerBookingId: identity.containerBookingId
|
||||||
})
|
})
|
||||||
|
const completionAttempt = { operationId, expectedTaskVersion, remark: savedAttempt?.remark || 'PDA确认装柜完成' }
|
||||||
|
saveLoadingCompletionAttempt(this.draftIdentity(), completionAttempt)
|
||||||
this.submitting = true
|
this.submitting = true
|
||||||
try {
|
try {
|
||||||
const completedTask = await completeContainerPickingTask(this.remoteTaskId, {
|
const completedTask = await completeContainerPickingTask(this.remoteTaskId, {
|
||||||
expectedTaskVersion,
|
expectedTaskVersion,
|
||||||
operationId,
|
operationId,
|
||||||
remark: 'PDA确认装柜完成'
|
remark: completionAttempt.remark
|
||||||
})
|
})
|
||||||
if (!this.isEvidenceContextActive(contextId, identity)) return
|
if (!this.isEvidenceContextActive(contextId, identity)) return
|
||||||
if (completedTask) {
|
if (completedTask) {
|
||||||
this.remotePick = normalizePickingTask({ ...this.remotePick, ...completedTask })
|
this.remotePick = normalizePickingTask({ ...this.remotePick, ...completedTask })
|
||||||
this.pickingVersion = this.remotePick.pickingVersion ?? this.remotePick.version ?? this.pickingVersion
|
this.pickingVersion = this.remotePick.pickingVersion ?? this.remotePick.version ?? this.pickingVersion
|
||||||
}
|
}
|
||||||
this.showToast('已确认装柜完成', 'success')
|
const phase = resolveCompletionPhase({ taskStatus: completedTask?.taskStatus, commandStatus: completedTask?.commandStatus })
|
||||||
|
if (phase === COMPLETION_PHASE.PROCESSING) {
|
||||||
|
this.showToast('完成命令已受理,后台处理中', 'success')
|
||||||
|
this.persistSession()
|
||||||
|
navigateBackOr('/pages/tasks/tasks')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (phase === COMPLETION_PHASE.COMPLETED) clearLoadingCompletionAttempt(this.draftIdentity())
|
||||||
|
this.showToast('装柜已完成', 'success')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (!this.isEvidenceContextActive(contextId, identity)) return
|
if (!this.isEvidenceContextActive(contextId, identity)) return
|
||||||
|
if (shouldReconcileCompletion(error)) {
|
||||||
|
this.remoteError = '提交结果待确认,请返回任务列表刷新'
|
||||||
|
this.showToast('提交结果待确认', 'warning')
|
||||||
|
navigateBackOr('/pages/tasks/tasks')
|
||||||
|
return
|
||||||
|
}
|
||||||
if (this.isTaskConflict(error)) {
|
if (this.isTaskConflict(error)) {
|
||||||
await this.refreshAfterTaskConflict(error.message || '订舱、装柜关联或证据状态已变化,请刷新后重试')
|
await this.refreshAfterTaskConflict(error.message || '订舱、装柜关联或证据状态已变化,请刷新后重试')
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -46,11 +46,14 @@
|
|||||||
onUnload() { if (this._stopNetwork) this._stopNetwork(); clearTimeout(this._toastTimer) },
|
onUnload() { if (this._stopNetwork) this._stopNetwork(); clearTimeout(this._toastTimer) },
|
||||||
methods: {
|
methods: {
|
||||||
statusLabel(status) {
|
statusLabel(status) {
|
||||||
|
if (status === 'PROCESSING') return '完成处理中'
|
||||||
|
if (status === 'COMPLETION_FAILED') return '完成失败'
|
||||||
if (status === 'AWAITING_REVIEW') return '待上传证据/确认装柜'
|
if (status === 'AWAITING_REVIEW') return '待上传证据/确认装柜'
|
||||||
return status === 'IN_PROGRESS' ? '拣货中' : '待领取'
|
return status === 'IN_PROGRESS' ? '拣货中' : '待领取'
|
||||||
},
|
},
|
||||||
taskActionLabel(task) {
|
taskActionLabel(task) {
|
||||||
if (!this.isTaskReady(task)) return '任务暂不可进入'
|
if (!this.isTaskReady(task)) return '任务暂不可进入'
|
||||||
|
if (task.workflowStatus === 'COMPLETION_FAILED') return '重试完成'
|
||||||
return task.workflowStatus === 'AWAITING_REVIEW' ? '继续装柜取证' : '进入并开始拣货'
|
return task.workflowStatus === 'AWAITING_REVIEW' ? '继续装柜取证' : '进入并开始拣货'
|
||||||
},
|
},
|
||||||
isMergedTask(task) { return task?.recordStatus === 'MERGED' || Boolean(task?.mergedIntoContainerBookingId) },
|
isMergedTask(task) { return task?.recordStatus === 'MERGED' || Boolean(task?.mergedIntoContainerBookingId) },
|
||||||
@@ -58,7 +61,8 @@
|
|||||||
const operatorId = session.currentUser?.id || session.currentUser?.userId || session.currentUser?.accountId
|
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 })
|
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.workflowStatus === 'AWAITING_REVIEW' || (task.assignmentVersion !== null && task.assignmentVersion !== undefined && Number.isInteger(Number(task.assignmentVersion)))) && ['UNASSIGNED', 'IN_PROGRESS', 'AWAITING_REVIEW'].includes(task.workflowStatus)) },
|
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)) && (['AWAITING_REVIEW', 'COMPLETION_FAILED'].includes(task.workflowStatus) || (task.assignmentVersion !== null && task.assignmentVersion !== undefined && Number.isInteger(Number(task.assignmentVersion)))) && ['UNASSIGNED', 'IN_PROGRESS', 'AWAITING_REVIEW', 'COMPLETION_FAILED'].includes(task.workflowStatus)) },
|
||||||
|
isTaskVisible(task) { return Boolean(task.recordStatus === 'ACTIVE' && !task.mergedIntoContainerBookingId && task.taskId && ['UNASSIGNED', 'IN_PROGRESS', 'AWAITING_REVIEW', 'PROCESSING', 'COMPLETION_FAILED'].includes(task.workflowStatus)) },
|
||||||
async loadTasks() {
|
async loadTasks() {
|
||||||
if (!isOnline()) { this.error = '当前网络不可用,请联网后刷新'; return }
|
if (!isOnline()) { this.error = '当前网络不可用,请联网后刷新'; return }
|
||||||
this.loading = true; this.error = ''; this.notice = ''
|
this.loading = true; this.error = ''; this.notice = ''
|
||||||
@@ -66,7 +70,7 @@
|
|||||||
const result = await listLoadingOrders({ page: 1, size: 50 })
|
const result = await listLoadingOrders({ page: 1, size: 50 })
|
||||||
const records = result.records || []
|
const records = result.records || []
|
||||||
records.filter((task) => this.isMergedTask(task)).forEach((task) => this.clearMergedTaskDraft(task))
|
records.filter((task) => this.isMergedTask(task)).forEach((task) => this.clearMergedTaskDraft(task))
|
||||||
this.tasks = records.filter((task) => this.isTaskReady(task))
|
this.tasks = records.filter((task) => this.isTaskVisible(task))
|
||||||
if (records.length > this.tasks.length) this.notice = '部分任务暂不可作业,请稍后刷新'
|
if (records.length > this.tasks.length) this.notice = '部分任务暂不可作业,请稍后刷新'
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.error = error.network ? '当前网络不可用,请联网后刷新' : (error.message || '任务加载失败')
|
this.error = error.network ? '当前网络不可用,请联网后刷新' : (error.message || '任务加载失败')
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ function createInitialState() {
|
|||||||
let state = createInitialState()
|
let state = createInitialState()
|
||||||
|
|
||||||
const STORAGE_PREFIX = 'pda-picking-draft'
|
const STORAGE_PREFIX = 'pda-picking-draft'
|
||||||
|
const COMPLETION_PREFIX = 'pda-loading-completion'
|
||||||
|
|
||||||
function storageAvailable() {
|
function storageAvailable() {
|
||||||
return typeof uni !== 'undefined' && typeof uni.getStorageSync === 'function' && typeof uni.setStorageSync === 'function'
|
return typeof uni !== 'undefined' && typeof uni.getStorageSync === 'function' && typeof uni.setStorageSync === 'function'
|
||||||
@@ -80,6 +81,34 @@ export function clearPickingDraft(identity = {}) {
|
|||||||
if (key && typeof uni !== 'undefined' && typeof uni.removeStorageSync === 'function') uni.removeStorageSync(key)
|
if (key && typeof uni !== 'undefined' && typeof uni.removeStorageSync === 'function') uni.removeStorageSync(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function completionKey(identity = {}) {
|
||||||
|
const identityKey = stableTaskIdentityKey(identity)
|
||||||
|
return identityKey ? `${COMPLETION_PREFIX}:${identityKey}` : ''
|
||||||
|
}
|
||||||
|
|
||||||
|
export function saveLoadingCompletionAttempt(identity = {}, attempt = {}) {
|
||||||
|
const key = completionKey(identity)
|
||||||
|
if (!key || !storageAvailable() || !attempt.operationId) return
|
||||||
|
uni.setStorageSync(key, {
|
||||||
|
operationId: String(attempt.operationId),
|
||||||
|
expectedTaskVersion: Number(attempt.expectedTaskVersion),
|
||||||
|
remark: attempt.remark || '',
|
||||||
|
updatedAt: new Date().toISOString()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function restoreLoadingCompletionAttempt(identity = {}) {
|
||||||
|
const key = completionKey(identity)
|
||||||
|
if (!key || !storageAvailable()) return null
|
||||||
|
const saved = uni.getStorageSync(key)
|
||||||
|
return saved?.operationId && Number.isInteger(Number(saved.expectedTaskVersion)) ? clone(saved) : null
|
||||||
|
}
|
||||||
|
|
||||||
|
export function clearLoadingCompletionAttempt(identity = {}) {
|
||||||
|
const key = completionKey(identity)
|
||||||
|
if (key && typeof uni !== 'undefined' && typeof uni.removeStorageSync === 'function') uni.removeStorageSync(key)
|
||||||
|
}
|
||||||
|
|
||||||
export function resetPickingSession() {
|
export function resetPickingSession() {
|
||||||
state = createInitialState()
|
state = createInitialState()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user