@@ -1,11 +1,11 @@
< template >
< template >
< view v-if = "user" class="page-shell portal-page work-page receipt-page" :class="{ 'pda-task-list receipt-lookup-page': receiptDomain === 'customer' && !current && !result }" >
< view v-if = "user" class="page-shell portal-page work-page receipt-page" :class="{ 'pda-task-list receipt-lookup-page': receiptDomain === 'customer' && !current && !result }" >
< PortalHeader title = "退货入库" :user = "user" / >
< PortalHeader title = "退货入库" :user = "user" / >
< view class = "receipt-domain-tabs" role = "tablist" aria -label = " 退货入库类型 " >
< view v-if = "!current && !result && !exceptionWorking" class=" receipt-domain-tabs" role= "tablist" aria-label=" 退货入库类型" >
< button : class = "['receipt-domain-tab', { active: receiptDomain === 'customer' }]" role = "tab" : aria -selected = " receiptDomain = = = ' customer ' " :disabled = "busy" @tap ="switchDomain('customer')" > 客户退货入库 < / button >
< button : class = "['receipt-domain-tab', { active: receiptDomain === 'customer' }]" role = "tab" : aria -selected = " receiptDomain = = = ' customer ' " :disabled = "busy" @tap ="switchDomain('customer')" > 客户退货入库 < / button >
< button : class = "['receipt-domain-tab', { active: receiptDomain === 'exception' }]" role = "tab" : aria -selected = " receiptDomain = = = ' exception ' " :disabled = "busy" @tap ="switchDomain('exception')" > 异常退货入库 < / button >
< button : class = "['receipt-domain-tab', { active: receiptDomain === 'exception' }]" role = "tab" : aria -selected = " receiptDomain = = = ' exception ' " :disabled = "busy" @tap ="switchDomain('exception')" > 异常退货入库 < / button >
< / view >
< / view >
< ExceptionReturnWork v-if = "receiptDomain === 'exception'" / >
< ExceptionReturnWork v-if = "receiptDomain === 'exception'" @working-change="exceptionWorking = $event" / >
< scroll-view v-if = "receiptDomain === 'customer'" class="work-scroll" scroll-y><view class="portal-content work-content" >
< scroll-view v-if = "receiptDomain === 'customer'" class="work-scroll" scroll-y><view class="portal-content work-content" >
< view v-if = "!current && !result" class="work-card receipt-lookup-card" >
< view v-if = "!current && !result" class="work-card receipt-lookup-card" >
< text class = "card-title" > 扫描 SKU 69 码 < / text >
< text class = "card-title" > 扫描 SKU 69 码 < / text >
@@ -56,10 +56,8 @@
< / view >
< / view >
< / template >
< / template >
< view v-if = "unitType === 'PALLET'" class="item-list receipt-sku-list"><view v-for="item in selectedItems" :key="item.skuCode" class="item-card"><view class="row-head"><view><text class="business-code">{{ item.skuCode }}</text><text class="meta-line">{{ item.productNameZh || '商品名称未填写' }}</text></view><text class="remaining-copy">剩余 {{ remaining(item) }}</text></view><view class="sku-quantity-fields"><view class="quantity-row"><text>数量</text><input class="number-input" type="number" :value="quantity(item.skuCode)" placeholder="0" @input="setQuantity(item, $event.detail.value)" /></view><view v-if="unitType === 'PALLET'" class="quantity-row"><text>SKU 箱数</text><input class="number-input" type="number" :value="boxCount(item.skuCode)" placeholder="0" @input="setBoxCount(item, $event.detail.value)" / > < / view > < / view > < / view > < / view > < / view >
< view v-if = "unitType === 'PALLET'" class="item-list receipt-sku-list"><view v-for="item in selectedItems" :key="item.skuCode" class="item-card"><view class="row-head"><view><text class="business-code">{{ item.skuCode }}</text><text class="meta-line">{{ item.productNameZh || '商品名称未填写' }}</text></view><text class="remaining-copy">剩余 {{ remaining(item) }}</text></view><view class="sku-quantity-fields"><view class="quantity-row"><text>数量</text><input class="number-input" type="number" :value="quantity(item.skuCode)" placeholder="0" @input="setQuantity(item, $event.detail.value)" /></view><view v-if="unitType === 'PALLET'" class="quantity-row"><text>SKU 箱数</text><input class="number-input" type="number" :value="boxCount(item.skuCode)" placeholder="0" @input="setBoxCount(item, $event.detail.value)" / > < / view > < / view > < / view > < / view > < / view >
< view v-if = "unitType === 'PALLET'" class="summary-bar"><view><text class="summary-label">{{ unitType === 'PARCEL' ? '本次 合计' : '本托 合计' }}</text><text class="summary-value">{{ receiptQuantity }}</text></view><button class="primary submit-button" :disabled="!canConfirm || busy" @tap="confirmReceipt" > {{ busy ? ' 提交中 … ' : unitType = = = ' PARCEL ' ? ' 确认包裹 签收 ' : ' 确认签收并入托 ' }} < / button > < / view >
< view class = "summary-bar" > < view > < text class = "summary-label" > { { unitType === 'PALLET' ? '本托 合计' : '本次 合计' } } < / text > < text class = "summary-value" > { { receiptQuantity } } < / text > < / view > < view class = "receipt-action-buttons" > < button class = "primary submit-button" : disabled = "busy || !canConfirm || (unitType !== 'PALLET' && !allBoxRowsConfirmed)" @tap ="confirmReceipt" > {{ busy ? ' 提交中 … ' : ' 确认签收 ' }} < / button > < button class = "secondary manual-complete" :disabled = "busy" @tap ="manualComplete" > 缺货完结 < / button > < / view > < / view >
< view class = "work-card" > < view class = "row-head" > < view > < text class = "card-title" > 退货打托照片 < / text > < / view > < text class = "status-pill" > { { photoFileIds . length } } / 20 张 < / text > < / view > < button class = "secondary" : disabled = "busy || photoFileIds.length >= 20" @tap ="choosePhoto()" > 拍照或从相册选择 < / button > < view v-if = "photoFileIds.length" class="photo-grid"><view v-for="(fileId,index) in photoFileIds" :key="fileId" class="photo-tile"><image class="photo-thumbnail" :src="photoUrls[fileId]" mode="aspectFill" @tap="previewPhoto(fileId)" /><text class="photo-name">{{ photoNames[index] || '退货打托照片' }}</text><button class="link-button" :disabled="busy" @tap="removePhoto(index)" > 移除 < / button > < / view > < / view > < / view >
< view class = "work-card" > < view class = "row-head" > < view > < text class = "card-title" > 退货打托照片 < / text > < / view > < text class = "status-pill" > { { photoFileIds . length } } / 20 张 < / text > < / view > < button class = "secondary" : disabled = "busy || photoFileIds.length >= 20" @tap ="choosePhoto()" > 拍照或从相册选择 < / button > < view v-if = "photoFileIds.length" class="photo-grid"><view v-for="(fileId,index) in photoFileIds" :key="fileId" class="photo-tile"><image class="photo-thumbnail" :src="photoUrls[fileId]" mode="aspectFill" @tap="previewPhoto(fileId)" /><text class="photo-name">{{ photoNames[index] || '退货打托照片' }}</text><button class="link-button" :disabled="busy" @tap="removePhoto(index)" > 移除 < / button > < / view > < / view > < / view >
< view v-if = "unitType !== 'PALLET'" class="summary-bar"><view><text class="summary-label">本次合计</text><text class="summary-value">{{ receiptQuantity }}</text></view><button class="primary submit-button" :disabled="busy || !canConfirm || !allBoxRowsConfirmed || !photoFileIds.length" @tap="confirmReceipt" > {{ busy ? ' 提交中 … ' : ' 确认签收入库 ' }} < / button > < / view >
< button v-if = "current.status === 'PARTIALLY_RECEIVED'" class="secondary manual-complete" :disabled="busy" @tap="manualComplete" > 缺货手动完结 < / button >
< view v-if = "current.completedPallets?.length" class="work-card"><text class="card-title">本单已完成打托</text><view class="item-list"><view v-for="pallet in current.completedPallets" :key="pallet.palletCode" class="item-card"><text class="business-code">{{ pallet.palletCode }}</text><text class="meta-line">{{ pallet.skuCode }} · {{ pallet.quantity }} 件</text><text class="meta-line" > 签收批次 : {{ pallet.receiptBatchNo }} < / text > < / view > < / view > < / view >
< view v-if = "current.completedPallets?.length" class="work-card"><text class="card-title">本单已完成打托</text><view class="item-list"><view v-for="pallet in current.completedPallets" :key="pallet.palletCode" class="item-card"><text class="business-code">{{ pallet.palletCode }}</text><text class="meta-line">{{ pallet.skuCode }} · {{ pallet.quantity }} 件</text><text class="meta-line" > 签收批次 : {{ pallet.receiptBatchNo }} < / text > < / view > < / view > < / view >
< / template >
< / template >
@@ -82,7 +80,7 @@
export default {
export default {
components : { PortalHeader , ScanTargetInput , ExceptionReturnWork } ,
components : { PortalHeader , ScanTargetInput , ExceptionReturnWork } ,
data ( ) { return { receiptDomain : 'customer' , user : session . currentUser , current : null , candidates : null , result : null , receiptResults : [ ] , selectedSku : '' , palletCheck : null , newPalletHtml : '' , unitType : 'PALLET' , unitCode : '' , boxRows : [ { items : { } , length : '' , width : '' , height : '' , weight : '' , originalPackaging : true , confirmed : false } ] , palletLength : '' , palletWidth : '' , palletHeight : '' , palletWeight : '' , unitOptions : [ { value : 'PALLET' , label : '托盘' } , { value : 'BOX' , label : '箱子' } , { value : 'PARCEL' , label : '包裹' } ] , photoFileIds : [ ] , photoNames : [ ] , photoUrls : { } , activeTarget : 'lookup' , busy : false , error : null } } ,
data ( ) { return { receiptDomain : 'customer' , exceptionWorking : false , user : session . currentUser , current : null , candidates : null , result : null , receiptResults : [ ] , selectedSku : '' , palletCheck : null , newPalletHtml : '' , unitType : 'PALLET' , unitCode : '' , boxRows : [ { items : { } , length : '' , width : '' , height : '' , weight : '' , originalPackaging : true , confirmed : false } ] , palletLength : '' , palletWidth : '' , palletHeight : '' , palletWeight : '' , unitOptions : [ { value : 'PALLET' , label : '托盘' } , { value : 'BOX' , label : '箱子' } , { value : 'PARCEL' , label : '包裹' } ] , photoFileIds : [ ] , photoNames : [ ] , photoUrls : { } , activeTarget : 'lookup' , busy : false , error : null } } ,
computed : {
computed : {
entryMode : { get ( ) { return warehouseWork . returnReceipt . entryMode } , set ( value ) { updateWarehouseWork ( 'returnReceipt' , { entryMode : value } ) } } ,
entryMode : { get ( ) { return warehouseWork . returnReceipt . entryMode } , set ( value ) { updateWarehouseWork ( 'returnReceipt' , { entryMode : value } ) } } ,
lookupCode : { get ( ) { return warehouseWork . returnReceipt . lookupCode } , set ( value ) { updateWarehouseWork ( 'returnReceipt' , { lookupCode : value } ) } } ,
lookupCode : { get ( ) { return warehouseWork . returnReceipt . lookupCode } , set ( value ) { updateWarehouseWork ( 'returnReceipt' , { lookupCode : value } ) } } ,
@@ -143,7 +141,7 @@
printPallet ( ) { if ( ! this . newPalletHtml ) return ; /* #ifdef H5 */ const view = window . open ( '' , '_blank' ) ; if ( view ) { view . document . write ( this . newPalletHtml ) ; view . document . close ( ) ; view . focus ( ) ; view . print ( ) } else this . setError ( new Error ( '请允许打开标签打印窗口' ) , '打印失败' ) ; /* #endif */ /* #ifndef H5 */ this . setError ( new Error ( '当前设备未配置打印服务' ) , '打印失败' ) ; /* #endif */ } ,
printPallet ( ) { if ( ! this . newPalletHtml ) return ; /* #ifdef H5 */ const view = window . open ( '' , '_blank' ) ; if ( view ) { view . document . write ( this . newPalletHtml ) ; view . document . close ( ) ; view . focus ( ) ; view . print ( ) } else this . setError ( new Error ( '请允许打开标签打印窗口' ) , '打印失败' ) ; /* #endif */ /* #ifndef H5 */ this . setError ( new Error ( '当前设备未配置打印服务' ) , '打印失败' ) ; /* #endif */ } ,
printReturnCarton ( label ) { if ( ! label ? . printableHtml ) return ; /* #ifdef H5 */ const view = window . open ( '' , '_blank' ) ; if ( view ) { view . document . write ( label . printableHtml ) ; view . document . close ( ) ; view . focus ( ) ; view . print ( ) } else this . setError ( new Error ( '请允许打开标签打印窗口' ) , '打印失败' ) ; /* #endif */ /* #ifndef H5 */ this . setError ( new Error ( '当前设备未配置打印服务' ) , '打印失败' ) ; /* #endif */ } ,
printReturnCarton ( label ) { if ( ! label ? . printableHtml ) return ; /* #ifdef H5 */ const view = window . open ( '' , '_blank' ) ; if ( view ) { view . document . write ( label . printableHtml ) ; view . document . close ( ) ; view . focus ( ) ; view . print ( ) } else this . setError ( new Error ( '请允许打开标签打印窗口' ) , '打印失败' ) ; /* #endif */ /* #ifndef H5 */ this . setError ( new Error ( '当前设备未配置打印服务' ) , '打印失败' ) ; /* #endif */ } ,
acceptTracking ( raw ) { this . trackingNo = String ( raw ? . detail ? . value ? ? raw ? ? '' ) . trim ( ) ; playSuccessSound ( ) } ,
acceptTracking ( raw ) { this . trackingNo = String ( raw ? . detail ? . value ? ? raw ? ? '' ) . trim ( ) ; playSuccessSound ( ) } ,
async confirmReceipt ( ) { if ( this . busy ) return ; const totalBoxCount = this . palletBoxCount ; if ( this . unitType === 'PALLET' && totalBoxCount <= 0 ) { this . setError ( new Error ( '请先填写各 SKU 的箱数' ) , '托盘签收校验失败' ) ; return } if ( ! this . canConfirm || this . busy || ( this . unitType !== 'PALLET' && ! this . allBoxRowsConfirmed ) ) return ; if ( this . unitType !== 'PALLET' && ! this . photoFileIds . length ) { this . setError ( new Error ( '请先拍照或上传签收照片,再确认签收入库' ) , '签收校验失败' ) ; return } const willComplete = this . current . expectedItems . every ( ( item ) => ( this . unitType !== 'PALLET' ? this . boxRows . reduce ( ( sum , box ) => sum + ( Number ( box . items [ item . skuCode ] ) || 0 ) , 0 ) : this . quantity ( item . skuCode ) ) >= this . remaining ( item ) ) ; if ( willComplete && ! this . photoFileIds . length ) { const modal = await new Promise ( ( resolve ) => uni . showModal ( { title : '缺少退货打托照片 ' , content : '本次签收将完成整单入库,请先拍照并上传后再确认打托 。' , confirmText : '去拍照' , cancelText : '取消' , success : resolve } ) ) ; if ( modal ? . confirm ) this . error = { message : '请先拍照并上传退货打托照片后再确认' } ; return }
async confirmReceipt ( ) { if ( this . busy ) return ; const totalBoxCount = this . palletBoxCount ; if ( this . unitType === 'PALLET' && totalBoxCount <= 0 ) { this . setError ( new Error ( '请先填写各 SKU 的箱数' ) , '托盘签收校验失败' ) ; return } if ( ! this . canConfirm || this . busy || ( this . unitType !== 'PALLET' && ! this . allBoxRowsConfirmed ) ) return ; const willComplete = this . current . expectedItems . every ( ( item ) => ( this . unitType !== 'PALLET' ? this . boxRows . reduce ( ( sum , box ) => sum + ( Number ( box . items [ item . skuCode ] ) || 0 ) , 0 ) : this . quantity ( item . skuCode ) ) >= this . remaining ( item ) ) ; if ( ! this . photoFileIds . length && ( this . unitType !== 'PALLET' || willComplete ) ) { uni . showModal ( { title : '请先拍照 ' , content : '请先拍照或从相册上传签收照片,再点击确认签收 。' , showCancel : false , confirmText : '知道了' } ) ; return }
await this . run ( async ( ) => {
await this . run ( async ( ) => {
const rows = this . unitType !== 'PALLET' ? this . boxRows : [ null ]
const rows = this . unitType !== 'PALLET' ? this . boxRows : [ null ]
let expectedReceivedQuantity = this . current . expectedItems . reduce ( ( sum , item ) => sum + Number ( item . receivedQuantity || 0 ) , 0 )
let expectedReceivedQuantity = this . current . expectedItems . reduce ( ( sum , item ) => sum + Number ( item . receivedQuantity || 0 ) , 0 )
@@ -254,13 +252,14 @@
. receipt - page : deep ( . portal - header - shell ) { flex - shrink : 0 }
. receipt - page : deep ( . portal - header - shell ) { flex - shrink : 0 }
. receipt - page > . work - scroll { flex : 1 ; height : 0 ; min - height : 0 }
. receipt - page > . work - scroll { flex : 1 ; height : 0 ; min - height : 0 }
. receipt - page . work - content { padding - bottom : calc ( 24 px + env ( safe - area - inset - bottom ) ) }
. receipt - page . work - content { padding - bottom : calc ( 24 px + env ( safe - area - inset - bottom ) ) }
. receipt - page . summary - bar { position : static }
. receipt - page . summary - bar { position : static ; margin - bottom : 10 px }
. receipt - domain - tabs { display : flex ; flex - shrink : 0 ; gap : 4 px ; margin : 10 px 14 px 0 ; padding : 4 px ; border : 1 px solid # d8e4f5 ; border - radius : 10 px ; background : # e8eff9 }
. receipt - domain - tabs { display : flex ; flex - shrink : 0 ; gap : 4 px ; margin : 10 px 14 px 0 ; padding : 4 px ; border : 1 px solid # d8e4f5 ; border - radius : 10 px ; background : # e8eff9 }
. receipt - domain - tab { box - sizing : border - box ; flex : 1 ; min - width : 0 ; min - height : 40 px ; margin : 0 ; padding : 0 8 px ; border - radius : 7 px ; color : # 52677 f ; background : transparent ; font - size : 14 px ; line - height : 40 px }
. receipt - domain - tab { box - sizing : border - box ; flex : 1 ; min - width : 0 ; min - height : 40 px ; margin : 0 ; padding : 0 8 px ; border - radius : 7 px ; color : # 52677 f ; background : transparent ; font - size : 14 px ; line - height : 40 px }
. receipt - domain - tab : : after { border : 0 }
. receipt - domain - tab : : after { border : 0 }
. receipt - domain - tab . active { color : # 135 ee8 ; background : # fff ; font - weight : 700 ; box - shadow : 0 1 px 5 px rgba ( 28 , 69 , 121 , .1 ) }
. receipt - domain - tab . active { color : # 135 ee8 ; background : # fff ; font - weight : 700 ; box - shadow : 0 1 px 5 px rgba ( 28 , 69 , 121 , .1 ) }
. receipt - domain - tab [ disabled ] { opacity : .6 }
. receipt - domain - tab [ disabled ] { opacity : .6 }
. manual - complete { margin - top : 12 px }
. summary - bar > . receipt - action - buttons { display : flex ; flex : 1 ; min - width : 0 ; gap : 8 px }
. receipt - action - buttons > . primary , . receipt - action - buttons > . secondary { flex : 1 ; min - width : 0 ; width : auto ; margin : 0 ; padding : 0 6 px ; white - space : nowrap }
. receipt - lookup - page { background : # f1f5fd }
. receipt - lookup - page { background : # f1f5fd }
. receipt - lookup - page . work - content { padding - top : 14 px }
. receipt - lookup - page . work - content { padding - top : 14 px }