@@ -10,7 +10,7 @@
< / view >
< view v-if = "!current || activeTab === 'picking'" class="fulfillment-card dispatch-scan-card" >
< text class = "card-title" > { { current ? '扫码拣货' : '扫码查询排车' } } < / text >
< ScanTargetInput name = "dispatch" :model-value = "scanCode" label = "排车单号/实物托盘码/来源箱唛" placeholder = "扫描业务单号或实物 码" : active = "activeTab === 'picking'" @ update : modelValue = "scanCode = $event" @submit ="handleScan" / >
< ScanTargetInput name = "dispatch" :model-value = "scanCode" label = "排车单号/实物托盘码/来源箱唛" placeholder = "扫托盘 码" : active = "activeTab === 'picking'" @ update : modelValue = "scanCode = $event" @submit ="handleScan" / >
< button class = "secondary-button" : disabled = "busy || loading || !scanCode" @tap ="handleScan(scanCode)" > 匹配扫描 < / button >
< / view >
< view v-if = "loading" class="fulfillment-state" > < text > 正在加载排车事实 … < / text > < / view >
@@ -32,9 +32,7 @@
< DispatchHandoverStatus v-if = "departed" :dispatch-no="current.dispatchNo" :record-version="current.recordVersion" / >
< view class = "fulfillment-card summary-card" >
< text class = "card-title" > 任务摘要 < / text >
< view class = "task-head" > < text class = "business-code" > { { current . dispatchNo } } < / text > < text class = " status-badge"> { { stageLabel ( current ) } } < / text > < / view >
< text class = "task-meta" > 任务号 { { current . taskNo || '—' } } < / text >
< text v-if = "!gate('PICK') && current.stage === 'LOADING'" class="gate-warning" > {{ current.actions.PICK ? .blockReason | | ' 拣货资格尚未允许 , 请核对两级审核及当前作业状态 ' }} < / text >
< view class = "task-head" > < text class = "status-badge" > { { stageLabel ( current ) } } < / text > < / view >
< text class = "picking-section-label" > 拣货进度 < / text >
< view class = "metrics" aria -label = " 拣货进度 " > < view > < text class = "metric-value" > { { plannedPackages } } < / text > < text > 计划箱数 < / text > < / view > < view > < text class = "metric-value success" > { { pickedPackages } } < / text > < text > 真实已拣 < / text > < / view > < view > < text class = "metric-value warning" > { { remainingPackages } } < / text > < text > 未拣箱数 < / text > < / view > < / view >
< / view >
@@ -186,7 +184,7 @@ export default {
this . sourceChoices = [ ] ; this . choiceCartonCode = '' ; this . error = null
} ,
async submitPick ( ) { if ( ! this . canPick ) return ; try { const body = { recordVersion : requireDispatchVersion ( this . current . recordVersion ) , positionId : this . selectedReturnCarton ? null : this . selectedPosition . positionId , packages : this . selectedReturnCarton ? 1 : Number ( this . packages ) , productQuantity : this . selectedReturnCarton ? . productQuantity || 0 , cartonMark : this . selectedReturnCarton ? . cartonMark || null , remark : null } ; await this . submitCommand ( this . current . dispatchNo , 'PICK' , { ... body , dispatchItemId : this . selectedItem . dispatchItemId } ) } catch ( cause ) { this . setError ( cause , '拣货未提交' ) } } ,
async prepareLoading ( ) { if ( ! this . canComplete ) return ; await this . run ( async ( ) => { const data = await getDispatchLoading ( this . current . dispatchNo ) ; if ( data . dispatchNo !== this . current . dispatchNo || data . pickingReconciled !== true || data . loadingComplete ? . allowed !== true ) throw new Error ( data . loadingComplete ? . blockReason || '装车前置未满足' ) ; requireDispatchVersion ( data . recordVersion ) ; this . loadingPreview = data ; this . loadingRows = data . pallets } ) } ,
async prepareLoading ( ) { if ( ! this . canComplete || this . busy || this . loading ) return ; await this . run ( async ( ) => { const data = await getDispatchLoading ( this . current . dispatchNo ) ; if ( data . dispatchNo !== this . current . dispatchNo || data . pickingReconciled !== true || data . loadingComplete ? . allowed !== true ) throw new Error ( data . loadingComplete ? . blockReason || '装车前置未满足' ) ; requireDispatchVersion ( data . recordVersion ) ; this . loadingPreview = data ; this . loadingRows = data . pallets } ) } ,
async completeLoading ( ) { if ( ! this . loadingPreview || this . busy ) return ; try { if ( ! this . loadingRows . length || this . loadingRows . some ( row => row . deliveryIndex == null || ! Number . isInteger ( row . deliveryIndex ) || row . deliveryIndex < 1 ) ) throw new Error ( '托盘卸货站尚未匹配,请后台核对' ) ; await this . submitCommand ( this . loadingPreview . dispatchNo , 'LOADING_COMPLETE' , { recordVersion : requireDispatchVersion ( this . loadingPreview . recordVersion ) , manifestHash : this . loadingPreview . manifestHash , remark : null } ) } catch ( cause ) { this . setError ( cause , '装车完成未确认' ) } } ,
updateReviewReason ( event ) { if ( this . busy || this . loading ) return ; this . reviewReason = String ( event . detail . value || '' ) } ,
async submitReview ( decision ) {
@@ -228,7 +226,7 @@ export default {
} ,
async retry ( ) { if ( this . current ? . dispatchNo ) await this . openTask ( this . current . dispatchNo ) ; else await this . loadTasks ( ) } ,
async run ( work , loading = false ) { if ( this . busy || this . loading ) return ; this . clearScanNotice ( ) ; this . error = null ; this . successMessage = '' ; if ( loading ) this . loading = true ; else this . requestBusy = true ; try { await work ( ) } catch ( cause ) { this . setError ( cause , '排车操作失败' ) } finally { this . loading = false ; this . requestBusy = false } } ,
setError ( cause , fallback ) { this . clearScanNotice ( ) ; this . error = toDispatchError ( cause , fallback ) ; playErrorSound ( ) } ,
setError ( cause , fallback ) { this . clearScanNotice ( ) ; this . error = toDispatchError ( cause , fallback ) ; playErrorSound ( ) ; if ( typeof uni !== 'undefined' ) uni . showToast ? . ( { title : this . error . message || fallback , icon : 'none' , duration : 3000 } ) } ,
stageLabel ( task ) { if ( task . status === 'CANCELLED' ) return '已取消' ; if ( task . stage === 'BEFORE_LOADING' ) { if ( task . actions ? . LOADING _REVIEW ? . allowed === true ) return '待理货审核' ; if ( [ task . reviews ? . dispatchReview , task . reviews ? . loadingReview ] . some ( review => review ? . status === 'REJECTED' ) ) return '已退回客服处理' } ; return ( { BEFORE _LOADING : '装车前' , LOADING : '装车中' , AFTER _LOADING : '装车后' , DELIVERING : '派送中' , DELIVERED : '派送完成' } ) [ task . stage ] || '状态待核实' } ,
reviewTime ( value ) { if ( ! value ) return '未填写' ; const date = new Date ( new Date ( value ) . getTime ( ) + 8 * 60 * 60 * 1000 ) ; return Number . isNaN ( date . getTime ( ) ) ? '时间无效' : date . toISOString ( ) . slice ( 0 , 16 ) . replace ( 'T' , ' ' ) } ,
reviewLabel ( review ) { return ( { NOT _SUBMITTED : '未提交' , PENDING : '待审核' , APPROVED : '已通过' , REJECTED : '已驳回' } ) [ review ? . status ] || '待核对' } ,
@@ -278,7 +276,7 @@ export default {
. review - actions button { flex : 1 ; min - width : 0 ; margin : 0 ; padding : 0 16 rpx ; min - height : 88 rpx ; line - height : 88 rpx ; font - size : 28 rpx ; }
. review - actions . primary - button { min - height : 88 rpx ; line - height : 88 rpx ; font - size : 28 rpx ; }
. review - card textarea { box - sizing : border - box ; width : 100 % ; height : 160 rpx ; min - height : 160 rpx ; margin - top : 20 rpx ; padding : 16 rpx ; border : 1 px solid # cbd5e1 ; border - radius : 8 rpx ; }
. loading - overlay { position : fixed ; inset : 0 ; background : rgba ( 15 , 23 , 42 , .46 ) ; display : flex ; align - items : center ; justify - content : center ; z - index : 100 ; padding : 40 rpx ; box - sizing : border - box ; }
. loading - overlay { position : fixed ; top : 0 ; right : 0 ; bottom : 0 ; left : 0 ; background : rgba ( 15 , 23 , 42 , .46 ) ; display : flex ; align - items : center ; justify - content : center ; z - index : 100 ; padding : 40 rpx ; box - sizing : border - box ; }
. loading - dialog { width : 100 % ; max - width : 620 rpx ; max - height : 88 vh ; overflow - y : auto ; box - sizing : border - box ; background : # fff ; border - radius : 28 rpx ; padding : 36 rpx 32 rpx 32 rpx ; box - shadow : 0 24 rpx 80 rpx rgba ( 15 , 23 , 42 , .18 ) ; }
. loading - dialog - heading { display : flex ; flex - direction : column ; gap : 10 rpx ; }
. loading - dialog - title { color : # 142 c49 ; font - size : 36 rpx ; font - weight : 700 ; line - height : 1.4 ; }