@@ -32,7 +32,7 @@ type ServiceType = 'labeling' | 'transfer' | 'disposal' | 'relabel'
type PageMode = 'list' | 'create' | 'edit' | 'detail'
type LabelingMode = 'carton' | 'sku' | 'carton_and_sku'
type RelabelTemplateType = 'carton' | 'sku' | 'mixed'
interface CargoRow { id : number ; included ? : boolean ; sku : string ; name : string ; quantity : number ; availableQuantity ? : number ; sourceOrderId ? : string ; sourceInventoryBalanceId ? : string ; sourceInventoryPositionId ? : string ; sourceInboundOrderId ? : string ; returnCartonId ? : string ; sourceInboundOrderNo ? : string ; sourceInboundCode ? : string ; sourceWarehouseName ? : string ; palletNo ? : string ; warehouseLocationCode ? : string ; fnsku : string ; shipmentNo : string ; newSkuCode : string ; shippingMark : string ; customerCartonMarks : string ; cartonMark : string ; fbaCartonMark : string ; destinationWarehouseId : string ; remark : string }
interface CargoRow { id : number ; included ? : boolean ; sku : string ; name : string ; quantity : number ; availableQuantity ? : number ; sourceOrderId ? : string ; sourceType ? : 'DROPSHIP' | 'RETURN_CARTON' ; sourceInventoryBalanceId ? : string ; sourceInventoryPositionId ? : string ; sourceInboundOrderId ? : string ; returnCartonId ? : string ; sourceInboundOrderNo ? : string ; sourceInboundCode ? : string ; sourceWarehouseName ? : string ; palletNo ? : string ; warehouseLocationCode ? : string ; fnsku : string ; shipmentNo : string ; newSkuCode : string ; shippingMark : string ; customerCartonMarks : string ; cartonMark : string ; fbaCartonMark : string ; destinationWarehouseId : string ; remark : string }
interface SkuRelabelRow { id : number ; sourceRowId : number | null ; cartonMark : string ; originalSku : string ; newSku : string ; quantity : number ; fbaCartonMark : string ; destinationWarehouseId : string ; remark : string }
interface ImportedRelabelBinding extends SkuRelabelRow { templateType : RelabelTemplateType ; selected : boolean ; shipmentNo : string }
interface ReturnRelabelSkuRow { skuCode : string ; productNameZh : string ; availableQuantity : number ; selected : boolean ; quantity : number }
@@ -55,19 +55,27 @@ 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 数' , '总箱数' , '状态' , '创建时间' ] } ,
transfer : { title : '仓储转运单' , createTitle : '新增仓储转运单' , numberLabel : '转运单号' , emptyText : '暂无仓储转运订单' , columns : [ '操作' , '转运单号' , '箱数' , '海外仓' , 'FBA 目的仓' , '入仓方式' , '是否换标' , '换标单号' , '换标状态' , '状态' , '创建时间' ] } ,
disposal : { title : '货物废弃' , createTitle : '新增货物废弃申请' , numberLabel : '废弃申请单号' , emptyText : '暂无货物废弃申请' , columns : [ '操作' , '废弃申请单号' , '进仓编号' , '操作仓' , 'SKU 汇总' , '废弃数量' , '预估费用' , '状态' , '创建时间' ] } ,
relabel : { title : '退货换标' , createTitle : '新建退货换标单' , numberLabel : '退货换标单号' , emptyText : '暂无退货换标订单' , columns : [ '操作' , '退货换标单号' , '来源 FBA 仓' , '目的 FBA 仓' , '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 汇总' , '退件总数' , '是否换标' , '换标状态' , '尾程状态' , '审核时间' , '测量时间' , '出库时间' , '创建时间' ] } ,
}
const config = computed ( ( ) => configs [ props . service ] )
const listQueryModel = computed < QueryModel > ( {
get : ( ) => ( { ... filters , createdDates : [ filters . start , filters . end ] . filter ( Boolean ) } ) ,
get : ( ) => ( { ... filters , createdDates : [ filters . start , filters . end ] . filter ( Boolean ) , reviewDates : [ filters . reviewStart , filters . reviewEnd ] . filter ( Boolean ) , completedDates : [ filters . completedStart , filters . completedEnd ] . filter ( Boolean ) , measuredDates : [ filters . measuredStart , filters . measuredEnd ] . filter ( Boolean ) , outboundDates : [ filters . outboundStart , filters . outboundEnd ] . filter ( Boolean ) } ) ,
set : ( value ) => Object . assign ( filters , {
number : String ( value . number || '' ) , inboundNo : String ( value . inboundNo || '' ) , warehouse : String ( value . warehouse || '' ) ,
destination : String ( value . destination || '' ) , status : String ( value . status || '' ) ,
start : Array . isArray ( value . createdDates ) ? String ( value . createdDates [ 0 ] || '' ) : '' ,
end : Array . isArray ( value . createdDates ) ? String ( value . createdDates [ 1 ] || '' ) : '' ,
reviewStart : Array . isArray ( value . reviewDates ) ? String ( value . reviewDates [ 0 ] || '' ) : '' ,
reviewEnd : Array . isArray ( value . reviewDates ) ? String ( value . reviewDates [ 1 ] || '' ) : '' ,
completedStart : Array . isArray ( value . completedDates ) ? String ( value . completedDates [ 0 ] || '' ) : '' ,
completedEnd : Array . isArray ( value . completedDates ) ? String ( value . completedDates [ 1 ] || '' ) : '' ,
measuredStart : Array . isArray ( value . measuredDates ) ? String ( value . measuredDates [ 0 ] || '' ) : '' ,
measuredEnd : Array . isArray ( value . measuredDates ) ? String ( value . measuredDates [ 1 ] || '' ) : '' ,
outboundStart : Array . isArray ( value . outboundDates ) ? String ( value . outboundDates [ 0 ] || '' ) : '' ,
outboundEnd : Array . isArray ( value . outboundDates ) ? String ( value . outboundDates [ 1 ] || '' ) : '' ,
} ) ,
} )
const statusOptions = computed ( ( ) => props . service === 'labeling'
@@ -84,7 +92,7 @@ const statusOptions = computed(() => props.service === 'labeling'
{ label : '审核驳回' , value : 'REJECTED' } ]
: [ { label : '待审核' , value : 'PENDING_REVIEW' } , { label : '已通过' , value : 'APPROVED' } , { label : '待分配' , value : 'PENDING_ASSIGNMENT' } , { label : '已分配' , value : 'ALLOCATED' } , { label : '已出库' , value : 'OUTBOUNDED' } , { label : '部分收货' , value : 'PARTIALLY_RECEIVED' } , { label : '已收货' , value : 'RECEIVED' } , { label : '已拒绝' , value : 'REJECTED' } ] )
const mode = ref < PageMode > ( props . detailPage ? 'detail' : props . createPage ? 'create' : 'list' )
const filters = reactive ( { number : '' , inboundNo : '' , warehouse : '' , destination : '' , status : '' , start : '' , end : '' } )
const filters = reactive ( { number : '' , inboundNo : '' , warehouse : '' , destination : '' , status : '' , start : '' , end : '' , reviewStart : '' , reviewEnd : '' , completedStart : '' , completedEnd : '' , measuredStart : '' , measuredEnd : '' , outboundStart : '' , outboundEnd : '' } )
const form = reactive ( { customerNo : '' , inboundNo : '' , sourceWarehouse : '' , warehouse : '' , destination : '' , privateDestinationAddress : '' , privateDestinationPostalCode : '' , privateDestinationContact : '' , privateDestinationPhone : '' , address : '' , contact : '' , phone : '' , pickupContact : '' , pickupPhone : '' , relabelContact : '' , relabelPhone : '' , deliveryMethod : '' , relabelDeliveryMethod : '' , trackingNo : '' , reason : '' , requirement : '' , relabel : false , remark : '' , labelDeadline : '' , estimatedArrivalAt : '' , estimatedShipAt : '' , fbaShipmentNo : '' , volumeCbm : 0.01 } )
const transferDestinationType = ref < 'NONE' | 'FBA' | 'PRIVATE' > ( 'NONE' )
const selectedOperationWarehouse = ref < MerchantWarehouseDetail | null > ( null )
@@ -120,6 +128,7 @@ interface ServiceDetail extends Record<string, unknown> {
remark ? : string ; needRelabel ? : boolean ; labelDeadline ? : string ; relabelContactName ? : string
relabelContactPhone ? : string ; relabelDeliveryMethod ? : string ; relabelNo ? : string
relabelStatus ? : string ; relabelFulfillmentStatus ? : string ; createdAt ? : string
inboundCode ? : string ; sourceOrders ? : Array < { lastlegOrderId ? : string | number } >
items ? : Array < { skuCode ? : string ; productNameZh ? : string ; packageCount ? : number ; shippingMark ? : string } >
relabelBindings ? : Array < { operationType ? : string ; customerCartonMark ? : string ; originalSku ? : string ; newSku ? : string ; quantity ? : number ; fbaCartonMark ? : string ; destinationWarehouseCode ? : string ; remark ? : string } >
cartons ? : TransferCarton [ ] ; pallets ? : TransferPallet [ ] ; measurementPhotos ? : TransferMeasurementPhoto [ ] ; customerConfirmedAt ? : string
@@ -152,9 +161,22 @@ const listQueryFields = computed<QueryField[]>(() => {
options : ( props . service === 'relabel' ? fbaWarehouses . value : operationWarehouses . value ) . map ( ( warehouse ) => ( { label : warehouseLabel ( warehouse ) , value : String ( warehouse . id ) } ) ) ,
} )
if ( props . service === 'transfer' || props . service === 'relabel' ) fields . push ( { key : 'destination' , label : '目的 FBA 仓' , type : 'input' , placeholder : '请输入仓代码' } )
fields . push (
{ key : 'st atu s' , label : '订单状态 ' , type : 'select' , options : statusOptions . value } ,
{ key : 'createdDates' , label : '创建时间' , type : 'datera nge ' } ,
fields . push ( { key : 'status' , label : '订单状态' , type : 'select' , options : statusOptions . value } )
fields . push ( { key : 'createdD ate s' , label : '创建时间 ' , type : 'daterange' } )
if ( props . service === 'labeli ng') fields . push (
{ key : 'reviewDates' , label : '审核时间' , type : 'daterange' } ,
{ key : 'completedDates' , label : '贴标完成时间' , type : 'daterange' } ,
{ key : 'outboundDates' , label : '贴标出库时间' , type : 'daterange' } ,
)
if ( props . service === 'relabel' ) fields . push (
{ key : 'reviewDates' , label : '审核时间' , type : 'daterange' } ,
{ key : 'measuredDates' , label : '测量时间' , type : 'daterange' } ,
{ key : 'outboundDates' , label : '出库时间' , type : 'daterange' } ,
)
if ( props . service === 'transfer' ) fields . push (
{ key : 'reviewDates' , label : '审核时间' , type : 'daterange' } ,
{ key : 'completedDates' , label : '测量时间' , type : 'daterange' } ,
{ key : 'outboundDates' , label : '出库时间' , type : 'daterange' } ,
)
return fields
} )
@@ -170,7 +192,9 @@ const selectedRelabelInboundOrderId = ref('')
const relabelCandidatesLoading = ref ( false )
const transferInventoryCandidates = ref < RelabelInventoryCandidate [ ] > ( [ ] )
const selectedTransferInventory = ref ( '' )
const selectedTransferSourceOrderIds = ref < number [ ] > ( [ ] )
// Snowflake IDs exceed JavaScript's safe integer range. Keep them as strings
// all the way to JSON so the backend receives the exact source order ID.
const selectedTransferSourceOrderIds = ref < string [ ] > ( [ ] )
const returnRelabelSkuRows = ref < ReturnRelabelSkuRow [ ] > ( [ ] )
const isReturnRelabelSource = computed ( ( ) => props . service === 'labeling' && selectedRelabelInboundOrderId . value . startsWith ( 'RETURN_ORDER:' ) )
const selectedReturnOrderNo = computed ( ( ) => isReturnRelabelSource . value ? selectedRelabelInboundOrderId . value . slice ( 'RETURN_ORDER:' . length ) : '' )
@@ -267,7 +291,7 @@ onMounted(() => {
void nextTick ( refreshWarehouseMarkLabels )
} )
onBeforeUnmount ( ( ) => { clearCompletionPhotos ( ) ; clearMeasurementPhotos ( ) } )
function resetFilters ( ) { Object . assign ( filters , { number : '' , inboundNo : '' , warehouse : '' , destination : '' , status : '' , start : '' , end : '' } ) }
function resetFilters ( ) { Object . assign ( filters , { number : '' , inboundNo : '' , warehouse : '' , destination : '' , status : '' , start : '' , end : '' , reviewStart : '' , reviewEnd : '' , completedStart : '' , completedEnd : '' , measuredStart : '' , measuredEnd : '' , outboundStart : '' , outboundEnd : '' } ) }
function resetForm ( ) {
Object . assign ( form , { customerNo : '' , inboundNo : '' , sourceWarehouse : '' , warehouse : '' , destination : '' , privateDestinationAddress : '' , privateDestinationPostalCode : '' , privateDestinationContact : '' , privateDestinationPhone : '' , address : '' , contact : '' , phone : '' , pickupContact : '' , pickupPhone : '' , relabelContact : '' , relabelPhone : '' , deliveryMethod : '' , relabelDeliveryMethod : '' , trackingNo : '' , reason : '' , requirement : '' , relabel : false , remark : '' , labelDeadline : '' , estimatedArrivalAt : '' , estimatedShipAt : '' , fbaShipmentNo : '' , volumeCbm : 0.01 } )
cargoRows . value = [ ]
@@ -314,19 +338,21 @@ async function openCreate() {
function returnToList ( ) {
if ( props . createPage || props . detailPage ) {
const routeName = ( { labeling : 'overseas-labeling' , transfer : 'overseas-storage-transfer' , disposal : 'overseas-cargo-disposal' , relabel : 'overseas-return-relabel' } as Record < ServiceType , string > ) [ props . service ]
void router . push ( { name : routeName } )
void router . push ( { name : serviceListRouteName ( props . service ) } )
return
}
mode . value = 'list'
}
function serviceListRouteName ( service : ServiceType ) {
return ( { labeling : 'overseas-labeling' , transfer : 'overseas-storage-transfer' , disposal : 'overseas-cargo-disposal' , relabel : 'overseas-return-relabel' } as Record < ServiceType , string > ) [ service ]
}
async function openEdit ( transferNo : string ) {
try {
resetForm ( )
await loadWarehouses ( )
const detail = await getTransfer ( transferNo ) as ServiceDetail
if ( detail . status !== 'DRAFT' ) throw new Error ( '只有草稿状态的转运单可以编辑' )
if ( ! [ 'DRAFT' , 'REJECTED' ] . includes ( detail . status || '' ) ) throw new Error ( '只有草稿或审核驳回 状态的转运单可以编辑' )
editingTransferNo . value = transferNo
Object . assign ( form , {
warehouse : detail . operatingWarehouseId || '' , destination : detail . destinationWarehouseCode || detail . fbaWarehouseCode || '' ,
@@ -362,6 +388,20 @@ async function openEdit(transferNo: string) {
if ( form . warehouse ) {
try { selectedOperationWarehouse . value = await getMerchantWarehouseDetail ( form . warehouse ) } catch { selectedOperationWarehouse . value = null }
}
// The warehouse selector loads inventory candidates for a new transfer, but
// edit mode starts with an already populated warehouse and never emits the
// selector change event. Load the same candidates explicitly so the source
// inventory dropdown is usable when editing a draft.
if ( props . service === 'transfer' && form . warehouse ) {
await loadTransferInventoryCandidates ( form . warehouse )
const sourceInboundCode = String ( detail . inboundCode || '' )
if ( transferInventoryOptions . value . some ( ( option ) => option . value === sourceInboundCode ) ) {
selectedTransferInventory . value = sourceInboundCode
form . inboundNo = sourceInboundCode
selectedTransferSourceOrderIds . value = ( detail . sourceOrders || [ ] )
. map ( ( item ) => String ( item . lastlegOrderId || '' ) ) . filter ( Boolean )
}
}
mode . value = 'edit'
} catch ( error ) { ElMessage . error ( error instanceof Error ? error . message : '草稿加载失败' ) }
}
@@ -375,11 +415,25 @@ async function selectDiscardWarehouse() {
discardCandidatesLoading . value = true
try {
const inventory = await listRelabelInventoryCandidates ( form . warehouse )
const orderByInbound = new Map < string , string > ( )
const inboundCodes = [ ... new Set ( inventory . map ( ( candidate ) => String ( candidate . inboundCode || '' ) ) . filter ( Boolean ) ) ]
const historicalGroups = await Promise . all ( inboundCodes . map ( async ( inboundCode ) => ( { inboundCode , orders : await listRelabelHistoricalOrders ( form . warehouse , inboundCode ) } ) ) )
for ( const { inboundCode , orders } of historicalGroups ) {
for ( const order of orders ) {
if ( order . id ) orderByInbound . set ( inboundCode , String ( order . id ) )
}
}
const grouped = new Map < string , import ( ' .. / api / warehouseServices ' ) .DiscardCandidate > ( )
for ( const candidate of inventory ) {
const orderId = String ( candidate . inboundOrderId || '' )
if ( ! orderId ) continue
const current = grouped . get ( orderId ) || { orderId , inboundCode : candidate . inboundCode || '' , inboundOrderNo : candidate . inboundOrderNo || '' , overseasWarehouseId : String ( candidate . overseasWarehouseId || form . warehouse ) , overseasWarehouseName : candidate . overseasWarehouseName || '' , availablePackages : 0 , skus : [ ] }
const returnCartonId = String ( candidate . returnCartonId || '' )
const sourceType = candidate . sourceType === 'RETURN_CARTON' ? 'RETURN_CARTON' : 'DROPSHIP'
const inboundCode = String ( candidate . inboundCode || '' )
const orderId = sourceType === 'RETURN_CARTON' ? ` return: ${ returnCartonId } ` : ( orderByInbound . get ( inboundCode ) || '' )
if ( ! orderId || ( sourceType === 'RETURN_CARTON' && ! returnCartonId ) ) continue
const current = grouped . get ( orderId ) || { orderId , inboundCode , inboundOrderNo : candidate . inboundOrderNo || '' , overseasWarehouseId : String ( candidate . overseasWarehouseId || form . warehouse ) , overseasWarehouseName : candidate . overseasWarehouseName || '' , availablePackages : 0 , skus : [ ] }
current . sourceType = sourceType
current . returnCartonId = returnCartonId || undefined
current . cartonMark = candidate . cartonMark || undefined
const packages = inventoryQuantity ( candidate . availablePackages )
current . availablePackages += packages
const skuCode = String ( candidate . skuCode || '' ) . trim ( )
@@ -741,12 +795,13 @@ function importSelectedBindingsToDetails() {
function selectDiscardInbound ( orderId : string ) {
const candidate = discardCandidates . value . find ( ( item ) => item . orderId === orderId )
if ( ! candidate ) { cargoRows . value = [ ] ; return }
form . inboundNo = candidate . inboundCode
form . inboundNo = candidate . inboundCode || candidate . inboundOrderNo
form . warehouse = candidate . overseasWarehouseId
cargoRows . value = candidate . skus . map ( ( sku ) => ( {
id : ++ cargoSequence , sku : sku . skuCode , name : sku . productName , quantity : 0 ,
availableQuantity : sku . availablePackages , sourceOrderId : candidate . orderId ,
fnsku : '' , shipmentNo : '' , newSkuCode : '' , shippingMark : '' , customerCartonMarks : '' , cartonMark : '' , fbaCartonMark : '' , destinationWarehouseId : '' , remark : '' ,
availableQuantity : sku . availablePackages , sourceOrderId : candidate . orderId , sourceType : candidate . sourceType ,
returnCartonId : candidate . returnCartonId , cartonMark : candidate . cartonMark || '' ,
fnsku : '' , shipmentNo : '' , newSkuCode : '' , shippingMark : '' , customerCartonMarks : '' , fbaCartonMark : '' , destinationWarehouseId : '' , remark : '' ,
} ) )
}
function dateTime ( value : string , end = false ) { return value ? ` ${ value } T ${ end ? '23:59:59' : '00:00:00' } ` : undefined }
@@ -860,25 +915,25 @@ async function load() {
loading . value = true
try {
if ( props . service === 'labeling' ) {
const result = await listRelabels ( { page : page . value , size : pageSize . value , relabelNo : filters . number || undefined , overseasWarehouseId : filters . warehouse || undefined , status : filters . status || undefined , createdFrom : dateTime ( filters . start ) , createdTo : dateTime ( filters . end , true ) } )
const result = await listRelabels ( { page : page . value , size : pageSize . value , relabelNo : filters . number || undefined , overseasWarehouseId : filters . warehouse || undefined , status : filters . status || undefined , createdFrom : dateTime ( filters . start ) , createdTo : dateTime ( filters . end , true ) , reviewedFrom : dateTime ( filters . reviewStart ) , reviewedTo : dateTime ( filters . reviewEnd , true ) , completedFrom : dateTime ( filters . completedStart ) , completedTo : dateTime ( filters . completedEnd , true ) , outboundFrom : dateTime ( filters . outboundStart ) , outboundTo : dateTime ( filters . outboundEnd , true ) } )
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 , '—' , item . itemCount , item . totalPackageCount , statusLabel ( item . status ) , formatDateTime ( item . createdAt ) ] ) , _ _status : item . status , _ _confirmedAt : item . customerConfirmedAt || '' }
return { ... row ( [ item . relabelNo , item . customerOrderNo , destinationWarehouses , '—' , item . itemCount , item . totalPackageCount , 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 ) } )
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 ) } )
total . value = Number . isFinite ( Number ( result . total ) ) ? Number ( result . total ) : result . records . length
submittedRows . value = result . records . map ( ( item ) => row ( [ item . discardNo , item . inboundCodes , item . overseasWarehouseName || '—' , '—' , item . totalPackages , ` ${ item . currency } ${ item . totalFee } ` , statusLabel ( item . status ) , formatDateTime ( item . createdAt ) ] ) )
submittedRows . value = result . records . map ( ( item ) => row ( [ item . discardNo , item . outboundNo || '—' , item . inboundCodes , item . overseasWarehouseName || warehouseDisplay ( item . overseasWarehouseId ) || '—' , '—' , item . totalPackages , ` ${ item . currency } ${ item . totalFee } ` , statusLabel ( item . status ) , formatDateTime ( item . completedAt ) , formatDateTime ( item . createdAt ) ] ) )
} else {
const result = await listTransfers ( { page : page . value , size : pageSize . value , transferNo : filters . number || undefined , inboundCode : filters . inboundNo || undefined , operatingWarehouseId : filters . warehouse || undefined , fbaWarehouseCode : filters . destination || undefined , status : filters . status || undefined , ... ( props . service === 'relabel' ? { needRelabel : true } : { } ) , createdFrom : dateTime ( filters . start ) , createdTo : dateTime ( filters . end , true ) } )
const result = await listTransfers ( { page : page . value , size : pageSize . value , transferNo : filters . number || undefined , inboundCode : filters . inboundNo || undefined , operatingWarehouseId : filters . warehouse || undefined , fbaWarehouseCode : filters . destination || undefined , status : filters . status || undefined , ... ( props . service === 'relabel' ? { needRelabel : true } : { } ) , createdFrom : dateTime ( filters . start ) , createdTo : dateTime ( filters . end , true ) , reviewedFrom : dateTime ( filters . reviewStart ) , reviewedTo : dateTime ( filters . reviewEnd , true ) , measuredFrom : props . service === 'relabel' ? dateTime ( filters . measuredStart ) : dateTime ( filters . completedStart ) , measuredTo : props . service === 'relabel' ? dateTime ( filters . measuredEnd , true ) : dateTime ( filters . completedEnd , true ) , outboundFrom : dateTime ( filters . outboundStart ) , outboundTo : dateTime ( filters . outboundEnd , true ) } )
total . value = Number . isFinite ( Number ( result . total ) ) ? Number ( result . total ) : result . records . length
submittedRows . value = result . records . map ( ( item ) => {
const operationWarehouse = warehouseDisplay ( item . operatingWarehouseId )
const relabelStatus = transferRelabelStatus ( item )
return props . service === 'transfer'
? { ... row ( [ item . transferNo , item . packageCount , operationWarehouse , item . fbaWarehouseCode , deliveryMethodLabel ( item . deliveryMethod ) , item . needRelabel ? '是' : '否' , item . relabelNo , relabelStatus . label , statusLabel ( item . status ) , formatDateTime ( item . createdAt ) ] ) , _ _status : item . status , _ _statusLabel : statusLabel ( item . status ) , _ _relabelStatus : relabelStatus . code , _ _relabelStatusLabel : relabelStatus . label }
: row ( [ item . transferNo , '—' , item . fbaWarehouseCode , '—' , item . packageCount , '是' , item . relabelNo ? '已生成' : '待生成' , statusLabel ( item . status ) , formatDateTime ( item . createdAt ) ] )
? { ... row ( [ item . transferNo , item . packageCount , operationWarehouse , item . fbaWarehouseCode , deliveryMethodLabel ( item . deliveryMethod ) , item . needRelabel ? '是' : '否' , item . relabelNo , relabelStatus . label , statusLabel ( item . status ) , formatDateTime ( item . reviewedAt ) , formatDateTime ( item . measuredAt ) , formatDateTime ( item . outboundAt ) , formatDateTime ( item . createdAt ) ] ) , _ _status : item . status , _ _statusLabel : statusLabel ( item . status ) , _ _relabelStatus : relabelStatus . code , _ _relabelStatusLabel : relabelStatus . label }
: row ( [ item . transferNo , '—' , item . fbaWarehouseCode , '—' , item . packageCount , '是' , item . relabelNo ? '已生成' : '待生成' , statusLabel ( item . status ) , formatDateTime ( item . reviewedAt ) , formatDateTime ( item . measuredAt ) , formatDateTime ( item . outboundAt ) , formatDateTime ( item . createdAt ) ] )
} )
}
} catch ( error ) { submittedRows . value = [ ] ; total . value = 0 ; ElMessage . error ( error instanceof Error ? error . message : '列表加载失败' ) }
@@ -889,7 +944,7 @@ function resetListQuery() { resetFilters(); query() }
function listRowActions ( candidate : object ) : TableAction [ ] {
const item = candidate as Record < string , string >
const actions : TableAction [ ] = [ { key : 'detail' , label : '详情' , run : ( ) => openDetail ( item ) } ]
if ( canCreate . value && props . service === 'transfer' && item . _ _status === 'DRAFT' ) actions . push ( { key : 'edit' , label : '编辑' , run : ( ) => openEdit ( item [ '0' ] ) } )
if ( canCreate . value && props . service === 'transfer' && [ 'DRAFT' , 'REJECTED' ] . includes ( item . _ _status ) ) actions . push ( { key : 'edit' , label : '编辑' , run : ( ) => openEdit ( item [ '0' ] ) } )
if ( canConfirmTransfer . value && props . service === 'transfer' && item . _ _status === 'WAIT_CUSTOMER_CONFIRMATION' ) actions . push ( { key : 'confirm-measurement' , label : '确认测量' , run : ( ) => confirmTransferFromList ( item [ '0' ] ) } )
if ( canConfirmRelabel . value && props . service === 'labeling' && item . _ _status === 'CUSTOMER_CONFIRM_PENDING' && ! item . _ _confirmedAt ) actions . push ( { key : 'confirm-relabel' , label : '确认换标' , run : ( ) => confirmRelabelFromList ( item [ '0' ] ) } )
return actions
@@ -980,9 +1035,6 @@ async function submit(action: 'draft' | 'submit' = 'submit') {
if ( props . service === 'transfer' && transferDestinationType . value === 'FBA' && ! form . destination ) {
ElMessage . warning ( '请选择 FBA 目的仓' ) ; return
}
if ( props . service === 'transfer' && transferDestinationType . value === 'NONE' ) {
ElMessage . warning ( '请选择 FBA 仓或私人仓' ) ; return
}
if ( props . service === 'transfer' && transferDestinationType . value === 'PRIVATE' && ( ! form . privateDestinationAddress || ! form . privateDestinationPostalCode || ! form . privateDestinationContact || ! form . privateDestinationPhone ) ) {
ElMessage . warning ( '请完整填写私人仓地址、邮编、联系人和电话' ) ; return
}
@@ -1018,20 +1070,22 @@ async function submit(action: 'draft' | 'submit' = 'submit') {
if ( shippingLabelFile . value ) await uploadRelabelShippingLabel ( created . application . relabelNo , shippingLabelFile . value )
} catch ( error ) {
// Creation already committed: return to the list even when an attachment still needs retrying.
if ( props . createPage ) await router . push ( { name : 'overseas-labeling' } )
if ( props . createPage ) await router . push ( { name : serviceListRouteName ( props . service ) } )
else { mode . value = 'list' ; await load ( ) ; await openDetail ( { '0' : created . application . relabelNo } ) }
await ElMessageBox . alert ( ` 申请 ${ created . application . relabelNo } 已创建,部分附件上传失败: ${ error instanceof Error ? error . message : '上传失败' } 。请在原单详情补传失败附件,不要重复下单。 ` , '申请已创建,附件待补传' , { confirmButtonText : '知道了' } )
return
}
ElMessage . success ( ` ${ config . value . title } 提交成功 ` )
await router . push ( { name : 'overseas-labeling' } )
await router . push ( { name : serviceListRouteName ( props . service ) } )
return
} else if ( props . service === 'disposal' ) {
const items = cargoRows . value . filter ( ( item ) => item . quantity > 0 ) . map ( ( item ) => {
if ( ! item . sourceOrderId || item . sourceOrderId !== selectedDiscardOrderId . value || item . quantity > Number ( item . availableQuantity || 0 ) ) {
throw new Error ( ` SKU ${ item . sku } 的可废弃库存不足 ` )
}
return { orderId : item . sourceOrderId , skuCode : item . sku , discardPackages : item . quantity }
return { orderId : item . sourceType === 'RETURN_CARTON' ? undefined : item . sourceOrderId ,
returnCartonId : item . sourceType === 'RETURN_CARTON' ? item . returnCartonId : undefined ,
skuCode : item . sku , discardPackages : item . quantity }
} )
await createDiscard ( { items , idempotencyKey , remark : [ form . reason , form . remark ] . filter ( Boolean ) . join ( '; ' ) || undefined } )
} else {
@@ -1057,7 +1111,7 @@ async function submit(action: 'draft' | 'submit' = 'submit') {
if ( ! relabelNo ) {
ElMessage . success ( '转运单已提交审核;PDA 入库确认后将自动生成关联换标单' )
await ElMessageBox . alert ( ` 转运单 ${ transferNo } 已提交。关联换标单会在 PDA 完成入库、客户确认实测库存后自动生成;届时请进入关联换标单详情上传贴标总文件。 ` , '转运单已提交' , { confirmButtonText : '知道了' } )
await openDetail ( { '0' : transferNo } )
await router . push ( { name : serviceListRouteName ( 'transfer' ) } )
return
}
try {
@@ -1070,13 +1124,12 @@ async function submit(action: 'draft' | 'submit' = 'submit') {
}
}
ElMessage . success ( '转运单已提交审核' )
await openDetail ( { '0' : transferNo } )
await router . push ( { name : serviceListRouteName ( 'transfer' ) } )
return
}
}
ElMessage . success ( props . service === 'transfer' ? '转运单草稿保存成功' : ` ${ config . value . title } 提交成功 ` )
if ( props . createPage ) await router . push ( { name : props . service === 'relabel' ? 'overseas-return-relabel' : 'overseas-labeling' } )
else { mode . value = 'list' ; await load ( ) }
await router . push ( { name : serviceListRouteName ( props . service ) } )
} catch ( error ) { ElMessage . error ( error instanceof Error ? error . message : '提交失败' ) }
finally { saving . value = false }
}
@@ -1220,6 +1273,16 @@ async function loadWarehouses() {
} finally { fbaWarehousesLoading . value = false }
}
function warehouseLabel ( value : MerchantWarehouse ) { return ` ${ value . warehouseName } · ${ value . warehouseCode } ` }
async function loadTransferInventoryCandidates ( warehouseId : string ) {
transferInventoryCandidates . value = [ ]
if ( ! warehouseId ) return
relabelCandidatesLoading . value = true
try {
transferInventoryCandidates . value = await listRelabelInventoryCandidates ( warehouseId )
} catch ( error ) {
ElMessage . error ( error instanceof Error ? error . message : '当前海外仓库存来源加载失败' )
} finally { relabelCandidatesLoading . value = false }
}
async function selectTransferWarehouse ( ) {
selectedOperationWarehouse . value = null
form . pickupContact = ''
@@ -1235,7 +1298,7 @@ async function selectTransferWarehouse() {
form . relabelContact = detail . warehouse . contactName || ''
form . relabelPhone = detail . warehouse . contactPhone || ''
if ( form . deliveryMethod === 'CUSTOMER_DELIVERY' ) form . address = operationWarehouseAddress . value
transferInventoryCandidates . value = await listRelabelInventoryCandidates ( form . warehouse )
await loadTransferInventoryCandidates ( form . warehouse )
} catch ( error ) {
ElMessage . error ( error instanceof Error ? error . message : '中转仓联系信息加载失败' )
} finally { operationWarehouseDetailLoading . value = false }
@@ -1264,6 +1327,7 @@ async function resubmitRejectedRelabel() {
idempotencyKey : createIdempotencyKey ( ) ,
} )
ElMessage . success ( '贴标单已重新提交审核' )
await router . push ( { name : serviceListRouteName ( 'labeling' ) } )
} catch ( error ) {
if ( error !== 'cancel' && error !== 'close' ) ElMessage . error ( error instanceof Error ? error . message : '重新提交失败' )
}
@@ -1302,7 +1366,7 @@ async function selectTransferInventory(value: string) {
cargoRows . value = [ ... grouped . values ( ) ]
try {
const historical = await listRelabelHistoricalOrders ( form . warehouse , value )
const sourceIds = historical . map ( ( item ) => Number ( item . id ) ) . filter ( ( id ) => Number . isInteger ( id ) && id > 0 )
const sourceIds = historical . map ( ( item ) => String ( item . id || '' ) ) . filter ( Boolean )
selectedTransferSourceOrderIds . value = sourceIds
} catch ( error ) {
ElMessage . error ( error instanceof Error ? error . message : '库存来源订单加载失败' )
@@ -1405,11 +1469,11 @@ function selectRelabelInventory(inboundOrderId: string) {
< form : class = "{ 'warehouse-transfer-form': props.service === 'transfer', 'warehouse-transfer-form--relabel': props.service === 'transfer' && form.relabel }" @submit.prevent ="submit('submit')" >
< div class = "warehouse-card warehouse-form-section warehouse-transfer-main-card" > < h3 > { { props . service === 'transfer' ? '转运信息' : props . service === 'relabel' ? '退货信息' : props . service === 'labeling' ? '订单信息' : '基础信息' } } < / h3 >
< div class = "warehouse-form-grid" >
< label v-if = "props.service === 'disposal'" class="warehouse-span-2 "><span><i>*</i>操作仓</span><el-select v-model="form.warehouse" filterable clearable :loading="fbaWarehousesLoading" :disabled="Boolean(operationWarehousesError)" placeholder="请选择海外仓" @change="selectDiscardWarehouse"><el-option v-for="warehouse in operationWarehouses" :key="warehouse.id" :label="warehouseLabel(warehouse)" :value="String(warehouse.id)" /></el-select><small v-if="operationWarehousesError" class="warehouse-hint">{{ operationWarehousesError }}</small><small v-else class="warehouse-hint" > 请先选择本次废弃货物所在的海外仓 。 < / small > < / label >
< label v-if = "props.service === 'disposal'" class="warehouse-span-2 "><span><i>*</i>选择入库单</span><el-select v-model="selectedDiscardOrderId" filterable clearable :loading="discardCandidatesLoading" :disabled="!form.warehouse" no-data-text="当前海外仓暂无可废弃库存" placeholder="请先选择操作仓,再选择有可用库存的入库单" @change="selectDiscardInbound"><el-option v-for="candidate in discardCandidates" :key="candidate.orderId" :label="`${candidate.inboundOrderNo || candidate.inboundCode || '—'}| ${candidate.inboundCode || '—'}|可用 ${candidate.availablePackages} 箱`" :value="candidate.orderId" /></el-select><small class="warehouse-hint" > 仅 显示当前操作仓下一件代发中仍有可用箱数的入库单 。 < / small > < / label >
< label v-if = "props.service === 'disposal'" class="warehouse-disposal-field "><span><i>*</i>操作仓</span><el-select v-model="form.warehouse" filterable clearable :loading="fbaWarehousesLoading" :disabled="Boolean(operationWarehousesError)" placeholder="请选择海外仓" @change="selectDiscardWarehouse"><el-option v-for="warehouse in operationWarehouses" :key="warehouse.id" :label="warehouseLabel(warehouse)" :value="String(warehouse.id)" /></el-select><small v-if="operationWarehousesError" class="warehouse-hint">{{ operationWarehousesError }}</small><small v-else class="warehouse-hint" > 请先选择本次废弃货物所在的海外仓 。 < / small > < / label >
< label v-if = "props.service === 'disposal'" class="warehouse-disposal-field "><span><i>*</i>选择入库单</span><el-select v-model="selectedDiscardOrderId" filterable clearable :loading="discardCandidatesLoading" :disabled="!form.warehouse" no-data-text="当前海外仓暂无可废弃库存" placeholder="请先选择操作仓,再选择有可用库存的入库单" @change="selectDiscardInbound"><el-option v-for="candidate in discardCandidates" :key="candidate.orderId" :label="candidate.sourceType === 'RETURN_CARTON' ? `退货|${candidate.inboundOrderNo || '—'}| ${candidate.cartonMark || '—'}|可用 ${candidate.availablePackages}` : `${candidate.inboundOrderNo || candidate.inboundCode || '—'}| ${candidate.inboundCode || '—'}|可用 ${candidate.availablePackages} 箱`" :value="candidate.orderId" /></el-select><small class="warehouse-hint" > 显示当前操作仓内有可用数量的普通入库库存和退货库存 。 < / small > < / label >
< label v-if = "props.service === 'relabel'"><span><i>*</i>进仓编号</span><el-input v-model="form.inboundNo" placeholder="请输入已有入仓编号" / > < / label >
< label v-if = "props.service === 'relabel'"><span><i>*</i>来源 FBA 仓库</span><el-select v-model="form.sourceWarehouse" :loading="fbaWarehousesLoading" :disabled="Boolean(fbaWarehousesError)" placeholder="请选择平台维护的 FBA 仓"><el-option v-for="warehouse in fbaWarehouses" :key="warehouse.id" :label="warehouseLabel(warehouse)" :value="warehouse.warehouseCode" / > < / el-select > < / label >
< label v-else-if = "props.service === 'disposal'"><span>进仓编号 </span><el-inpu t v-model="form.inboundNo" readonly / > < / label >
< label v-else-if = "props.service === 'disposal'" class="warehouse-disposal-field"><span><i>*</i>废弃原因 </span><el-selec t v-model="form.reason" placeholder="请选择"><el-option label="客户退货" value="客户退货" /><el-option label="亚马逊移除" value="亚马逊移除" /><el-option label="残损不可售" value="残损不可售" / > < / el-select > < / label >
< label v-else > < span > < i > * < / i > { { props . service === 'labeling' ? '海外操作仓(中转仓)' : props . service === 'transfer' ? '入库海外仓' : '操作仓' } } < / span > < el-select v-model = "form.warehouse" :loading="fbaWarehousesLoading || operationWarehouseDetailLoading" :disabled="Boolean(operationWarehousesError)" clearable :placeholder="props.service === 'transfer' ? '请选择海外仓' : '请选择国外中转仓'" @change="props.service === 'labeling' ? selectOperationWarehouse() : props.service === 'transfer' && selectTransferWarehouse()"><el-option v-for="warehouse in operationWarehouses" :key="warehouse.id" :label="warehouseLabel(warehouse)" :value="String(warehouse.id)" /></el-select><small v-if="operationWarehousesError" class="warehouse-hint" > {{ operationWarehousesError }} < / small > < / label >
< label v-if = "props.service === 'labeling'" class="warehouse-span-2"><span><i>*</i>选择有库存的入仓单或退货单</span><el-select v-model="selectedRelabelInboundOrderId" filterable clearable :loading="relabelCandidatesLoading" :disabled="!form.warehouse" no-data-text="当前中转仓暂无可用库存" placeholder="请选择当前仍有可用库存的入仓单或退货单" @change="selectRelabelInventory"><el-option v-for="option in relabelInboundOptions" :key="option.value" :label="`${option.inboundOrderNo}| ${option.inboundCode}|可用 ${option.availablePackages} ${option.quantityUnit}${option.relabelledPackages > 0 ? `|已换标 ${option.relabelledPackages} ${option.quantityUnit}` : ''}`" :value="option.value" / > < / el-select > < / label >
< label v-if = "props.service === 'transfer'"><span>库存转运来源(可选)</span><el-select v-model="selectedTransferInventory" filterable clearable :loading="relabelCandidatesLoading" :disabled="!form.warehouse" no-data-text="当前海外仓暂无可用库存" placeholder="不选则按新货转运;选择后免 PDA 测量" @change="selectTransferInventory"><el-option v-for="option in transferInventoryOptions" :key="option.value" :label="`${option.label}|可用 ${option.packages} 箱`" :value="option.value" /></el-select><small class="warehouse-hint" > 库存转运可直接进入目的仓 、 换标和排车流程 , PDA 可拆托 、 重打或同目的仓合托 。 < / small > < / label >
@@ -1426,7 +1490,7 @@ function selectRelabelInventory(inboundOrderId: string) {
< / div >
< label v-if = "props.service === 'relabel'"><span><i>*</i>目的 FBA 仓</span><el-select v-model="form.destination" :loading="fbaWarehousesLoading" :disabled="Boolean(fbaWarehousesError)" placeholder="请选择平台维护的 FBA 仓"><el-option v-for="warehouse in fbaWarehouses" :key="warehouse.id" :label="warehouseLabel(warehouse)" :value="warehouse.warehouseCode" / > < / el-select > < / label >
< label v-if = "props.service === 'relabel'"><span><i>*</i>派送方式</span><el-select v-model="form.deliveryMethod" placeholder="请选择"><el-option label="卡车派送" value="卡车派送" /><el-option label="快递派送" value="快递派送" / > < / el-select > < / label >
< label v-if = "props.service === 'relabel' || props.service === 'disposal'"><span><i>*</i>{{ props.service === 'relabel' ? '退货原因' : '废弃原因' }} </span><el-select v-model="form.reason" placeholder="请选择"><el-option label="客户退货" value="客户退货" /><el-option label="亚马逊移除" value="亚马逊移除" /><el-option label="残损不可售" value="残损不可售" / > < / el-select > < / label >
< label v-if = "props.service === 'relabel'"><span><i>*</i>退货原因 </span><el-select v-model="form.reason" placeholder="请选择"><el-option label="客户退货" value="客户退货" /><el-option label="亚马逊移除" value="亚马逊移除" /><el-option label="残损不可售" value="残损不可售" / > < / el-select > < / label >
< label v-if = "props.service === 'labeling'"><span><i>*</i>海外仓签收联系人<em v-if="form.warehouse && !form.contact" class="warehouse-unmaintained">未维护</em></span><el-input v-model="form.contact" placeholder="选择海外操作仓后自动带出" / > < / label >
< label v-if = "props.service === 'labeling'"><span><i>*</i>海外仓联系电话<em v-if="form.warehouse && !form.phone" class="warehouse-unmaintained">未维护</em></span><el-input v-model="form.phone" placeholder="选择海外操作仓后自动带出" / > < / label >
< label v-if = "props.service === 'labeling'"><span><i>*</i>贴标截止时间</span><el-date-picker v-model="form.labelDeadline" type="datetime" value-format="YYYY-MM-DDTHH:mm:ss" placeholder="请选择截止时间" / > < / label >
@@ -1437,7 +1501,7 @@ function selectRelabelInventory(inboundOrderId: string) {
< label v-if = "props.service === 'transfer' || props.service === 'relabel'"><span><i>*</i>{{ props.service === 'transfer' ? '预估体积(CBM) ' : '预估总体积(CBM) ' }}</span><el-input-number v-model="form.volumeCbm" :min="0" :precision="4" :step="0.0001" controls-position="right" placeholder="请输入预估总体积" / > < / label >
< label v-if = "props.service === 'transfer' || props.service === 'relabel'"><span><i>*</i>{{ props.service === 'transfer' ? '预计入仓时间' : '预计到达时间' }}</span><el-date-picker v-model="form.estimatedArrivalAt" type="datetime" value-format="YYYY-MM-DDTHH:mm:ss" :placeholder="props.service === 'transfer' ? '请选择预计入仓时间' : '请选择预计到达时间'" / > < / label >
< label v-if = "props.service === 'transfer'"><span>期望发货时间</span><el-date-picker v-model="form.estimatedShipAt" type="datetime" value-format="YYYY-MM-DDTHH:mm:ss" placeholder="选填" / > < / label >
< div v-if = "props.service === 'transfer'" class="warehouse-choice-field warehouse-span-2"><span><i>*</i> 目的去向</span><el-radio-group v-model="transferDestinationType" @change="selectTransferDestinationType"><el-radio-button value="NONE">暂未确定</el-radio-button><el-radio-button value="FBA">FBA 仓</el-radio-button><el-radio-button value="PRIVATE" > 私人仓 < / el -radio -button > < / el-radio-group > < / div >
< div v-if = "props.service === 'transfer'" class="warehouse-choice-field warehouse-span-2"><span>目的去向</span><el-radio-group v-model="transferDestinationType" @change="selectTransferDestinationType"><el-radio-button value="NONE">暂未确定</el-radio-button><el-radio-button value="FBA">FBA 仓</el-radio-button><el-radio-button value="PRIVATE" > 私人仓 < / el -radio -button > < / el-radio-group > < / div >
< label v-if = "props.service === 'transfer' && transferDestinationType === 'FBA'"><span><i>*</i>FBA 目的仓</span><el-select v-model="form.destination" :loading="fbaWarehousesLoading" :disabled="Boolean(fbaWarehousesError)" filterable placeholder="请选择 FBA 仓"><el-option v-for="warehouse in fbaWarehouses" :key="warehouse.id" :label="warehouseLabel(warehouse)" :value="warehouse.warehouseCode" / > < / el-select > < / label >
< label v-if = "props.service === 'transfer' && transferDestinationType === 'FBA'"><span>FBA 货件编号</span><el-input v-model="form.fbaShipmentNo" placeholder="选填,可后续补充" / > < / label >
< label v-if = "props.service === 'transfer' && transferDestinationType === 'PRIVATE'"><span><i>*</i>私人仓地址</span><el-input v-model="form.privateDestinationAddress" maxlength="1000" placeholder="请输入私人仓完整地址" / > < / label >
@@ -1539,7 +1603,7 @@ function selectRelabelInventory(inboundOrderId: string) {
<header class=" warehouse - detail - header ">
<button type=" button " class=" warehouse - detail - back " @click=" returnToList "><ArrowLeft :size=" 16 " />返回列表</button>
<div class=" warehouse - detail - title "><strong>{{ props.service === 'labeling' ? selectedRelabelDetail?.application.relabelNo : selectedDetail?.transferNo || selectedDetail?.['0'] || `${config.title}详情` }}</strong><span>{{ config.title }}详情</span></div>
<div class=" warehouse - detail - actions "><el-button v-if=" canCreate && props . service === 'labeling' && selectedRelabelDetail ? . application . status === 'REJECTED' " type=" primary " @click=" resubmitRejectedRelabel ">修改后重新提交</el-button><el-button v-if=" canConfirmRelabel && props . service === 'labeling' && selectedRelabelDetail ? . application . status === 'CUSTOMER_CONFIRM_PENDING' && ! selectedRelabelDetail ? . application . customerConfirmedAt " type=" primary " @click=" confirmSelectedRelabel ">确认换标完成</el-button><el-button v-if=" canCancelDiscard && props . service === 'disposal' && selectedDetail ? . status === 'PENDING_REVIEW' " type=" danger " @click=" cancelSelectedDiscard ">取消申请</el-button><el-button v-if=" canCreate && props . service === 'transfer' && selectedDetail ? . status === 'DRAFT ' " type=" primary " @click=" openEdit ( String ( selectedDetail ? . transferNo || selectedDetail ? . [ '0' ] || '' ) ) ">编辑</el-button><el-button v-if=" canConfirmTransfer && props . service === 'transfer' && selectedDetail ? . status === 'WAIT_CUSTOMER_CONFIRMATION' " type=" primary " @click=" confirmTransferMeasurements ">确认测量数据</el-button><el-tag :type=" props . service === 'labeling' ? relabelStatusTagType ( selectedRelabelDetail ? . application . status || '' ) : undefined " effect=" light ">{{ statusLabel(props.service === 'labeling' ? selectedRelabelDetail?.application.status || '' : String(selectedDetail?.status || '')) }}</el-tag></div>
<div class=" warehouse - detail - actions "><el-button v-if=" canCreate && props . service === 'labeling' && selectedRelabelDetail ? . application . status === 'REJECTED' " type=" primary " @click=" resubmitRejectedRelabel ">修改后重新提交</el-button><el-button v-if=" canConfirmRelabel && props . service === 'labeling' && selectedRelabelDetail ? . application . status === 'CUSTOMER_CONFIRM_PENDING' && ! selectedRelabelDetail ? . application . customerConfirmedAt " type=" primary " @click=" confirmSelectedRelabel ">确认换标完成</el-button><el-button v-if=" canCancelDiscard && props . service === 'disposal' && selectedDetail ? . status === 'PENDING_REVIEW' " type=" danger " @click=" cancelSelectedDiscard ">取消申请</el-button><el-button v-if=" canCreate && props . service === 'transfer' && [ 'DRAFT' , 'REJECTED' ] . includes ( selectedDetail ? . status || '' ) " type=" primary " @click=" openEdit ( String ( selectedDetail ? . transferNo || selectedDetail ? . [ '0' ] || '' ) ) ">编辑</el-button><el-button v-if=" canConfirmTransfer && props . service === 'transfer' && selectedDetail ? . status === 'WAIT_CUSTOMER_CONFIRMATION' " type=" primary " @click=" confirmTransferMeasurements ">确认测量数据</el-button><el-tag :type=" props . service === 'labeling' ? relabelStatusTagType ( selectedRelabelDetail ? . application . status || '' ) : undefined " effect=" light ">{{ statusLabel(props.service === 'labeling' ? selectedRelabelDetail?.application.status || '' : String(selectedDetail?.status || '')) }}</el-tag></div>
</header>
<div v-if=" detailError " class=" warehouse - detail - error " role=" alert "><strong>{{ detailError }}</strong><el-button @click=" loadDetailPage ">重新加载</el-button></div>
<el-alert v-else-if=" props . service === 'labeling' && selectedRelabelDetail ? . application . status === 'REJECTED' " class=" warehouse - rejected - alert " type=" error " :closable=" false " show-icon title=" 贴标单审核已驳回 " :description=" selectedRelabelDetail . application . reviewRemark || '平台未填写驳回备注' " />
@@ -1628,6 +1692,7 @@ function selectRelabelInventory(inboundOrderId: string) {
. warehouse - page - size - select { width : 112 px ; margin - left : 8 px ; }
. warehouse - page - size - select : deep ( . el - input _ _inner ) , . warehouse - page - size - select : deep ( . el - select _ _selected - item ) { color : # 526176 ! important ; opacity : 1 ! important ; - webkit - text - fill - color : # 526176 ; }
. warehouse - list - actions { display : flex ; align - items : center ; gap : 8 px ; white - space : nowrap ; }
. warehouse - disposal - field { max - width : 520 px ; }
@ media ( max - width : 720 px ) {
. warehouse - pagination : deep ( . el - pagination ) { width : 100 % ; justify - content : flex - end ; }
. warehouse - transfer - main - card . warehouse - form - grid { grid - template - columns : minmax ( 0 , 1 fr ) ; }