@@ -33,10 +33,10 @@
< / view >
< view class = "stage-switch" role = "tablist" aria -label = " 作业阶段 " >
< button class = "stage-tab" : class = "{ active: activeStage === 'picking' }" role = "tab" : aria -selected = " activeStage = = = ' picking ' " @tap ="selectStage('picking')" > 拣货作业 < / button >
< button class = "stage-tab" : class = "{ active: activeStage === 'evidence', locked: !loadingStageReady }" role = "tab" : aria -selected = " activeStage = = = ' evidence ' " :aria-disabled = "!loadingStageReady" @tap ="selectStage('evidence')" > 装柜取证 < text v-if = "!loadingStageReady" class="stage-tab-hint" > 完成拣货后开放 < / text > < / button >
< button class = "stage-tab" : class = "{ active: activeStage === 'evidence' }" role = "tab" : aria -selected = " activeStage = = = ' evidence ' " @tap ="selectStage('evidence')" > 装柜取证 < text v-if = "!loadingStageReady" class="stage-tab-hint" > 照片可先暂存 < / text > < / button >
< / view >
< view v-if = "activeStage === 'picking' || !loadingStageReady " class="picking-stage" >
< view v-if = "activeStage === 'picking'" class="picking-stage" >
< view class = "scan-section" >
< view class = "scan-row" >
< ScanTargetInput v-model = "scanValue" name="boxCode" label="箱码扫码框" placeholder="扫描或输入箱唛标签码" :active="activeScanField === 'boxCode'" @activate="setScanTarget" @submit="handleManualSubmit" / >
@@ -123,7 +123,7 @@
< / view >
< / view >
< view v-if = "loadingStageReady && activeStage === 'evidence'" class="loading-evidence-section" >
< view v-if = "activeStage === 'evidence'" class="loading-evidence-section" >
< view class = "section-heading-row" >
< view > < text class = "section-title" > 装柜证据 < / text > < text class = "section-subtitle" > 空仓 、 半仓 、 满仓 、 锁仓及带锁码照 < / text > < / view >
< text class = "sync-state" :class = "remoteSyncState.tone" > { { remoteSyncState . label } } < / text >
@@ -132,12 +132,14 @@
< view v-for = "slot in evidenceSlots" :key="slot.type" class="evidence-card" :class="{ filled: evidenceEntries(slot.type).length }" >
< text class = "evidence-label" > { { slot . label } } < / text >
< view v-for = "photo in evidenceEntries(slot.type)" :key="photo.id || photo.fileId || photo.localPath" class="evidence-file-row" >
< text class = "evidence-file" > { { photo . fileName || '已记录' } } < / text >
< image v-if = "evidencePreviewSource(photo)" class="evidence-thumbnail" :src="evidencePreviewSource(photo)" mode="aspectFill" @tap="previewEvidencePhoto(photo, slot.type)" / >
< view class = "evidence-file-detail" @tap ="previewEvidencePhoto(photo, slot.type)" > < text class = "evidence-file" > { { photo . fileName || '已记录' } } < / text > < / view >
< button v-if = "photo.id && photo.status !== 'VOID' && photo.status !== 'VOIDED'" class="evidence-retry-btn" @tap="voidEvidencePhoto(photo)" > 作废 < / button >
< / view >
< text v-if = "evidenceQueueEntry(slot.type)" class="evidence-upload-state" :class="evidenceQueueEntry(slot.type).status.toLowerCase()" >
{{ evidenceUploadState ( slot.type ) }}
< / text >
< text v-else-if = "hasPendingLocalEvidence(slot.type)" class="evidence-upload-state pending" > 本地暂存 , 待自动上传 < / text >
< button class = "evidence-btn" @tap ="chooseEvidence(slot)" > {{ evidenceUploadLabel ( slot.type ) }} < / button >
< button v-if = "evidenceQueueEntry(slot.type)?.status === 'FAILED'" class="evidence-retry-btn" @tap="retryEvidence(slot.type)" > 重试上传 < / button >
< / view >
@@ -146,6 +148,7 @@
< input v-model = "sealNo" class="seal-input" placeholder="输入封签号(必填)" maxlength="64" / >
< button class = "evidence-btn seal-btn" @tap ="saveSealNo" > 保存封签 < / button >
< / view >
< view v-if = "!loadingStageReady" class="evidence-stage-hint" role="status" > 当前可先拍照留存 ; 拣货完成并同步装柜状态后 , 照片会自动上传绑定 。 < / 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 || loadingVersion === null || !evidenceComplete" @tap ="completeLoadingTask" > 确认装柜完成 < / button >
< view v-if = "loadingVersion === null" class="remote-error-row" >
@@ -153,12 +156,12 @@
< button class = "evidence-retry-btn" :disabled = "remoteLoading" @tap ="refreshTask" > 刷新任务 < / button >
< / view >
< / view >
< view v-if = "pickingCompleted && !loadingStageReady" class="remote-error-row submit-error-row" >
< view v-if = "pickingCompleted && !loadingStageReady && activeStage === 'picking' " class="remote-error-row submit-error-row" >
< text class = "version-hint" role = "status" > 拣货已完成 , 装柜状态尚未同步 , 请刷新重试 < / text >
< button class = "evidence-retry-btn" :disabled = "remoteLoading" @tap ="refreshTask" > 刷新任务 < / button >
< / view >
< view v-if = "activeStage === 'picking' || !loadingStageReady " class="submit-section" >
< view v-if = "activeStage === 'picking'" class="submit-section" >
< view class = "submit-info" >
< view class = "submit-pending" > < text class = "submit-pending-number" > { { summary . pending } } < / text > < text class = "submit-pending-label" > 箱待拣 < / text > < / view >
< text class = "submit-status-hint" > { { submitStatusHint } } < / text >
@@ -382,7 +385,7 @@
selectedIncompleteReason : '' ,
completionSummary : { picked : 0 , partial : 0 , skipped : 0 , autoSkipped : 0 } ,
evidenceSlots : EVIDENCE _SLOTS ,
evidenceFiles : { } , evidenceQueueItems : [ ] , evidenceQueue : null , evidenceContextId : 0 , evidenceIdentity : null , cameraPicking : false , sealNo : '' , loadingVersion : null , pickingVersion : null , remotePick : null ,
evidenceFiles : { } , evidenceDrafts : { } , evidencePreviewCache : { } , evidenceQueueItems : [ ] , evidenceQueue : null , evidenceContextId : 0 , evidenceIdentity : null , cameraPicking : false , sealNo : '' , loadingVersion : null , pickingVersion : null , remotePick : null ,
remoteTaskId : null , assignmentVersion : null , submitId : '' ,
taskRequestId : 0 ,
remoteError : '' , remoteLoading : false , submitting : false ,
@@ -524,10 +527,6 @@
} ,
methods : {
selectStage ( stage ) {
if ( stage === 'evidence' && ! this . loadingStageReady ) {
this . showToast ( '请先完成拣货并刷新任务' , 'warning' )
return
}
this . activeStage = stage
} ,
debugEvidence ( event , payload = { } ) {
@@ -563,6 +562,37 @@
return contextId === this . evidenceContextId
&& this . isSameEvidenceIdentity ( this . evidenceIdentity , identity )
} ,
evidenceDraftKey ( identity ) {
return [ 'taskId' , 'inboundId' , 'loadingPlanId' , 'containerBookingId' ] . map ( ( key ) => String ( identity ? . [ key ] ? ? '' ) ) . join ( '|' )
} ,
rememberEvidenceDraft ( identity = this . evidenceIdentity ) {
const key = this . evidenceDraftKey ( identity )
if ( ! key . replace ( /\|/g , '' ) ) return
const files = Object . fromEntries ( Object . entries ( this . evidenceFiles ) . map ( ( [ type , entries ] ) => [ type , ( Array . isArray ( entries ) ? entries : [ entries ] ) . filter ( ( entry ) => entry && ! entry . id && entry . localPath ) . map ( ( entry ) => ( { ... entry } ) ) ] ) . filter ( ( [ , entries ] ) => entries . length ) )
if ( Object . keys ( files ) . length || String ( this . sealNo || '' ) . trim ( ) ) this . evidenceDrafts = { ... this . evidenceDrafts , [ key ] : { files , sealNo : this . sealNo || '' } }
} ,
restoreEvidenceDraft ( identity = this . evidenceIdentity ) {
const draft = this . evidenceDrafts [ this . evidenceDraftKey ( identity ) ]
if ( ! draft ) return
this . evidenceFiles = Object . fromEntries ( Object . entries ( draft . files || { } ) . map ( ( [ type , entries ] ) => [ type , entries . map ( ( entry ) => ( { ... entry } ) ) ] ) )
if ( draft . sealNo ) this . sealNo = draft . sealNo
} ,
removeDraftPhoto ( type , localPath ) {
const key = this . evidenceDraftKey ( this . evidenceIdentity )
const draft = this . evidenceDrafts [ key ]
if ( ! draft ) return
const files = { ... ( draft . files || { } ) }
files [ type ] = ( files [ type ] || [ ] ) . filter ( ( entry ) => entry . localPath !== localPath )
if ( ! files [ type ] . length ) delete files [ type ]
this . evidenceDrafts = Object . keys ( files ) . length || draft . sealNo ? { ... this . evidenceDrafts , [ key ] : { ... draft , files } } : Object . fromEntries ( Object . entries ( this . evidenceDrafts ) . filter ( ( [ draftKey ] ) => draftKey !== key ) )
} ,
requireEvidenceCapture ( ) {
if ( ! this . evidenceIdentity ? . inboundId ) {
this . showToast ( '任务状态尚未同步,请刷新重试' , 'warning' )
return false
}
return true
} ,
requireEvidenceStage ( ) {
if ( ! this . pickingCompleted ) {
this . showToast ( '请先提交拣货完成' , 'warning' )
@@ -765,12 +795,32 @@
this . pickScannedBox ( result . item , value )
} ,
evidenceEntries ( type ) { return Array . isArray ( this . evidenceFiles [ type ] ) ? this . evidenceFiles [ type ] . filter ( ( item ) => item . status !== 'VOID' && item . status !== 'VOIDED' ) : ( this . evidenceFiles [ type ] ? [ this . evidenceFiles [ type ] ] : [ ] ) } ,
evidencePreviewSource ( photo ) { return photo ? . localPath || photo ? . previewUrl || photo ? . fileUrl || photo ? . downloadUrl || photo ? . url || '' } ,
evidencePreviewCacheKey ( fileId , identity = this . evidenceIdentity ) { return ` ${ this . evidenceDraftKey ( identity ) } | ${ String ( fileId || '' ) } ` } ,
cacheEvidencePreview ( fileId , localPath , identity = this . evidenceIdentity ) {
if ( ! fileId || ! localPath ) return
this . evidencePreviewCache = { ... this . evidencePreviewCache , [ this . evidencePreviewCacheKey ( fileId , identity ) ] : localPath }
} ,
withCachedEvidencePreview ( photo , identity = this . evidenceIdentity ) {
if ( ! photo || this . evidencePreviewSource ( photo ) ) return photo
const localPath = this . evidencePreviewCache [ this . evidencePreviewCacheKey ( photo . fileId , identity ) ]
return localPath ? { ... photo , localPath } : photo
} ,
previewEvidencePhoto ( photo , type ) {
const current = this . evidencePreviewSource ( photo )
const urls = this . evidenceEntries ( type ) . map ( ( item ) => this . evidencePreviewSource ( item ) ) . filter ( Boolean )
if ( ! current || ! urls . length ) { this . showToast ( '照片地址尚未同步,请刷新后重试' , 'warning' ) ; return }
if ( typeof uni . previewImage !== 'function' ) { this . showToast ( '当前环境不支持预览照片' , 'warning' ) ; return }
this . debugEvidence ( 'preview.open' , { evidenceType : type , evidenceIdentity : this . evidenceIdentity , count : urls . length } )
uni . previewImage ( { current , urls } )
} ,
evidenceItems ( result ) {
if ( Array . isArray ( result ? . evidences ) ) return result . evidences
return Array . isArray ( result ? . evidences ? . evidences ) ? result . evidences . evidences : [ ]
} ,
validEvidenceEntries ( type ) { return this . evidenceEntries ( type ) . filter ( ( item ) => item . id && item . fileId && item . uploadStatus !== 'PENDING' ) } ,
evidenceEntry ( type ) { const list = this . evidenceEntries ( type ) ; return list . length ? list [ list . length - 1 ] : null } ,
hasPendingLocalEvidence ( type ) { return this . evidenceEntries ( type ) . some ( ( item ) => item . localPath && ! item . fileId ) } ,
evidenceQueueEntry ( type ) {
return [ ... this . evidenceQueueItems ] . reverse ( ) . find ( ( item ) => item . evidenceType === type ) || null
} ,
@@ -780,12 +830,13 @@
if ( item . status === 'PENDING' ) return '本地待上传'
if ( item . status === 'UPLOADING' ) return ` 上传中 ${ item . progress || 0 } % `
if ( item . status === 'FAILED' ) return item . error || '上传失败'
if ( item . status === 'SUCCEEDED' ) return item . attempts > 1 ? '重试成功' : '已上传'
if ( item . status === 'SUCCEEDED' ) return this . validEvidenceEntries ( type ) . length ? '已上传并保存' : ( item . attempts > 1 ? '重试成功' : '已上传' )
if ( item . status === 'REPLACED' ) return '已替换'
return ''
} ,
async syncRemoteTask ( ) {
if ( ! isRemoteReady ( this . currentBox . inboundId || this . currentBox . boxNo ) ) return
this . rememberEvidenceDraft ( )
const requestId = ++ this . taskRequestId
const selector = { taskId : this . remoteTaskId || this . currentBox . taskId , cabinetNo : this . currentBox . cabinetNo , loadingPlanId : this . currentBox . loadingPlanId }
this . currentBox . inboundId = null
@@ -858,6 +909,7 @@
this . pickingVersion = this . remotePick . pickingVersion ? ? this . remotePick . version ? ? null
this . loadingVersion = task . inboundVersion ? ? task . loadingVersion ? ? this . remotePick . inboundVersion ? ? this . remotePick . loadingVersion ? ? null
this . evidenceIdentity = this . captureEvidenceIdentity ( )
this . restoreEvidenceDraft ( )
this . debugSubmit ( 'refresh.ready' , { requestId , workflowStatus : this . remotePick . workflowStatus , taskStatus : this . remotePick . taskStatus , assignmentVersion : this . assignmentVersion , loadingVersion : this . loadingVersion , evidenceIdentity : this . evidenceIdentity } )
this . assignmentVersion = this . remotePick . assignmentVersion ? ? this . assignmentVersion
if ( ! this . pickingCompleted ) this . restoreRemoteDraft ( )
@@ -875,9 +927,11 @@
try {
const evidence = await getEvidence ( identity . inboundId , this . currentBox . tenantId )
if ( ! this . isEvidenceContextActive ( contextId , identity ) ) return
this . evidenceFiles = ( evidence && evidence . evidences ? evidence . evidences : [ ] ) . reduce ( ( all , item ) => { ( all [ item . evidenceType ] || = [ ] ) . push ( item ) ; return all } , { } )
const localFiles = Object . fromEntries ( Object . entries ( this . evidenceFiles ) . map ( ( [ type , entries ] ) => [ type , ( Array . isArray ( entries ) ? entries : [ entries ] ) . filter ( ( entry ) => entry && ! entry . id && entry . localPath ) ] ) )
this . evidenceFiles = this . evidenceItems ( evidence ) . map ( ( item ) => this . withCachedEvidencePreview ( item , identity ) ) . reduce ( ( all , item ) => { ( all [ item . evidenceType ] || = [ ] ) . push ( item ) ; return all } , localFiles )
const seal = this . evidenceEntry ( 'SEAL' )
this . sealNo = seal ? . sealNo || ''
if ( seal ? . sealNo ) this . sealNo = seal . sealNo
await this . flushPendingEvidence ( )
} catch ( error ) {
if ( ! this . isEvidenceContextActive ( contextId , identity ) ) return
this . remoteError = error . network ? '网络失败,装柜证据未加载,请刷新重试' : ( error . message || '装柜证据加载失败' )
@@ -891,13 +945,14 @@
} ,
evidenceUploadLabel ( type ) {
const queued = this . evidenceQueueEntry ( type )
if ( queued ) return queued . status === 'UPLOADING' ? ` 上传中 ${ queued . progress || 0 } % ` : '等待上传'
if ( queued ? . status === 'UPLOADING' ) return ` 上传中 ${ queued . progress || 0 } % `
if ( queued ? . status === 'PENDING' ) return '等待上传'
return this . evidenceEntry ( type ) ? '继续拍照' : '拍照'
} ,
chooseEvidence ( slot ) {
const evidenceType = slot ? . type || null
this . debugEvidence ( 'choose.start' , { evidenceType , evidenceIdentity : this . evidenceIdentity , loadingStageReady : this . loadingStageReady , hasChooseImage : typeof uni . chooseImage === 'function' } )
if ( ! this . requireEvidenceStage ( ) ) { this . debugEvidence ( 'choose.blocked' , { evidenceType , reason : 'stage -not-ready' , evidenceIdentity : this . evidenceIdentity , loadingVersion : this . loadingVersion } ) ; return }
if ( ! this . requireEvidenceCapture ( ) ) { this . debugEvidence ( 'choose.blocked' , { evidenceType , reason : 'identity -not-ready' , evidenceIdentity : this . evidenceIdentity } ) ; return }
if ( ! isOnline ( ) ) { this . debugEvidence ( 'choose.blocked' , { evidenceType , reason : 'offline' } ) ; this . showToast ( '当前网络不可用,请联网后再拍照上传' , 'warning' ) ; return }
if ( typeof uni . chooseImage !== 'function' ) { this . debugEvidence ( 'choose.blocked' , { evidenceType , reason : 'chooseImage-unavailable' } ) ; this . showToast ( '当前环境不支持选择照片' , 'warning' ) ; return }
const contextId = this . evidenceContextId
@@ -912,7 +967,9 @@
this . debugEvidence ( 'camera.selected' , { evidenceType : slot . type , evidenceIdentity : identity } )
const next = { evidenceType : slot . type , fileName : path . split ( '/' ) . pop ( ) || path , localPath : path , uploadStatus : 'PENDING' }
this . evidenceFiles = { ... this . evidenceFiles , [ slot . type ] : [ ... ( this . evidenceFiles [ slot . type ] || [ ] ) , next ] }
this . queueEvidenceUpload ( slot . type , path )
this . rememberEvidenceDraft ( identity )
if ( this . loadingStageReady ) this . queueEvidenceUpload ( slot . type , path )
else this . showToast ( '照片已暂存,装柜状态同步后自动上传' , 'success' )
} , fail : ( error ) => {
this . cameraPicking = false
if ( ! this . isEvidenceContextActive ( contextId , identity ) ) return
@@ -921,7 +978,7 @@
} } )
} ,
async queueEvidenceUpload ( type , path ) {
if ( ! this . requireEvidenceStage ( ) ) return
if ( ! this . loadingStageReady ) return
if ( ! this . evidenceIdentity ? . inboundId || ! isRemoteReady ( this . evidenceIdentity . inboundId ) ) { this . showToast ( '当前会话未连接服务端,证据保留本地' , 'warning' ) ; return }
const contextId = this . evidenceContextId
const identity = { ... this . evidenceIdentity }
@@ -933,9 +990,15 @@
const latest = queue . snapshot ( ) . find ( ( item ) => item . id === entry . id )
this . debugSubmit ( 'evidence.queue.result' , { evidenceType : type , queueId : entry . id , status : latest ? . status || 'missing' , fileId : latest ? . result ? . fileId || null , error : latest ? . error || '' } )
if ( latest ? . status !== 'SUCCEEDED' ) { this . remoteError = latest ? . error || '文件上传失败' ; return }
const current = this . evidenceEntry ( type ) || { }
this . evidenceFiles = { ... this . evidenceFiles , [ type ] : [ ... ( this . evidenceFiles [ type ] || [ ] ) . slice ( 0 , - 1 ) , { ... current , fileId : latest . result ? . fileId , fileName : latest . result ? . fileName || current . fileName , uploadStatus : 'SUCCEEDED' } ] }
await this . saveEvidence ( type )
const entries = this . evidenceFiles [ type ] || [ ]
const current = entries . find ( ( item ) => item . localPath === path ) || { }
this . evidenceFiles = { ... this . evidenceFiles , [ type ] : entries . map ( ( item ) => item . localPath === path ? { ... item , fileId : latest . result ? . fileId , fileName : latest . result ? . fileName || item . fileName , uploadStatus : 'SUCCEEDED' } : item ) }
await this . saveEvidence ( type , null , path )
} ,
async flushPendingEvidence ( ) {
if ( ! this . loadingStageReady ) return
const pending = this . evidenceSlots . flatMap ( ( slot ) => this . evidenceEntries ( slot . type ) . filter ( ( entry ) => entry . localPath && ! entry . fileId ) . map ( ( entry ) => ( { type : slot . type , path : entry . localPath } ) ) )
for ( const item of pending ) await this . queueEvidenceUpload ( item . type , item . path )
} ,
async retryEvidence ( type ) {
if ( ! this . requireEvidenceStage ( ) ) return
@@ -953,7 +1016,7 @@
this . evidenceFiles = { ... this . evidenceFiles , [ type ] : [ ... ( this . evidenceFiles [ type ] || [ ] ) . slice ( 0 , - 1 ) , { ... current , fileId : latest . result ? . fileId , fileName : latest . result ? . fileName || current . fileName , uploadStatus : 'SUCCEEDED' } ] }
await this . saveEvidence ( type )
} ,
async saveEvidence ( type , operationId = null ) {
async saveEvidence ( type , operationId = null , localPath = null ) {
if ( ! this . requireEvidenceStage ( ) ) return
if ( ! this . evidenceIdentity ? . inboundId ) { this . showToast ( '状态尚未同步,请刷新重试' , 'warning' ) ; return }
if ( ! isRemoteReady ( this . evidenceIdentity . inboundId ) ) { this . showToast ( '当前会话未连接服务端,证据保留本地' , 'warning' ) ; return }
@@ -961,7 +1024,7 @@
const identity = { ... this . evidenceIdentity }
const inboundId = identity . inboundId
const tenantId = this . currentBox . tenantId
const current = this . evidenceEntry ( type ) || { }
const current = ( localPath ? this . evidenceEntries ( type ) . find ( ( item ) => item . localPath === localPath ) : null ) || this . evidenceEntry ( type ) || { }
if ( ! current . fileId ) { this . showToast ( '照片尚未上传完成' , 'warning' ) ; return }
const payload = { expectedVersion : current . version || 0 , evidenceType : type , fileId : current . fileId || null , fileName : current . fileName || null , sealNo : type === 'SEAL' ? this . sealNo : null , remark : null , operationId : operationId || ` evidence- ${ identity . taskId || identity . inboundId } - ${ type } - ${ current . fileId || 'seal' } ` }
this . debugSubmit ( 'evidence.bind.start' , { evidenceType : type , fileId : current . fileId , expectedVersion : payload . expectedVersion , evidenceIdentity : identity } )
@@ -969,7 +1032,10 @@
const result = await upsertEvidence ( inboundId , payload , tenantId )
if ( ! this . isEvidenceContextActive ( contextId , identity ) ) return
this . debugSubmit ( 'evidence.bind.success' , { evidenceType : type , fileId : current . fileId , evidenceCount : result ? . evidences ? . length || 0 , evidenceIdentity : identity } )
this . evidenceFiles = this . evidenceItems ( result ) . reduce ( ( all , item ) => { const list = ( all [ item . evidenceType ] || [ ] ) . filter ( ( entry ) => entry . id || entry . fileId !== item . fileId ) ; const index = list . findIndex ( ( entry ) => entry . id === item . id ) ; all [ item . evidenceType ] = index >= 0 ? list . map ( ( entry , i ) => i === index ? item : entry ) : [ ... list , item ] ; return all } , this . evidenceFiles )
this . cacheEvidencePreview ( current . fileId , current . localPath , identity )
this . evidenceFiles = this . evidenceItems ( result ) . map ( ( item ) => this . withCachedEvidencePreview ( item , identity ) ) . reduce ( ( all , item ) => { const list = ( all [ item . evidenceType ] || [ ] ) . filter ( ( entry ) => entry . id || entry . fileId !== item . fileId ) ; const index = list . findIndex ( ( entry ) => entry . id === item . id ) ; all [ item . evidenceType ] = index >= 0 ? list . map ( ( entry , i ) => i === index ? item : entry ) : [ ... list , item ] ; return all } , this . evidenceFiles )
if ( current . localPath ) this . evidenceFiles = { ... this . evidenceFiles , [ type ] : ( this . evidenceFiles [ type ] || [ ] ) . filter ( ( entry ) => entry . id || entry . localPath !== current . localPath ) }
this . removeDraftPhoto ( type , current . localPath )
this . showToast ( ` ${ type === 'SEAL' ? '封签' : '照片' } 已保存 ` , 'success' )
} catch ( error ) {
if ( ! this . isEvidenceContextActive ( contextId , identity ) ) return
@@ -978,8 +1044,9 @@
}
} ,
async saveSealNo ( ) {
if ( ! this . requireEvidenceStage ( ) ) return
if ( ! String ( this . sealNo || '' ) . trim ( ) ) { this . showToast ( '请输入封签号' , 'warning' ) ; return }
this . rememberEvidenceDraft ( )
if ( ! this . loadingStageReady ) { this . showToast ( '封签号已暂存,装柜状态同步后自动保存' , 'success' ) ; return }
await this . saveEvidence ( 'SEAL' , ` seal-no- ${ this . evidenceIdentity ? . taskId || this . evidenceIdentity ? . inboundId } - ${ Date . now ( ) } ` )
} ,
async voidEvidencePhoto ( photo ) {
@@ -1183,7 +1250,11 @@
this . debugSubmit ( 'submit.batch' , { localItemIds : this . currentBox . items . map ( ( item ) => item . id ) , actionItemIds : actions . map ( ( action ) => action . itemId ) , actionCount : actions . length } )
const submittedTask = await submitContainerPickingBatch ( { taskId : this . remoteTaskId , loadingPlanId : this . currentBox . loadingPlanId , containerBookingId : this . currentBox . containerBookingId , version : Number ( this . pickingVersion ) } , actions , { warehouseId , operationId : submitId , inventorySnapshotToken : inventorySnapshot ? . snapshotToken } )
this . debugSubmit ( 'submit.success' , { itemCount : actions . length , workflowStatus : submittedTask ? . workflowStatus || null , taskStatus : submittedTask ? . taskStatus || null } )
if ( submittedTask ) this . remotePick = normalizePickingTask ( { ... this . remotePick , ... submittedTask } )
if ( submittedTask ) {
this . remotePick = normalizePickingTask ( { ... this . remotePick , ... submittedTask } )
this . loadingVersion = submittedTask . inboundVersion ? ? submittedTask . loadingVersion ? ? this . remotePick . inboundVersion ? ? this . remotePick . loadingVersion ? ? this . loadingVersion
this . evidenceIdentity = this . captureEvidenceIdentity ( )
}
// Legacy inbound completePicking(expectedVersion: Number(this.pickingVersion)) remains adapter-only; cabinet tasks use item actions.
} catch ( error ) {
this . debugSubmit ( 'submit.error' , { message : error . message || '' , code : error . code || '' , statusCode : error . statusCode || '' , requestIdFromServer : error . requestId || '' , traceId : error . traceId || '' , network : Boolean ( error . network ) } )