@@ -3,7 +3,7 @@
< PortalHeader : title = "inboundMode ? '拆柜收货' : operationName" :user = "user" / >
< view class = "portal-content receiving-content" : class = "{ 'receiving-content-with-action': phase === 'detail' || phase === 'sign' || (phase === 'receive' && canFinishOnsite) }" >
< template v-if = "inboundMode" >
< view v-if = "phase !== 'candidates' && phase !== 'receive'" class="receiving-heading"><view><text class="receiving-title">{{ pageTitle }}</text><text v-if="pageSubtitle" class="receiving-subtitle" > {{ pageSubtitle }} < / text > < / view > < / view >
< view v-if = "phase !== 'candidates' && phase !== 'receive'" class="receiving-heading"><view><text class="receiving-title" > {{ pageTitle }} < / text > < / view > < / view >
< view v-if = "loading" class="state-card"><uni-load-more status="loading" / > < text > 正在恢复当前柜作业 … < / text > < / view >
@@ -42,7 +42,7 @@
< template v-else-if = "phase === 'sign'" >
< ReceivingSessionSummary :session = "current" / >
< view class = "photo-card domestic-photo-card" >
< view class = "card-head" > < view > < text class = "card-title" > 国内装柜照片 ( 只读 ) < / text > < text class = "container-meta" > 现场对照国内装柜过程与封柜状态 < / text > < / view > < text class = "status-tag status-good" > { { domesticPhotoCount } } / 5 < / text > < / view >
< view class = "card-head" > < view > < text class = "card-title" > 国内装柜照片 ( 只读 ) < / text > < / view > < text class = "status-tag status-good" > { { domesticPhotoCount } } / 5 < / text > < / view >
< view class = "domestic-photo-grid" >
< view v-for = "photo in domesticLoadingPhotos" :key="photo.evidenceType" class="domestic-photo-item" @tap="previewDomesticPhoto(photo)" >
< image v-if = "photo.localPath" class="domestic-photo-thumb" :src="photo.localPath" mode="aspectFill" @error="invalidatePhotoPreview(photo)" / >
@@ -52,7 +52,7 @@
< / view >
< / view >
< view class = "photo-card" >
< view class = "card-head" > < view > < text class = "card-title" > 卸货签收照片 < / text > < text class = "container-meta" > 三个固定槽位各拍摄 1 张现场照片 < / text > < / view > < text class = "status-tag" : class = "photosReady ? 'status-good' : 'status-wait'" > { { uploadedPhotoCount } } / 3 < / text > < / view >
< view class = "card-head" > < view > < text class = "card-title" > 卸货签收照片 < / text > < / view > < text class = "status-tag" : class = "photosReady ? 'status-good' : 'status-wait'" > { { uploadedPhotoCount } } / 3 < / text > < / view >
< view class = "photo-grid" >
< view v-for = "(step, index) in photoSteps" :key="step.role" class="photo-role" :class="{ complete: photoSlots[index]?.fileId }" >
< view class = "photo-preview" : class = "{ failed: photoSlots[index]?.status === 'error' }" @tap ="previewPhoto(index)" >
@@ -61,7 +61,7 @@
< / view >
< view class = "photo-role-info" >
< view class = "photo-role-head" > < text > { { step . label } } < / text > < text : class = "photoSlots[index]?.fileId ? 'photo-valid' : 'photo-required'" > { { photoSlots [ index ] ? . fileId ? '已上传' : '必传' } } < / text > < / view >
< text class = "photo-state" > { { photoStateLabel ( photoSlots [ index ] ) } } < / text >
< text v-if = "photoSlots[index]?.status === 'uploading' || photoSlots[index]?.status === 'error'" class=" photo-state" > {{ photoStateLabel ( photoSlots [ index ] ) }} < / text >
< text v-if = "photoSlots[index]?.status === 'error'" class="photo-error" > {{ photoSlots [ index ] .errorMessage | | ' 请重试 ' }} < / text >
< / view >
< view class = "photo-actions" > < button :disabled = "busy" @tap ="choosePhoto(index)" > {{ photoSlots [ index ] ? .fileId ? ' 重拍 ' : ' 拍照 ' }} < / button > < / view >
@@ -160,8 +160,7 @@
canSubmitReceipt ( ) { if ( ! this . activeInbound || ! this . resolvedPallet ? . compatible ) return false ; if ( this . activeInbound . destinationType === 'FBA' ) { const count = Number ( this . packageCount ) ; return Number . isInteger ( count ) && count > 0 } return Boolean ( this . activeInbound . cartonMark ) } ,
allInboundsReconciled ( ) { const values = this . current ? . inbounds || [ ] ; return values . length > 0 && ! this . current ? . hasDifference && values . every ( ( item ) => Number ( item . pendingPackages ) === 0 ) } ,
canFinishOnsite ( ) { return Boolean ( this . current && [ 'SIGNED' , 'RECEIVING' ] . includes ( this . current . status ) ) } ,
pageTitle ( ) { return { candidates : '待卸货集装箱' , detail : '柜号详情' , sign : '拆柜拍照签收' , receive : '当前柜收货' , completed : '入库完成' } [ this . phase ] } ,
pageSubtitle ( ) { return { candidates : '选择集装箱后拍摄 3 张照片完成签收' , detail : '' , sign : '完成现场取证后进入收货' , receive : '扫描箱码定位入仓码后开始收货' , completed : '' } [ this . phase ] }
pageTitle ( ) { return { candidates : '待卸货集装箱' , detail : '柜号详情' , sign : '拆柜拍照签收' , receive : '当前柜收货' , completed : '入库完成' } [ this . phase ] }
} ,
onLoad ( options ) { this . moduleKey = options . module || 'query' ; const key = options . op || ( this . moduleKey === 'inbound' ? 'receiving' : 'inventoryQuery' ) ; this . operationName = ( { receiving : '拆柜收货' , stocktake : '库存盘点' , move : '商品移库' , pallet : '托盘管理' , relabelReceive : '换标列表' , relabelExecute : '换标执行' , relabelReview : '换标复核' , inventoryQuery : '库存查询' , locationQuery : '库位查询' , palletQuery : '托盘查询' } ) [ key ] || '库存查询' } ,
onShow ( ) { if ( ! requireSession ( ) ) return ; this . user = session . currentUser ; this . _pageActive = true ; this . startScanner ( ) ; if ( this . inboundMode && ( ! this . bootstrapped || this . phase === 'candidates' || this . _resumeReceiving ) ) { if ( ! this . busy ) { this . _resumeReceiving = false ; this . bootstrap ( ) } } } ,
@@ -175,7 +174,7 @@
receivingWarehouseLabel ( item ) { const code = String ( item ? . overseasWarehouseCode || '' ) . trim ( ) ; const name = String ( item ? . overseasWarehouseName || '' ) . trim ( ) ; return code && name ? ` ${ code } · ${ name } ` : '—' } ,
validateReceivingWarehouse ( value , label ) { return assertReceivingWarehouse ( value , this . user , label ) } ,
async refreshBoundWarehouse ( ) { this . user = applyWarehouseProfile ( await getCurrentWarehouseProfile ( ) ) ; return this . user } ,
photoStateLabel ( photo ) { if ( photo ? . status === 'uploading' ) return '正在上传…' ; if ( photo ? . status === 'error' ) return photo . errorMessage || ( photo . fileId ? '重拍失败,已保留原图' : '上传失败,请重试' ) ; return photo ? . fileId ? '点击左侧照片可查看大图' : '请拍摄现场照片 ' } ,
photoStateLabel ( photo ) { if ( photo ? . status === 'uploading' ) return '正在上传…' ; if ( photo ? . status === 'error' ) return photo . errorMessage || ( photo . fileId ? '重拍失败,已保留原图' : '上传失败,请重试' ) ; return ' ' } ,
startScanner ( ) { this . stopScanner ( ) ; this . _stopScanner = startScanBroadcast ( { onScan : ( value ) => this . handleHardwareScan ( value ) , onError : ( error ) => { this . error = error ; this . showToast ( '硬件扫码接收器异常,请检查扫描服务' , 'warning' ) } } ) } , stopScanner ( ) { if ( typeof this . _stopScanner === 'function' ) this . _stopScanner ( ) ; this . _stopScanner = null } ,
handleHardwareScan ( value ) { if ( ! this . _pageActive || this . loading ) return ; const scan = { value : String ( value || '' ) . trim ( ) , target : this . activeTarget } ; if ( ! scan . value ) return ; if ( ! this . inboundMode ) return this . showToast ( '该功能缺少海外 PDA 专用接口,扫码未提交' , 'warning' ) ; if ( this . phase === 'candidates' ) return this . showToast ( '请直接点击待卸货集装箱进入详情' , 'warning' ) ; if ( this . phase !== 'receive' ) return ; if ( this . busy || this . activeTarget === 'quantity' ) { if ( this . scanQueue . length >= 100 ) return this . showToast ( '扫码队列已满,请先处理当前托盘' , 'warning' ) ; this . scanQueue . push ( scan ) ; return this . showToast ( ` 已排队 ${ this . scanQueue . length } 个扫码 ` , 'warning' ) } return this . dispatchHardwareScan ( scan ) } ,
dispatchHardwareScan ( scan ) { if ( ! this . _pageActive || this . loading ) return ; const palletWorkflowActive = Boolean ( this . activeInbound || this . resolvedPallet ) ; const target = palletWorkflowActive ? 'pallet' : scan . target ; console . info ( '[Overseas PDA scan] dispatch' , { value : scan . value , target , queuedTarget : scan . target } ) ; if ( target === 'pallet' ) { this . palletInput = scan . value ; return this . resolvePallet ( scan . value ) } this . cartonInput = scan . value ; return this . resolveCarton ( scan . value ) } ,