@@ -55,7 +55,7 @@ const canConfirmRelabel = computed(() => auth.hasPermission('merchant:overseas-w
const canConfirmTransfer = computed ( ( ) => auth . hasPermission ( 'merchant:overseas-warehouse:transfer:confirm' ) )
const canCancelDiscard = computed ( ( ) => auth . hasPermission ( 'merchant:overseas-warehouse:discard:cancel' ) )
const configs : Record < ServiceType , ServiceConfig > = {
labeling : { title : '贴标申请' , createTitle : '新增贴标申请' , numberLabel : '贴标申请单号' , emptyText : '暂无贴标申请' , columns : [ '操作' , '贴标申请单号' , '客户 单号' , '海外操作仓' , '贴标要求' , 'SKU 数(种)' , '总箱数' , '状态' , '审核时间' , '贴标完成时间' , '贴标出库时间' , '创建时间' ] } ,
labeling : { title : '贴标申请' , createTitle : '新增贴标申请' , numberLabel : '贴标申请单号' , emptyText : '暂无贴标申请' , columns : [ '操作' , '贴标申请单号' , '关联业务 单号' , '海外操作仓' , 'SKU 数(种)' , '总箱数/包裹' , '换标类型 ', '状态' , '审核时间' , '贴标完成时间' , '贴标出库时间' , '创建时间' ] } ,
transfer : { title : '仓储转运单' , createTitle : '新增仓储转运单' , numberLabel : '转运单号' , emptyText : '暂无仓储转运订单' , columns : [ '操作' , '转运单号' , '箱数' , '海外仓' , 'FBA 目的仓' , '入仓方式' , '是否换标' , '换标单号' , '换标状态' , '状态' , '审核时间' , '测量时间' , '出库时间' , '创建时间' ] } ,
disposal : { title : '货物废弃' , createTitle : '新增货物废弃申请' , numberLabel : '废弃申请单号' , emptyText : '暂无货物废弃申请' , columns : [ '操作' , '废弃申请单号' , '废弃出库单号' , '进仓编号' , '操作仓' , 'SKU 汇总' , '废弃数量' , '预估费用' , '状态' , '废弃完成时间' , '创建时间' ] } ,
relabel : { title : '退货换标' , createTitle : '新建退货换标单' , numberLabel : '退货换标单号' , emptyText : '暂无退货换标订单' , columns : [ '操作' , '退货换标单号' , '来源 FBA 仓' , '目的 FBA 仓' , 'SKU 汇总' , '退件总数' , '是否换标' , '换标状态' , '尾程状态' , '审核时间' , '测量时间' , '出库时间' , '创建时间' ] } ,
@@ -183,7 +183,7 @@ const listQueryFields = computed<QueryField[]>(() => {
const listColumns = computed < DataTableColumn [ ] > ( ( ) => config . value . columns . map ( ( label , index ) => index === 0
? { key : 'actions' , label , slotName : 'actions' , width : props . service === 'transfer' ? 210 : 160 , fixed : 'left' }
: { key : String ( index - 1 ) , prop : String ( index - 1 ) , label , minWidth : index === 1 || label === '换标单号' ? 190 : 130 ,
slotName : props . service === 'transfer' && label === '换标状态' ? 'transferRelabelStatus' : props . service === 'transfer' && label === '状态' ? 'transferStatus' : undefined } ) )
slotName : props . service === 'labeling' && label === '换标类型' ? 'labelingType' : props . service === 'labeling' && label === '状态' ? 'labelingStatus' : props . service === 'transfer' && label === '换标状态' ? 'transferRelabelStatus' : props . service === 'transfer' && label === '状态' ? 'transferStatus' : undefined } ) )
const discardCandidates = ref < import ( ' .. / api / warehouseServices ' ) .DiscardCandidate [ ] > ( [ ] )
const selectedDiscardOrderId = ref ( '' )
const discardCandidatesLoading = ref ( false )
@@ -898,6 +898,19 @@ function statusLabel(value: string) {
return labels [ value ] || value || '—'
}
type StatusTagType = 'primary' | 'success' | 'warning' | 'danger' | 'info'
function labelingTypeLabels ( value : unknown ) : string [ ] {
return typeof value === 'string' && value !== '—' ? [ ... new Set ( value . split ( '、' ) ) ] : [ ]
}
function labelingTypeTagType ( label : string ) : StatusTagType {
if ( label === '只换 SKU' ) return 'primary'
if ( label === '只换箱唛' ) return 'warning'
return 'success'
}
function labelingStatusTagType ( status : string ) : StatusTagType {
if ( status === 'CANCELLED' ) return 'info'
if ( ! [ 'READY_FOR_OUTBOUND' , 'COMPLETED' , 'REJECTED' , 'WAIT_APPROVAL' , 'PENDING_REVIEW' , 'PENDING_PROCESSING' , 'CUSTOMER_CONFIRM_PENDING' , 'PROCESSING' ] . includes ( status ) ) return 'info'
return relabelStatusTagType ( status )
}
function relabelStatusTagType ( status : string ) : StatusTagType {
if ( [ 'READY_FOR_OUTBOUND' , 'COMPLETED' ] . includes ( status ) ) return 'success'
if ( [ 'REJECTED' , 'CANCELLED' ] . includes ( status ) ) return 'danger'
@@ -971,7 +984,7 @@ async function load() {
total . value = Number . isFinite ( Number ( result . total ) ) ? Number ( result . total ) : result . records . length
submittedRows . value = result . records . map ( ( item ) => {
const destinationWarehouses = warehouseDisplay ( item . overseasWarehouseId )
return { ... row ( [ item . relabelNo , item . customerOrderNo , destinationWarehouses , '—' , new Set ( item . skuCodes . split ( ',' ) . map ( code => code . trim ( ) ) . filter ( Boolean ) ) . size , item . totalPackageCount , statusLabel ( item . status ) , formatDateTime ( item . reviewedAt ) , formatDateTime ( item . relabelCompletedAt ) , formatDateTime ( item . outboundAt ) , formatDateTime ( item . createdAt ) ] ) , _ _status : item . status , _ _confirmedAt : item . customerConfirmedAt || '' }
return { ... row ( [ item . relabelNo , item . customerOrderNo , destinationWarehouses , new Set ( item . skuCodes . split ( ',' ) . map ( code => code . trim ( ) ) . filter ( Boolean ) ) . size , item . totalPackageCount , ( item . operationTypes || [ ] ) . map ( type => ( { CARTON _LABEL : '只换箱唛' , SKU _LABEL : '只换 SKU' , RETURN _BULK _SKU _LABEL : '只换 SKU' , CARTON _AND _SKU : 'SKU 和箱唛都换' } [ type ] || type ) ) . join ( '、' ) || '—' , statusLabel ( item . status ) , formatDateTime ( item . reviewedAt ) , formatDateTime ( item . relabelCompletedAt ) , formatDateTime ( item . outboundAt ) , formatDateTime ( item . createdAt ) ] ) , _ _status : item . status , _ _confirmedAt : item . customerConfirmedAt || '' }
} )
} else if ( props . service === 'disposal' ) {
const result = await listDiscards ( { page : page . value , size : pageSize . value , discardNo : filters . number || undefined , inboundCode : filters . inboundNo || undefined , overseasWarehouseId : filters . warehouse || undefined , status : filters . status || undefined , createdFrom : dateTime ( filters . start ) , createdTo : dateTime ( filters . end , true ) , completedFrom : dateTime ( filters . completedStart ) , completedTo : dateTime ( filters . completedEnd , true ) } )
@@ -1474,12 +1487,14 @@ function addReturnBoxBinding(source: RelabelInventoryCandidate) {
< template >
< section class = "warehouse-service-page" : class = "{ 'warehouse-service-page--detail': mode === 'detail', 'warehouse-service-page--return-relabel': isReturnRelabelSource }" >
< template v-if = "mode === 'list'" >
< QueryToolbar v-model = "listQueryModel" layout="grid" :ariaLabel="`${config.title}筛选`" :fields="listQueryFields" :search-on-reset="false" @search="query" @reset="resetListQuery" / >
< QueryToolbar v-model = "listQueryModel" :class="{ 'labeling-query': props.service === 'labeling', 'transfer-query': props.service === 'transfer' || props.service === 'disposal', 'disposal-query': props.service === 'disposal' }" layout="grid" :ariaLabel="`${config.title}筛选`" :fields="listQueryFields" :search-on-reset="false" @search="query" @reset="resetListQuery" / >
< div class = "warehouse-card warehouse-table-card" >
< DataTable v -model :page = "page" v -model :page-size = "pageSize" :data = "submittedRows" :columns = "listColumns" :loading = "loading" :total = "total" : page -sizes = " [ 15 , 30 , 50 , 100 ] " :row-actions = "listRowActions" :empty-text = "config.emptyText" >
< template # toolbar -actions > < el-button v-if = "canCreate" type="primary" @click="openCreate"><Plus :size="16" / > { { props . service === 'relabel' ? '新建退货换标单' : props . service === 'transfer' ? '新增订单' : ` 新增 ${ config . title } ` } } < / el-button > < / template >
< template v-if = "props.service === 'transfer' && submittedRows.some((item) => item.__status === 'WAIT_CUSTOMER_CONFIRMATION')" #before-table><div class="warehouse-list-notice" > 部分入库完成订单已有 PDA 实测数据 , 请在对应行确认测量数据 。 < / div > < / template >
< template # transferRelabelStatus = "{ row }" > < el-tag :type = "transferRelabelStatusTagType(row.__relabelStatus)" effect = "light" > { { row . _ _relabelStatusLabel } } < / el-tag > < / template >
< template # labelingType = "{ row }" > < span v-if = "row['5'] === '—'">—</span><span v-else class="labeling-type-tags"><el-tag v-for="label in labelingTypeLabels(row['5'])" :key="label" :type="labelingTypeTagType(label)" size="small" effect="light" > {{ label }} < / el -tag > < / span > < / template >
< template # labelingStatus = "{ row }" > < el-tag :type = "labelingStatusTagType(row.__status)" size = "small" effect = "light" > { { statusLabel ( row . _ _status ) } } < / el-tag > < / template >
< template # transferStatus = "{ row }" > < el-tag :type = "transferStatusTagType(row.__status)" effect = "light" > { { row . _ _statusLabel } } < / el-tag > < / template >
< / DataTable >
< / div >
@@ -1687,6 +1702,32 @@ function addReturnBoxBinding(source: RelabelInventoryCandidate) {
< / template >
< style scoped >
. labeling - type - tags { display : inline - flex ; flex - wrap : wrap ; gap : 4 px ; }
. labeling - query . query - toolbar -- grid { display : grid ; grid - template - columns : repeat ( 4 , minmax ( 0 , 320 px ) ) ; gap : 14 px 18 px ; align - items : end ; }
. labeling - query : deep ( . query - toolbar _ _filters ) { display : contents ; }
. labeling - query : deep ( . query - toolbar _ _field ) { display : flex ; flex - direction : column ; align - items : stretch ; gap : 6 px ; }
. labeling - query : deep ( . query - toolbar _ _field : nth - child ( - n + 3 ) ) { grid - row : 1 ; }
. labeling - query : deep ( . query - toolbar _ _field : nth - child ( n + 4 ) ) { grid - row : 2 ; }
. labeling - query : deep ( . query - toolbar _ _label ) { line - height : 18 px ; }
. labeling - query : deep ( . query - toolbar _ _actions ) { grid - column : 4 ; grid - row : 1 ; margin : 0 ; padding : 0 ; border : 0 ; align - self : end ; }
. transfer - query . query - toolbar -- grid { display : grid ; grid - template - columns : repeat ( 5 , minmax ( 0 , 280 px ) ) ; gap : 14 px 18 px ; align - items : end ; }
. transfer - query : deep ( . query - toolbar _ _filters ) { display : contents ; }
. transfer - query : deep ( . query - toolbar _ _field ) { display : flex ; flex - direction : column ; align - items : stretch ; gap : 6 px ; }
. transfer - query : deep ( . query - toolbar _ _field : nth - child ( - n + 5 ) ) { grid - row : 1 ; }
. transfer - query : deep ( . query - toolbar _ _field : nth - child ( n + 6 ) ) { grid - row : 2 ; }
. transfer - query : deep ( . query - toolbar _ _label ) { line - height : 18 px ; }
. transfer - query : deep ( . query - toolbar _ _actions ) { grid - column : 5 ; grid - row : 2 ; margin : 0 ; padding : 0 ; border : 0 ; align - self : end ; }
. disposal - query : deep ( . query - toolbar _ _field : nth - child ( 5 ) ) { grid - column : 1 ; grid - row : 2 ; }
. disposal - query : deep ( . query - toolbar _ _actions ) { grid - column : 2 ; }
@ media ( max - width : 1200 px ) {
. labeling - query . query - toolbar -- grid { grid - template - columns : repeat ( 2 , minmax ( 0 , 320 px ) ) ; }
. labeling - query : deep ( . query - toolbar _ _field : nth - child ( n ) ) { grid - row : auto ; }
. labeling - query : deep ( . query - toolbar _ _actions ) { grid - column : auto ; grid - row : auto ; }
. transfer - query . query - toolbar -- grid { grid - template - columns : repeat ( 2 , minmax ( 0 , 320 px ) ) ; }
. transfer - query : deep ( . query - toolbar _ _field : nth - child ( n ) ) { grid - row : auto ; }
. disposal - query : deep ( . query - toolbar _ _field : nth - child ( 5 ) ) { grid - column : auto ; }
. transfer - query : deep ( . query - toolbar _ _actions ) { grid - column : auto ; grid - row : auto ; }
}
. warehouse - order - info - heading { display : flex ; align - items : center ; justify - content : space - between ; max - width : 1296 px ; margin - bottom : 16 px ; border - bottom : 1 px solid # edf0f4 ; padding - bottom : 10 px ; }
. warehouse - form - section . warehouse - order - info - heading h3 { margin : 0 ; padding : 0 ; border : 0 ; }
. warehouse - order - info - summary { display : flex ; flex - wrap : wrap ; gap : 10 px 24 px ; color : # 64748 b ; font - size : 12 px ; }