@@ -36,20 +36,25 @@
< / view >
< / view >
< view v-if = "current.stage === 'BEFORE_LOADING'" class="fulfillment-card review-card" >
< view v-if = "current.stage === 'BEFORE_LOADING'" class="fulfillment-card review-card" >
< text class = "card-title" > 理货员审核 < / text >
< text class = "card-title" > 理货员审核 < / text >
< text class = "task-meta" > 车牌号 : { { current . resources ? . vehicle ? . plate || '—' } } · 托车公司 : { { current . resources ? . vehicle ? . carrierName || '—' } } < / text >
< view class = "review-facts" >
< text class = "task-meta" > 司机 : { { current . resources ? . driver ? . name || '—' } } · 电话 : { { current . resources ? . driver ? . phone || '—' } } < / text >
< view class = "review-fact" > < text class = "review-label" > 车牌号 < / text > < text > { { current . resources ? . vehicle ? . plate || '—' } } < / text > < / view >
< text class = "task-meta" > 计划发车 ( 北京时间 ) : { { reviewTime ( current . plannedDepartureAt ) } } < / text >
< view class = "review-fact" > < text class = "review-label" > 托车公司 < / text > < text > { { current . resources ? . vehicle ? . carrierName || '—' } } < / text > < / view >
< text class = "task-meta" > 预计送达 ( 北京时间 ) : { { reviewTime ( current . estimatedArrivalAt ) } } < / text >
< view class = "review-fact" > < text class = "review-label" > 司机 < / text > < text > { { current . resources ? . driver ? . name || '—' } } < / text > < / view >
< text v-for = "appointment in current.fbaAppointments || []" :key="appointment.destinationKey" class="task-meta "> {{ appointment.destinationWarehouseCode }} · {{ appointment.appointmentNo | | ' — ' }} · {{ appointment.status = = = ' SUCCESS ' ? ' 已确认 ' : appointment.status = = = ' REGISTERED ' ? ' 已登记 ' : ' 未填写 ' }} : {{ reviewTime ( appointment.confirmedAt ) }} ( 北京时间 ) < / text >
< view class = "review-fact" > < text class = "review-label "> 电话 < / text > < text > { { current . resources ? . driver ? . phone || '—' } } < / text > < / view >
< text class = "task-meta" > 驾驶证号 : { { current . resources ? . driver ? . evidence ? . licenseNo || '—' } } · 有效期 : { { current . resources ? . driver ? . evidence ? . licenseExpiresOn || '—' } } < / text >
< view class = "review-fact" > < text class = "review-label" > 计划发车 < / text > < text > { { reviewTime ( current . plannedDepartureAt ) } } ( 北京时间 ) < / text > < / view >
< text class = "task-meta" > 调度审核 : { { reviewLabel ( current . reviews ? . dispatchReview ) } } · { { current . reviews ? . dispatchReview ? . reviewedAt || '—' } } < / text >
< view class = "review-fact" > < text class = "review-label" > 预计送达 < / text > < text > { { reviewTime ( current . estimatedArrivalAt ) } } ( 北京时间 ) < / text > < / view >
< text v-if = "current.reviews?.dispatchReview?.reason" class="task-meta" > 调度意见 : {{ current.reviews.dispatchReview.reason }} < / text >
< view v-for = "appointment in current.fbaAppointments || []" :key="appointment.destinationKey" class="review-fact"><text class="review-label">{{ appointment.destinationWarehouseCode }}</text><view class="review-appointment" > < text > { { appointment . appointmentNo || '—' } } < / text > < text > { { appointment . status === 'SUCCESS' ? '已确认' : appointment . status === 'REGISTERED' ? '已登记' : '未填写' } } : { { reviewTime ( appointment . confirmedAt ) } } ( 北京时间 ) < / text > < / view > < / view >
< view class = "review-fact" > < text class = "review-label" > 驾驶证号 < / text > < text > { { current . resources ? . driver ? . evidence ? . licenseNo || '—' } } < / text > < / view >
< view class = "review-fact" > < text class = "review-label" > 有效期 < / text > < text > { { current . resources ? . driver ? . evidence ? . licenseExpiresOn || '—' } } < / text > < / view >
< view class = "review-fact" > < text class = "review-label" > 调度审核 < / text > < text > { { reviewLabel ( current . reviews ? . dispatchReview ) } } · { { reviewTime ( current . reviews ? . dispatchReview ? . reviewedAt ) } } < / text > < / view >
< view v-if = "current.reviews?.dispatchReview?.reason" class="review-fact"><text class="review-label" > 调度意见 < / text > < text > { { current . reviews . dispatchReview . reason } } < / text > < / view >
< / view >
< text v-if = "rejectionReason" class="gate-warning" > 已退回客服处理 ( 车辆信息 ) : {{ rejectionReason }} 。 修改后须重新提交审核 。 < / text >
< text v-if = "rejectionReason" class="gate-warning" > 已退回客服处理 ( 车辆信息 ) : {{ rejectionReason }} 。 修改后须重新提交审核 。 < / text >
< template v-if = "canReview" >
< template v-if = "canReview" >
< text v-if = "current.actions.LOADING_REVIEW?.blockCode === 'APPROVAL_BLOCKED'" class="gate-warning" > {{ current.actions.LOADING_REVIEW.blockReason }} < / text >
< text v-if = "current.actions.LOADING_REVIEW?.blockCode === 'APPROVAL_BLOCKED'" class="gate-warning" > {{ current.actions.LOADING_REVIEW.blockReason }} < / text >
< text class = "task-meta" > 请核对车辆 、 司机及下方货物 ; 通过后才能开始真实拣货 。 驳回将回到客服处理 ( 车辆信息 ) 。 < / text >
< text class = "task-meta" > 请核对车辆 、 司机及下方货物 ; 通过后才能开始真实拣货 。 驳回将回到客服处理 ( 车辆信息 ) 。 < / text >
< textarea :value = "reviewReason" maxlength = "1000" placeholder = "审核意见(驳回时必填)" : disabled = "busy || loading" @input ="updateReviewReason" / >
< textarea :value = "reviewReason" maxlength = "1000" placeholder = "审核意见(驳回时必填)" : disabled = "busy || loading" @input ="updateReviewReason" / >
< view class = "count-row " > < button : disabled = "busy || loading || !reviewReason.trim()" @tap ="submitReview('REJECTED')" > 驳回至客服处理 < / button > < button class = "primary-button" : disabled = "busy || loading || current.actions.LOADING_REVIEW?.blockCode === 'APPROVAL_BLOCKED'" @tap ="submitReview('APPROVED')" > 审核通过 < / button > < / view >
< view class = "review-actions " > < button : disabled = "busy || loading || !reviewReason.trim()" @tap ="submitReview('REJECTED')" > 驳回 < / button > < button class = "primary-button" : disabled = "busy || loading || current.actions.LOADING_REVIEW?.blockCode === 'APPROVAL_BLOCKED'" @tap ="submitReview('APPROVED')" > 审核通过 < / button > < / view >
< / template >
< / template >
< text v-else-if = "!rejectionReason" class="gate-warning" > {{ current.actions.LOADING_REVIEW ? .blockReason | | ' 当前不具备理货审核资格 , 请先完成客服提交和调度审核 ' }} < / text >
< text v-else-if = "!rejectionReason" class="gate-warning" > {{ current.actions.LOADING_REVIEW ? .blockReason | | ' 当前不具备理货审核资格 , 请先完成客服提交和调度审核 ' }} < / text >
< / view >
< / view >
@@ -72,16 +77,15 @@
< view v-else class = "picking-quantity" > < text class = "picking-instruction" > 拣货箱数 < / text > < input type = "number" aria -label = " 本次拣货箱数 " :value = "packages" : placeholder = "`请输入 1-${selectedRemaining} 箱`" :disabled = "busy" @input ="updatePackages" / > < text class = "task-meta" > 本次最多可拣 { { selectedRemaining } } 箱 < / text > < / view >
< view v-else class = "picking-quantity" > < text class = "picking-instruction" > 拣货箱数 < / text > < input type = "number" aria -label = " 本次拣货箱数 " :value = "packages" : placeholder = "`请输入 1-${selectedRemaining} 箱`" :disabled = "busy" @input ="updatePackages" / > < text class = "task-meta" > 本次最多可拣 { { selectedRemaining } } 箱 < / text > < / view >
< button class = "primary-button" : disabled = "!canPick || busy" @tap ="submitPick" > 确认真实拣货 < / button >
< button class = "primary-button" : disabled = "!canPick || busy" @tap ="submitPick" > 确认真实拣货 < / button >
< / view >
< / view >
< view v-if = "current.stage === 'LOADING'" class="fulfillment-card gate-card picking-completion"><text class="card-title">确认装车完成</text><text class="picking-instruction">{{ current.pickingReconciled ? 'PDA拣货已核平,可边装车边上传5张装车照片;确认装车完成且照片齐全后自动进入派送中。' : '先完成全部真实拣货,不能补录数量代替核平。' }}</text><text v-if="!gate('LOADING_COMPLETE')" class="gate-warning">{{ current.actions.LOADING_COMPLETE?.blockReason || '尚未获得装车完成资格' }}</text><button class="primary-button" :disabled="!canComplete || busy" @tap="prepareLoading" > 确认装车完成 < / button > < / view >
< view v-if = "current.stage === 'LOADING'" class="fulfillment-card gate-card picking-completion"><text v-if="!gate('LOADING_COMPLETE')" class="gate-warning">{{ current.actions.LOADING_COMPLETE?.blockReason || '尚未获得装车完成资格' }}</text><button class="primary-button" :disabled="!canComplete || busy" @tap="prepareLoading" > 确认装车完成 < / button > < / view >
< / view >
< / view >
< / template > < view v-if = "successMessage" class="fulfillment-success" role="status" > {{ successMessage }} < / view >
< / template > < view v-if = "successMessage" class="fulfillment-success" role="status" > {{ successMessage }} < / view >
< / view > < / scroll -view >
< / view > < / scroll -view >
< view v-if = "loadingPreview" class="loading-overlay" >
< view v-if = "loadingPreview" class="loading-overlay" >
< view class = "loading-dialog" role = "dialog" aria -modal = " true " aria -labelledby = " loading -confirm -title " >
< view class = "loading-dialog" role = "dialog" aria -modal = " true " aria -labelledby = " loading -confirm -title " >
< view class = "loading-dialog-heading" > < text id = "loading-confirm-title" class = "loading-dialog-title" > 确认装车完成 < / text > < text class = "loading-dialog-number" > { { loadingPreview . dispatchNo } } < / text > < / view >
< view class = "loading-dialog-heading" > < text id = "loading-confirm-title" class = "loading-dialog-title" > 确认装车 < / text > < / view >
< view class = "loading-counts" > < view class = "loading-count" > < text class = "loading-count-value" > { { loadingRows . length } } < text class = "loading-count-unit" > 托 < / text > < / text > < text class = "loading-count-label" > 已拣托盘 < / text > < / view > < view class = "loading-count" > < text class = "loading-count-value" > { { loadingRows . reduce ( ( sum , row ) => sum + Number ( row . pickedPackages || 0 ) , 0 ) } } < text class = "loading-count-unit" > 箱 < / text > < / text > < text class = "loading-count-label" > 已拣箱数 < / text > < / view > < / view >
< view class = "loading-counts" > < view class = "loading-count" > < text class = "loading-count-value" > { { loadingRows . length } } < text class = "loading-count-unit" > 托 < / text > < / text > < text class = "loading-count-label" > 已拣托盘 < / text > < / view > < view class = "loading-count" > < text class = "loading-count-value" > { { loadingRows . reduce ( ( sum , row ) => sum + Number ( row . pickedPackages || 0 ) , 0 ) } } < text class = "loading-count-unit" > 箱 < / text > < / text > < text class = "loading-count-label" > 已拣箱数 < / text > < / view > < / view >
< text class = "loading-confirm-question " > 本车货物已全部装车 ? < / text > < text class = "loading-confirm-note " > 照片可在 “ 装车拍照 ” 中继续上传 < / text >
< view class = "loading-dialog-actions" > < button class = "loading-cancel" :disabled = "busy" @tap ="loadingPreview = null "> 取消 < / button > < button class = "loading-confirm" :disabled = "busy" :loading = "busy" @tap ="completeLoading "> 确认 < / button > < / view >
< view class = "loading-dialog-actions" > < button class = "loading-cancel" :disabled = "busy" @tap ="loadingPreview = null" > 取消 < / button > < button class = "loading-confirm" :disabled = "busy" :loading = "busy" @tap ="completeLoading" > 确认装车 < / button > < / view >
< / view >
< / view >
< / view >
< / view >
< / view >
< / view >
@@ -244,20 +248,25 @@ export default {
. stage - tab : : after { border : 0 ; }
. stage - tab : : after { border : 0 ; }
. stage - tab . active { color : # 1262 f4 ; background : # fff ; box - shadow : 0 1 px 4 px rgba ( 31 , 41 , 55 , .12 ) ; }
. stage - tab . active { color : # 1262 f4 ; background : # fff ; box - shadow : 0 1 px 4 px rgba ( 31 , 41 , 55 , .12 ) ; }
. stage - tab [ disabled ] { opacity : .55 ; }
. stage - tab [ disabled ] { opacity : .55 ; }
. review - card textarea { box - sizing : border - box ; width : 100 % ; min - height : 160 rpx ; padding : 16 rpx ; border : 1 px solid # cbd5e1 ; border - radius : 8 rpx ; }
. review - facts { margin : 16 rpx 0 ; }
. review - fact { display : flex ; align - items : flex - start ; gap : 20 rpx ; padding : 10 rpx 0 ; color : # 243 c58 ; font - size : 26 rpx ; line - height : 1.5 ; }
. review - label { flex : 0 0 132 rpx ; color : # 718095 ; overflow - wrap : anywhere ; }
. review - fact > : last - child { flex : 1 ; min - width : 0 ; overflow - wrap : anywhere ; }
. review - appointment { display : flex ; flex - direction : column ; gap : 4 rpx ; }
. review - actions { display : flex ; gap : 20 rpx ; margin - top : 24 rpx ; }
. 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 ; 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 - 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 { 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 - 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 ; }
. loading - dialog - title { color : # 142 c49 ; font - size : 36 rpx ; font - weight : 700 ; line - height : 1.4 ; }
. loading - dialog - number { color : # 7 a8aa0 ; font - size : 26 rpx ; line - height : 1.5 ; overflow - wrap : anywhere ; }
. loading - counts { display : flex ; margin : 28 rpx 0 ; padding : 24 rpx 0 ; border - radius : 20 rpx ; background : # f3f7ff ; }
. loading - counts { display : flex ; margin : 28 rpx 0 ; padding : 24 rpx 0 ; border - radius : 20 rpx ; background : # f3f7ff ; }
. loading - count { flex : 1 ; display : flex ; align - items : center ; flex - direction : column ; gap : 8 rpx ; }
. loading - count { flex : 1 ; display : flex ; align - items : center ; flex - direction : column ; gap : 8 rpx ; }
. loading - count + . loading - count { border - left : 1 px solid # dce6f6 ; }
. loading - count + . loading - count { border - left : 1 px solid # dce6f6 ; }
. loading - count - value { color : # 1764 ec ; font - size : 48 rpx ; font - weight : 700 ; line - height : 1.2 ; }
. loading - count - value { color : # 1764 ec ; font - size : 48 rpx ; font - weight : 700 ; line - height : 1.2 ; }
. loading - count - unit { font - size : 26 rpx ; font - weight : 500 ; }
. loading - count - unit { font - size : 26 rpx ; font - weight : 500 ; }
. loading - count - label { color : # 72839 b ; font - size : 24 rpx ; }
. loading - count - label { color : # 72839 b ; font - size : 24 rpx ; }
. loading - confirm - question { display : block ; color : # 253 a54 ; font - size : 30 rpx ; font - weight : 600 ; line - height : 1.5 ; }
. loading - confirm - note { display : block ; color : # 7 a8aa0 ; font - size : 25 rpx ; line - height : 1.5 ; margin - top : 10 rpx ; }
. loading - dialog - actions { display : flex ; gap : 20 rpx ; margin - top : 32 rpx ; }
. loading - dialog - actions { display : flex ; gap : 20 rpx ; margin - top : 32 rpx ; }
. loading - dialog - actions button { flex : 1 ; min - width : 0 ; margin : 0 ; height : 88 rpx ; line - height : 88 rpx ; padding : 0 12 rpx ; border - radius : 16 rpx ; font - size : 30 rpx ; font - weight : 600 ; }
. loading - dialog - actions button { flex : 1 ; min - width : 0 ; margin : 0 ; height : 88 rpx ; line - height : 88 rpx ; padding : 0 12 rpx ; border - radius : 16 rpx ; font - size : 30 rpx ; font - weight : 600 ; }
. loading - dialog - actions button : : after { border : 0 ; }
. loading - dialog - actions button : : after { border : 0 ; }