@@ -129,14 +129,14 @@
< text class = "evidence-count" : class = "{ complete: evidenceEntries(slot.type).length }" > { { evidenceEntries ( slot . type ) . length ? ` 已拍 ${ evidenceEntries ( slot . type ) . length } 张 ` : '待拍摄' } } < / text >
< / view >
< / view >
< button class = "evidence-btn evidence-add-btn" @tap ="chooseEvidence(slot)" > {{ evidenceUploadLabel ( slot.type ) }} < / button >
< button class = "evidence-btn evidence-add-btn" :disabled = "evidenceReadOnly" @tap ="chooseEvidence(slot)" > {{ evidenceUploadLabel ( slot.type ) }} < / button >
< / view >
< view v-if = "evidenceEntries(slot.type).length" class="evidence-photo-grid" >
< view v-for = "photo in evidenceEntries(slot.type)" :key="photo.id || photo.fileId || photo.localPath" class="evidence-photo-item" >
< image v-if = "evidencePreviewSource(photo)" class="evidence-thumbnail" :src="evidencePreviewSource(photo)" mode="aspectFill" @tap="previewEvidencePhoto(photo, slot.type)" / >
< view v-else class = "evidence-thumbnail evidence-thumbnail-placeholder" @tap ="previewEvidencePhoto(photo, slot.type)" > 图片处理中 < / view >
< text class = "evidence-file" @tap ="previewEvidencePhoto(photo, slot.type)" > {{ photo.fileName | | ' 已记录 ' }} < / text >
< button v-if = "photo.id && photo.status !== 'VOID' && photo.status !== 'VOIDED'" class="evidence-delete-btn" aria-label="删除照片" @tap="deleteEvidencePhoto(photo)" > × < / button >
< button v-if = "!evidenceReadOnly && photo.id && photo.status !== 'VOID' && photo.status !== 'VOIDED'" class="evidence-delete-btn" aria-label="删除照片" @tap="deleteEvidencePhoto(photo)" > × < / button >
< / view >
< / view >
< text v-if = "evidenceQueueEntry(slot.type)" class="evidence-upload-state" :class="evidenceQueueEntry(slot.type).status.toLowerCase()" >
@@ -149,11 +149,12 @@
< / view >
< / view >
< view class = "seal-row" >
< input v-model = "sealNo" class="seal-input" placeholder="输入封签号(必填)" maxlength="64" / >
< button class = "evidence-btn seal-btn" : disabled = "sealSaving || !sealReadyToSave" @tap ="saveSealNo" > {{ sealSaving ? ' 上传中 … ' : ' 保存并上传 ' }} < / button >
< input v-model = "sealNo" :disabled="evidenceReadOnly" class="seal-input" placeholder="输入封签号(必填)" maxlength="64" / >
< button class = "evidence-btn seal-btn" : disabled = "evidenceReadOnly || sealSaving || !sealReadyToSave" @tap ="saveSealNo" > {{ sealSaving ? ' 上传中 … ' : ' 保存并上传 ' }} < / button >
< / view >
< view v-if = "remoteError" class="remote-error-row"><text class="remote-error" role="alert">{{ remoteError }}</text><button class="evidence-retry-btn" :disabled="remoteLoading" @tap="refreshTask" > 刷新任务 < / button > < / view >
< button class = "loading-complete-btn" : disabled = "submitting || !loadingStageReady || pickingVersion === null || !evidenceComplete" @tap ="completeLoadingTask" > 确认装柜完成 < / button >
< text v-if = "evidenceReadOnly" class="version-hint" role="status" > {{ loadingCompletionState.label }} < / text >
< button v-else class = "loading-complete-btn" : disabled = "submitting || !loadingStageReady || pickingVersion === null || !evidenceComplete" @tap ="completeLoadingTask" > 确认装柜完成 < / button >
< view v-if = "pickingVersion === null" class="remote-error-row" >
< text class = "version-hint" role = "status" > 状态尚未同步 , 请刷新重试 < / text >
< button class = "evidence-retry-btn" :disabled = "remoteLoading" @tap ="refreshTask" > 刷新任务 < / button >
@@ -348,7 +349,7 @@
import { isOnline } from '@/utils/network.js'
import { stableOperationId } from '@/utils/idempotency.js'
import { stableTaskOperationIdentity } from '@/utils/task-identity.js'
import { COMPLETION _PHASE , resolveCompletionPhase , shouldReconcileCompletion } from '@/utils/loading-completion-state.js'
import { COMPLETION _PHASE , completionPresentation , resolveCompletionPhase , shouldReconcileCompletion } from '@/utils/loading-completion-state.js'
import { containerReference , formatApiError , sanitizeUserMessage } from '@/utils/user-facing.js'
import ScanTargetInput from '@/components/ScanTargetInput.vue'
import PopupHeader from '@/components/PopupHeader.vue'
@@ -444,6 +445,17 @@
&& Boolean ( this . remoteTaskId && this . currentBox . loadingPlanId && this . currentBox . containerBookingId )
&& this . pickingVersion !== null
} ,
loadingCompletionState ( ) {
const phase = resolveCompletionPhase ( {
taskStatus : this . remotePick ? . workflowStatus || this . remotePick ? . taskStatus ,
commandStatus : this . remotePick ? . completionCommand ? . status || this . remotePick ? . commandStatus ,
submitting : this . submitting
} )
return { ... completionPresentation ( phase ) , phase }
} ,
evidenceReadOnly ( ) {
return this . loadingCompletionState . blocked && ! this . loadingCompletionState . retryable
} ,
loadingStageReady ( ) {
return this . pickingCompleted
&& this . evidenceIdentity
@@ -791,6 +803,10 @@
return false
} ,
requireEvidenceStage ( ) {
if ( this . evidenceReadOnly ) {
this . showToast ( this . loadingCompletionState . label , 'warning' )
return false
}
if ( ! this . pickingCompleted ) {
this . showToast ( '请先提交拣货完成' , 'warning' )
return false