@@ -13,7 +13,7 @@ import {
getTransferCartonLabel , submitTransfer , updateTransfer ,
confirmRelabelCompletion , confirmTransferMeasurement , downloadRelabelCompletionPhoto , downloadTransferMeasurementPhoto , downloadDiscardPhoto , type DiscardPhoto , uploadRelabelShippingLabel ,
uploadRelabelFile ,
listDiscards , listRelabelInventoryCandidates , listRelabelHistoricalOrders , listRelabels , listTransfers , cancelDiscard ,
listDiscards , listDiscardCandidates , listRelabelInventoryCandidates , listRelabelHistoricalOrders , listRelabels , listTransfers , cancelDiscard ,
type RelabelInventoryCandidate ,
type RelabelCreate ,
type RelabelDetail ,
@@ -33,7 +33,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 ; sourceType ? : 'DROPSHIP' | 'RETURN_CARTON' | 'RETURN_PARCEL' ; 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 { discardCartonIds ? : string [ ] ; id : number ; included ? : boolean ; sku : string ; name : string ; quantity : number ; availableQuantity ? : number ; sourceOrderId ? : string ; sourceType ? : 'DROPSHIP' | 'RETURN_CARTON' | 'RETURN_PARCEL' | 'INVENTORY' ; 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 ; shipmentNo : string ; destinationWarehouseId : string ; remark : string }
interface ImportedRelabelBinding extends SkuRelabelRow { templateType : RelabelTemplateType ; selected : boolean ; shipmentNo : string }
interface ServiceConfig { title : string ; createTitle : string ; numberLabel : string ; emptyText : string ; columns : string [ ] }
@@ -130,7 +130,7 @@ interface ServiceDetail extends Record<string, unknown> {
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 } >
items ? : Array < { cartonMarks ? : string [ ] ; inboundCode ? : string ; productName ? : string ; discardPackages ? : number ; 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
}
@@ -201,7 +201,48 @@ const listColumns = computed<DataTableColumn[]>(() => config.value.columns.map((
: { key : String ( index - 1 ) , prop : String ( index - 1 ) , label , minWidth : label === '关联业务单号' ? 300 : index === 1 || label === '换标单号' ? 190 : 130 ,
singleLine : label !== '关联业务单号' , className : label === '关联业务单号' ? 'related-business-number-cell' : undefined ,
slotName : props . service === 'labeling' && label === '换标类型' ? 'labelingType' : props . service === 'labeling' && label === '状态' ? 'labelingStatus' : props . service === 'transfer' && label === '换标状态' ? 'transferRelabelStatus' : props . service === 'transfer' && label === '状态' ? 'transferStatus' : undefined } ) )
const discardSelectedBoxKeys = ref < string [ ] > ( [ ] )
const discardBoxSearch = ref ( '' )
const discardCandidates = ref < import ( ' .. / api / warehouseServices ' ) .DiscardCandidate [ ] > ( [ ] )
const discardSelectedCandidate = computed ( ( ) => discardCandidates . value . find ( item => item . orderId === selectedDiscardOrderId . value ) )
const discardSelectionSources = computed ( ( ) => {
const current = discardSelectedCandidate . value
if ( ! current ) return [ ]
if ( current . sourceType !== 'RETURN_CARTON' ) return [ current ]
return discardCandidates . value . filter ( item => item . sourceType === current . sourceType && item . inboundCode === current . inboundCode && item . overseasWarehouseId === current . overseasWarehouseId )
} )
const discardSelectableBoxes = computed ( ( ) => discardSelectionSources . value . flatMap ( source => ( source . cartons || [ ] ) . map ( box => ( { ... box , key : ` ${ source . sourceType } : ${ box . cartonId } ` , source } ) ) ) )
const discardVisibleBoxes = computed ( ( ) => discardSelectableBoxes . value . filter ( box => ! discardBoxSearch . value . trim ( ) || box . cartonMark . includes ( discardBoxSearch . value . trim ( ) ) || box . skus . some ( sku => sku . skuCode . includes ( discardBoxSearch . value . trim ( ) ) ) ) )
function toggleDiscardBox ( key : string , selected : boolean ) {
discardSelectedBoxKeys . value = selected ? [ ... new Set ( [ ... discardSelectedBoxKeys . value , key ] ) ] : discardSelectedBoxKeys . value . filter ( value => value !== key )
syncDiscardSelection ( )
}
function selectAllDiscardBoxes ( selected : boolean ) {
discardSelectedBoxKeys . value = selected ? discardSelectableBoxes . value . map ( box => box . key ) : [ ]
syncDiscardSelection ( )
}
function syncDiscardSelection ( ) {
const selected = discardSelectableBoxes . value . filter ( box => discardSelectedBoxKeys . value . includes ( box . key ) )
const groups = new Map < string , CargoRow > ( )
for ( const box of selected ) {
const source = box . source
const skus = source . sourceType === 'RETURN_CARTON' ? source . skus : source . sourceType === 'INVENTORY' ? source . skus . slice ( 0 , 1 ) : box . skus . slice ( 0 , 1 )
for ( const sku of skus ) {
const key = ` ${ source . orderId } : ${ sku . skuCode } `
let row = groups . get ( key )
if ( ! row ) {
row = { ... createCargoRow ( ) , sku : sku . skuCode , name : sku . productName , quantity : 0 , availableQuantity : source . sourceType === 'RETURN_CARTON' ? sku . availablePackages : source . availablePackages ,
sourceOrderId : source . orderId , sourceType : source . sourceType , sourceInventoryPositionId : source . inventoryPositionId , returnCartonId : source . returnCartonId ,
palletNo : source . palletNo , discardCartonIds : source . sourceType === 'RETURN_CARTON' ? undefined : [ ] }
groups . set ( key , row )
}
row . quantity += source . sourceType === 'RETURN_CARTON' ? sku . availablePackages : 1
row . discardCartonIds ? . push ( box . cartonId )
}
}
cargoRows . value = [ ... groups . values ( ) ]
}
const selectedDiscardOrderId = ref ( '' )
const discardCandidatesLoading = ref ( false )
const relabelCandidates = ref < RelabelInventoryCandidate [ ] > ( [ ] )
@@ -459,6 +500,8 @@ async function openEdit(transferNo: string) {
} catch ( error ) { ElMessage . error ( error instanceof Error ? error . message : '草稿加载失败' ) }
}
async function selectDiscardWarehouse ( ) {
discardSelectedBoxKeys . value = [ ]
discardBoxSearch . value = ''
selectedDiscardOrderId . value = ''
form . inboundNo = ''
discardCandidates . value = [ ]
@@ -467,37 +510,13 @@ async function selectDiscardWarehouse() {
if ( ! form . warehouse ) return
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 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 ( )
if ( skuCode ) {
const sku = current . skus . find ( ( item ) => item . skuCode === skuCode )
if ( sku ) sku . availablePackages += packages
else current . skus . push ( { skuCode , productName : candidate . productNameZh || '' , availablePackages : packages } )
}
grouped . set ( orderId , current )
}
discardCandidates . value = [ ... grouped . values ( ) ] . filter ( ( candidate ) => String ( candidate . overseasWarehouseId ) === form . warehouse )
const warehouse = form . warehouse
const candidates = await listDiscardCandidates ( )
if ( form . warehouse !== warehouse ) return
discardCandidates . value = candidates
. filter ( ( candidate ) => String ( candidate . overseasWarehouseId ) === warehouse )
. filter ( ( candidate ) => candidate . sourceType === 'DROPSHIP' ? Boolean ( candidate . orderId ) : candidate . sourceType === 'RETURN_CARTON' ? Boolean ( candidate . returnCartonId ) : candidate . sourceType === 'INVENTORY' && Boolean ( candidate . inventoryPositionId ) )
. map ( ( candidate ) => ( { ... candidate , orderId : candidate . sourceType === 'RETURN_CARTON' ? ` return: ${ candidate . returnCartonId } ` : candidate . sourceType === 'INVENTORY' ? ` inventory: ${ candidate . inventoryPositionId } ` : String ( candidate . orderId ) } ) )
} catch ( error ) { ElMessage . error ( error instanceof Error ? error . message : '可废弃入库单加载失败' ) }
finally { discardCandidatesLoading . value = false }
}
@@ -865,14 +884,17 @@ function importSelectedBindingsToDetails() {
ElMessage . success ( ` 已将 ${ selected . length } 条绑定关系导入本次换标明细 ` )
}
function selectDiscardInbound ( orderId : string ) {
discardSelectedBoxKeys . value = [ ]
discardBoxSearch . value = ''
const candidate = discardCandidates . value . find ( ( item ) => item . orderId === orderId )
if ( ! candidate ) { cargoRows . value = [ ] ; return }
form . inboundNo = candidate . inboundCode || candidate . inboundOrderNo
form . warehouse = candidate . overseasWarehouseId
if ( candidate . cartonSelectionSupported ) { selectAllDiscardBoxes ( true ) ; return }
cargoRows . value = candidate . skus . map ( ( sku ) => ( {
id : ++ cargoSequence , sku : sku . skuCode , name : sku . productName , quantity : 0 ,
id : ++ cargoSequence , sku : sku . skuCode , name : sku . productName , quantity : sku . availablePackages ,
availableQuantity : sku . availablePackages , sourceOrderId : candidate . orderId , sourceType : candidate . sourceType ,
returnCartonId : candidate . returnCartonId , cartonMark : candidate . cartonMark || '' ,
returnCartonId : candidate . returnCartonId , sourceInventoryPositionId : candidate . inventoryPositionId , palletNo : candidate . palletNo , cartonMark : candidate . cartonMark || '' ,
fnsku : '' , shipmentNo : '' , newSkuCode : '' , shippingMark : '' , customerCartonMarks : '' , fbaCartonMark : '' , destinationWarehouseId : '' , remark : '' ,
} ) )
}
@@ -981,7 +1003,7 @@ function transferRelabelStatusTagType(status: string): StatusTagType {
}
function deliveryMethodLabel ( value : string ) {
const labels : Record < string , string > = {
TRUCK : '卡车派送' , EXPRESS : '快递派送' , TRUCK _PICKUP : '卡车 自提' , EXPRESS _PICKUP : '快递 自提' ,
TRUCK : '卡车派送' , EXPRESS : '快递派送' , AMAZON _PICKUP : '亚马逊 自提' , CUSTOMER _PICKUP : '客户 自提' ,
WAREHOUSE _PICKUP : '仓库上门提货' , CUSTOMER _DELIVERY : '客户送货到仓' ,
// Keep the legacy value readable for historical applications created before the four-way split.
PICKUP : '仓库自提' ,
@@ -1067,7 +1089,7 @@ async function submit(action: 'draft' | 'submit' = 'submit') {
if ( props . service === 'labeling' && labelingMode . value !== 'sku' && ! form . destination ) {
ElMessage . warning ( '请选择本张贴标申请统一使用的 FBA 目的仓' ) ; return
}
if ( props . service === 'labeling' && labelingMode . value !== 'sku' && ( form . deliveryMethod === 'EXPRESS' || form . deliveryMethod === 'EXPRESS_PICKUP' )
if ( props . service === 'labeling' && labelingMode . value !== 'sku' && form . deliveryMethod === 'EXPRESS'
&& form . relabel && ! shippingLabelFile . value ) {
ElMessage . warning ( '选择打印面单时必须上传面单文件' ) ; return
}
@@ -1119,8 +1141,8 @@ async function submit(action: 'draft' | 'submit' = 'submit') {
const selectedOperationType = labelingMode . value === 'carton' ? 'CARTON_LABEL' : labelingMode . value === 'sku' ? 'SKU_LABEL' : 'CARTON_AND_SKU'
if ( items . some ( item => item . operationType !== selectedOperationType ) ) throw new Error ( '一张申请只能选择一种换标类型,请核对绑定明细' )
const created = await createRelabel ( { customerOrderNo : sourceInboundOrders || '贴标申请' , operationWarehouseId : form . warehouse , labelDeadline : form . labelDeadline , contactName : form . contact , contactPhone : form . phone , remark : form . remark || undefined , idempotencyKey , items ,
deliveryMethod : labelingMode . value === 'sku' ? undefined : form . deliveryMethod as 'TRUCK' | 'EXPRESS' | 'TRUCK _PICKUP' | 'EXPRESS _PICKUP' ,
printShippingLabel : labelingMode . value !== 'sku' && ( form . deliveryMethod === 'EXPRESS' || form . deliveryMethod === 'EXPRESS_PICKUP' ) && form . relabel } )
deliveryMethod : labelingMode . value === 'sku' ? undefined : form . deliveryMethod as 'TRUCK' | 'EXPRESS' | 'AMAZON _PICKUP' | 'CUSTOMER _PICKUP' ,
printShippingLabel : labelingMode . value !== 'sku' && form . deliveryMethod === 'EXPRESS' && form . relabel } )
try {
if ( cartonTotalFile . value ) await uploadRelabelFile ( created . application . relabelNo , 'CARTON_TOTAL' , cartonTotalFile . value )
if ( skuTotalFile . value ) await uploadRelabelFile ( created . application . relabelNo , 'SKU_TOTAL' , skuTotalFile . value )
@@ -1137,12 +1159,13 @@ async function submit(action: 'draft' | 'submit' = 'submit') {
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 ) ) {
if ( ! item . sourceOrderId || ! discardSelectionSources . value . some ( source => source . orderId = == item . sourceOrderId ) || item . quantity > Number ( item . availableQuantity || 0 ) ) {
throw new Error ( ` SKU ${ item . sku } 的可废弃库存不足 ` )
}
return { orderId : item . sourceType === 'RETURN_CARTON ' ? undefined : item . sourceOrderId ,
return { orderId : item . sourceType === 'DROPSHIP ' ? item . sourceOrderId : undefined ,
inventoryPositionId : item . sourceType === 'INVENTORY' ? item . sourceInventoryPositionId : undefined ,
returnCartonId : item . sourceType === 'RETURN_CARTON' ? item . returnCartonId : undefined ,
skuCode : item . sku , discardPackages : item . quantity }
skuCode : item . sku , discardPackages : item . quantity , cartonIds : item . discardCartonIds }
} )
await createDiscard ( { items , idempotencyKey , remark : [ form . reason , form . remark ] . filter ( Boolean ) . join ( '; ' ) || undefined } )
} else {
@@ -1151,7 +1174,7 @@ async function submit(action: 'draft' | 'submit' = 'submit') {
const transferRemark = [ form . remark , form . relabel && form . requirement ? ` 换标要求: ${ form . requirement } ` : '' ] . filter ( Boolean ) . join ( '; ' )
const operationType = labelingMode . value === 'carton' ? 'CARTON_LABEL' : labelingMode . value === 'sku' ? 'SKU_LABEL' : 'CARTON_AND_SKU'
const selectedFba = fbaWarehouses . value . find ( ( warehouse ) => warehouse . warehouseCode === form . destination )
const payload : TransferCreate = { operatingWarehouseId : primaryWarehouse , destinationType : transferDestinationType . value , fbaWarehouseCode : transferDestinationType . value === 'FBA' ? form . destination || undefined : undefined , destinationWarehouseId : transferDestinationType . value === 'FBA' ? String ( selectedFba ? . id || '' ) || undefined : undefined , destinationWarehouseCode : transferDestinationType . value === 'FBA' ? form . destination || undefined : undefined , destinationWarehouseName : transferDestinationType . value === 'FBA' ? selectedFba ? . warehouseName : undefined , privateDestinationAddress : transferDestinationType . value === 'PRIVATE' ? form . privateDestinationAddress : undefined , privateDestinationPostalCode : transferDestinationType . value === 'PRIVATE' ? form . privateDestinationPostalCode : undefined , privateDestinationContactName : transferDestinationType . value === 'PRIVATE' ? form . privateDestinationContact : undefined , privateDestinationContactPhone : transferDestinationType . value === 'PRIVATE' ? form . privateDestinationPhone : undefined , deliveryAddress : form . address , packageCount , volumeCbm : form . volumeCbm , deliveryMethod : form . deliveryMethod , estimatedArrivalAt : form . estimatedArrivalAt , estimatedShipAt : form . estimatedShipAt || undefined , fbaShipmentNo : transferDestinationType . value === 'FBA' ? form . fbaShipmentNo || undefined : undefined , trackingNo : form . deliveryMethod === 'CUSTOMER_DELIVERY' ? form . trackingNo || undefined : undefined , fbaBoxNos : customerCartonMarks . length ? customerCartonMarks . join ( ',' ) : undefined , remark : transferRemark || undefined , needRelabel : props . service === 'relabel' || form . relabel , labelDeadline : form . relabel ? form . labelDeadline : undefined , pickupContactName : form . deliveryMethod === 'WAREHOUSE_PICKUP' ? form . pickupContact : undefined , pickupContactPhone : form . deliveryMethod === 'WAREHOUSE_PICKUP' ? form . pickupPhone : undefined , relabelContactName : form . relabel ? form . relabelContact : undefined , relabelContactPhone : form . relabel ? form . relabelPhone : undefined , relabelDeliveryMethod : form . relabel && labelingMode . value !== 'sku' ? form . relabelDeliveryMethod as 'TRUCK' | 'EXPRESS' | 'TRUCK _PICKUP' | 'EXPRESS _PICKUP' : undefined , idempotencyKey , items : cargoRows . value . map ( ( item ) => ( { skuCode : item . sku , productNameZh : item . name , packageCount : item . quantity , shippingMark : transferCustomerMarks ( item ) . join ( ',' ) || undefined } ) ) , relabelItems : form . relabel ? transferRelabelRows . value . map ( ( item ) => ( { operationType , customerCartonMarks : transferCustomerMarks ( item ) , originalSku : item . sku , newSku : item . newSkuCode || undefined , quantity : item . quantity , fbaCartonMark : item . fbaCartonMark || undefined , destinationWarehouseCode : transferRelabelDefaultDestination . value || undefined , remark : item . remark || undefined } ) ) : undefined }
const payload : TransferCreate = { operatingWarehouseId : primaryWarehouse , destinationType : transferDestinationType . value , fbaWarehouseCode : transferDestinationType . value === 'FBA' ? form . destination || undefined : undefined , destinationWarehouseId : transferDestinationType . value === 'FBA' ? String ( selectedFba ? . id || '' ) || undefined : undefined , destinationWarehouseCode : transferDestinationType . value === 'FBA' ? form . destination || undefined : undefined , destinationWarehouseName : transferDestinationType . value === 'FBA' ? selectedFba ? . warehouseName : undefined , privateDestinationAddress : transferDestinationType . value === 'PRIVATE' ? form . privateDestinationAddress : undefined , privateDestinationPostalCode : transferDestinationType . value === 'PRIVATE' ? form . privateDestinationPostalCode : undefined , privateDestinationContactName : transferDestinationType . value === 'PRIVATE' ? form . privateDestinationContact : undefined , privateDestinationContactPhone : transferDestinationType . value === 'PRIVATE' ? form . privateDestinationPhone : undefined , deliveryAddress : form . address , packageCount , volumeCbm : form . volumeCbm , deliveryMethod : form . deliveryMethod , estimatedArrivalAt : form . estimatedArrivalAt , estimatedShipAt : form . estimatedShipAt || undefined , fbaShipmentNo : transferDestinationType . value === 'FBA' ? form . fbaShipmentNo || undefined : undefined , trackingNo : form . deliveryMethod === 'CUSTOMER_DELIVERY' ? form . trackingNo || undefined : undefined , fbaBoxNos : customerCartonMarks . length ? customerCartonMarks . join ( ',' ) : undefined , remark : transferRemark || undefined , needRelabel : props . service === 'relabel' || form . relabel , labelDeadline : form . relabel ? form . labelDeadline : undefined , pickupContactName : form . deliveryMethod === 'WAREHOUSE_PICKUP' ? form . pickupContact : undefined , pickupContactPhone : form . deliveryMethod === 'WAREHOUSE_PICKUP' ? form . pickupPhone : undefined , relabelContactName : form . relabel ? form . relabelContact : undefined , relabelContactPhone : form . relabel ? form . relabelPhone : undefined , relabelDeliveryMethod : form . relabel && labelingMode . value !== 'sku' ? form . relabelDeliveryMethod as 'TRUCK' | 'EXPRESS' | 'AMAZON _PICKUP' | 'CUSTOMER _PICKUP' : undefined , idempotencyKey , items : cargoRows . value . map ( ( item ) => ( { skuCode : item . sku , productNameZh : item . name , packageCount : item . quantity , shippingMark : transferCustomerMarks ( item ) . join ( ',' ) || undefined } ) ) , relabelItems : form . relabel ? transferRelabelRows . value . map ( ( item ) => ( { operationType , customerCartonMarks : transferCustomerMarks ( item ) , originalSku : item . sku , newSku : item . newSkuCode || undefined , quantity : item . quantity , fbaCartonMark : item . fbaCartonMark || undefined , destinationWarehouseCode : transferRelabelDefaultDestination . value || undefined , remark : item . remark || undefined } ) ) : undefined }
if ( props . service === 'transfer' && selectedTransferInventory . value ) {
; ( payload as TransferCreate ) . inboundCode = selectedTransferInventory . value
; ( payload as TransferCreate ) . sourceLastlegOrderIds = selectedTransferSourceOrderIds . value
@@ -1536,7 +1559,7 @@ function addReturnBoxBinding(source: RelabelInventoryCandidate) {
< / div >
< div v-show = "props.service !== 'labeling' || !orderInfoCollapsed" class="warehouse-form-grid" >
< 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 === '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.palletNo || '散箱'}| 可用 ${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'" class="warehouse-disposal-field"><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 >
@@ -1560,9 +1583,9 @@ function addReturnBoxBinding(source: RelabelInventoryCandidate) {
< 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 >
< label v-if = "props.service === 'labeling' && labelingMode !== 'sku'"><span><i>*</i>换标后派送方式</span><el-select v-model="form.deliveryMethod" placeholder="请选择派送方式"><el-option label="卡车派送" value="TRUCK" /><el-option label="快递派送" value="EXPRESS" /><el-option label="卡车 自提" value="TRUCK _PICKUP" /><el-option label="快递 自提" value="EXPRESS _PICKUP" / > < / el-select > < / label >
< div v-if = "props.service === 'labeling' && labelingMode !== 'sku' && ( form.deliveryMethod === 'EXPRESS' || form.deliveryMethod === 'EXPRESS_PICKUP') " class="warehouse-checkbox-field"><span>是否打印面单</span><el-checkbox v-model="form.relabel" > 需要仓库打印面单 < / el -checkbox > < / div >
< label v-if = "canManageRelabelFiles && props.service === 'labeling' && labelingMode !== 'sku' && ( form.deliveryMethod === 'EXPRESS' || form.deliveryMethod === 'EXPRESS_PICKUP') && form.relabel"><span><i>*</i>上传面单文件</span><input class="warehouse-native-file" type="file" accept="application/pdf,image/*,.pdf" @change="selectShippingLabel" > < / label >
< label v-if = "props.service === 'labeling' && labelingMode !== 'sku'"><span><i>*</i>换标后派送方式</span><el-select v-model="form.deliveryMethod" placeholder="请选择派送方式"><el-option label="卡车派送" value="TRUCK" /><el-option label="快递派送" value="EXPRESS" /><el-option label="亚马逊 自提" value="AMAZON _PICKUP" /><el-option label="客户 自提" value="CUSTOMER _PICKUP" / > < / el-select > < / label >
< div v-if = "props.service === 'labeling' && labelingMode !== 'sku' && form.deliveryMethod === 'EXPRESS'" class="warehouse-checkbox-field"><span>是否打印面单</span><el-checkbox v-model="form.relabel" > 需要仓库打印面单 < / el -checkbox > < / div >
< label v-if = "canManageRelabelFiles && props.service === 'labeling' && labelingMode !== 'sku' && form.deliveryMethod === 'EXPRESS' && form.relabel"><span><i>*</i>上传面单文件</span><input class="warehouse-native-file" type="file" accept="application/pdf,image/*,.pdf" @change="selectShippingLabel" > < / label >
< label v-if = "props.service === 'relabel'" class="warehouse-span-2"><span><i>*</i>详细派送地址</span><el-input v-model="form.address" placeholder="请输入派送地址" / > < / label >
< 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 >
@@ -1639,13 +1662,22 @@ function addReturnBoxBinding(source: RelabelInventoryCandidate) {
<div v-else class=" warehouse - table - scroll "><table class=" warehouse - edit - table warehouse - sku - label - table "><thead><tr><th>仓库唛头 *</th><th>旧 SKU *</th><th>新 SKU *</th><th>换标数量(件)*</th><th v-if=" labelingMode === 'carton_and_sku' ">新 FBA 箱唛 *</th><th v-if=" labelingMode === 'carton_and_sku' ">FBA 货件编号 *</th><th>备注</th><th>操作</th></tr></thead><tbody><tr v-for=" row in skuRelabelRows " :key=" row . id "><td><el-input v-model=" row . cartonMark " :readonly=" isReturnRelabelSource " placeholder=" 请输入仓库唛头 " @change=" applyExistingCartonBinding ( row ) " /></td><td><el-input v-model=" row . originalSku " :readonly=" isReturnRelabelSource " placeholder=" 请输入旧 SKU " @change=" applyExistingSkuBinding ( row ) " /></td><td><el-input v-model=" row . newSku " placeholder=" 请输入新 SKU " /></td><td><el-input-number v-model=" row . quantity " :min=" 1 " :precision=" 0 " controls-position=" right " placeholder=" 请输入件数 " /></td><td v-if=" labelingMode === 'carton_and_sku' "><el-input v-model=" row . fbaCartonMark " placeholder=" 请输入新的 FBA 箱唛 " /></td><td v-if=" labelingMode === 'carton_and_sku' "><el-input v-model=" row . shipmentNo " placeholder=" 请输入 FBA 货件编号 " /></td><td><el-input v-model=" row . remark " placeholder=" 请输入备注 " /></td><td><button type=" button " class=" warehouse - delete " title=" 删除 " @click=" removeSkuRelabelRow ( row . id ) "><Trash2 :size=" 16 " />删除</button></td></tr><tr v-if=" skuRelabelRows . length === 0 "><td :colspan=" labelingMode === 'carton_and_sku' ? 8 : 6 "><div class=" warehouse - empty ">暂无绑定明细,请点击“添加 SKU 明细”</div></td></tr></tbody></table></div>
</template>
<template v-else>
<div class=" warehouse - section - heading " :class=" { 'warehouse-step-heading' : props . service === 'transfer' } "><div><h3><span v-if=" props . service === 'transfer' " class=" warehouse - step - number ">1</span>货物明细</h3></div><div v-if=" props . service !== 'disposal' " class=" warehouse - heading - actions "><el-button v-if=" props . service === 'transfer' " size=" small " :disabled=" ! cargoRows . some ( ( row ) => row . sku . trim ( ) && row . quantity > 0 ) " @ click=" printTransferDraftCartonMarks "><Printer :size=" 15 " />打印唛头</el-button><el-button type=" primary " size=" small " @click=" addCargoRow "><Plus :size=" 15 " />添加行</el-button><el-button size=" small " @click=" cargoRows = [ ] ">清空</el-button></div></div >
<div class=" warehouse - table - scroll "><table class=" warehouse - edit - table "><thead><tr><th>商品 SKU{{ props.service === 'transfer' ? '' : ' *' }}</th><th>中文品名{{ props.service === 'transfer' ? '' : ' *' }}</th><th v-if=" props . service === 'disposal' ">可用箱数</th><th v-if=" props . service === 'relabel' ">FNSKU / 箱唛</th><th>{{ props.service === 'disposal' ? '本次废弃箱数' : '预计箱数' }} *</th><th v-if=" props . service !== 'disposal' ">操作</th></tr></thead><tbody><tr v-for=" row in cargoRows " :key=" row . id "><td><el-input v-model=" row . sku " :readonly=" props . service === 'disposal' " :placeholder=" props . service === 'transfer' ? '选填' : '请输入商品 SKU' " /></td><td><el-input v-model=" row . name " :readonly=" props . service === 'disposal' " :placeholder=" props . service === 'transfer' ? '选填' : '请输入中文品名' " /></td><td v-if=" props . service === 'disposal' "><strong>{{ row.availableQuantity }} 箱</strong></td><td v-if=" props . service === 'relabel' "><el-input v-model=" row . shippingMark " placeholder=" 请输入FNSKU或箱唛 " /></td><td><el-input-number v-model=" row . quantity " :min=" props . service === 'disposal' ? 0 : 1 " :max=" row . availableQuantity " controls-position=" right " placeholder=" 请输入箱数 " /></td><td v-if=" props . service !== 'disposal' "><button type=" button " class=" warehouse - delete " title=" 删除 " @click=" removeCargoRow ( row . id ) "><Trash2 :size=" 16 " />删除</button></td></tr><tr v-if=" cargoRows . length === 0 "><td :colspan=" props . service === 'disposal' ? 4 : props . service === 'relabel' ? 5 : 4 "><div class=" warehouse - empty ">{{ props.service === 'disposal' ? '请先选择入库单,系统将自动带出 SKU 和可用箱数' : '暂无货物明细,请点击“添加行”' }}</div></td></tr></tbody></table ></div>
<section v-if=" props . service === 'disposal' && discardSelectedCandidate " class=" warehouse - discard - boxes " >
<div class=" warehouse - section - heading "><div><h3>选择废弃箱子</h3><small class=" warehouse - hint ">选中箱子整体废弃,箱内所有 SKU 一起出库;未选箱子保留库存。</small></div><div v-if=" discardSelectedCandidate . cartonSelectionSupported " class=" warehouse - heading - actions "><el-button size=" small " @click=" selectAllDiscardBoxes ( true ) ">全选</el-button><el-button size=" small " @click=" selectAllDiscardBoxes ( false ) ">清空选择</el-button></div ></div>
<template v-if=" discardSelectedCandidate . cartonSelectionSupported ">
<div class=" warehouse - discard - box - toolbar "><el-input v-model=" discardBoxSearch " clearable placeholder=" 搜索箱唛或 SKU " /><strong>已选 {{ discardSelectedBoxKeys.length }} / {{ discardSelectableBoxes.length }} 箱</strong></div>
<div class=" warehouse - table - scroll "><table class=" warehouse - edit - table "><thead><tr><th>选择</th><th>箱唛</th><th>所属托盘</th><th>箱内 SKU</th><th>中文品名</th></tr></thead><tbody><tr v-for=" box in discardVisibleBoxes " :key=" box . key "><td><el-checkbox :model-value=" discardSelectedBoxKeys . includes ( box . key ) " :aria-label=" ` 选择废弃箱子 ${ box . cartonMark } ` " @change=" toggleDiscardBox ( box . key , Boolean ( $event ) ) " /></td><td><strong>{{ box.cartonMark }}</strong></td><td>{{ box.palletNo || '散箱' }}</td><td>{{ box.skus.map(sku => sku.skuCode).join('、') }}</td><td>{{ box.skus.map(sku => sku.productName || '—').join('、') }}</td></tr><tr v-if=" ! discardVisibleBoxes . length "><td colspan=" 5 "><div class=" warehouse - empty ">暂无匹配箱唛</div></td></tr></tbody></table></div>
<small v-if=" ! discardSelectedBoxKeys . length " class=" warehouse - hint ">请至少选择一个箱子。</small>
</template>
<el-alert v-else type=" info " :closable=" false " :title=" discardSelectedCandidate . cartonSelectionBlockReason || '当前库存缺少完整箱级记录,暂时只能选择全部库存废弃。' " />
</section>
<div v-if=" props . service !== 'disposal' || ! discardSelectedCandidate ? . cartonSelectionSupported " class=" warehouse - section - heading " :class=" { 'warehouse-step-heading' : props . service === 'transfer' } "><div><h3><span v-if=" props . service === 'transfer' " class=" warehouse - step - number ">1</span>货物明细</h3></div><div v-if=" props . service !== 'disposal' " class=" warehouse - heading - actions "><el-button v-if=" props . service === 'transfer' " size=" small " :disabled=" ! cargoRows . some ( ( row ) => row . sku . trim ( ) && row . quantity > 0 ) " @click=" printTransferDraftCartonMarks "><Printer :size=" 15 " />打印唛头</el-button><el-button type=" primary " size=" small " @click=" addCargoRow "><Plus :size=" 15 " />添加行</el-button><el-button size=" small " @click=" cargoRows = [ ] ">清空</el-button></div></div>
<div v-if=" props . service !== 'disposal' || ! discardSelectedCandidate ? . cartonSelectionSupported " class=" warehouse - table - scroll "><table class=" warehouse - edit - table "><thead><tr><th>商品 SKU{{ props.service === 'transfer' ? '' : ' *' }}</th><th>中文品名{{ props.service === 'transfer' ? '' : ' *' }}</th><th v-if=" props . service === 'disposal' ">可用箱数</th><th v-if=" props . service === 'relabel' ">FNSKU / 箱唛</th><th>{{ props.service === 'disposal' ? '本次废弃箱数' : '预计箱数' }} *</th><th v-if=" props . service !== 'disposal' ">操作</th></tr></thead><tbody><tr v-for=" row in cargoRows " :key=" row . id "><td><el-input v-model=" row . sku " :readonly=" props . service === 'disposal' " :placeholder=" props . service === 'transfer' ? '选填' : '请输入商品 SKU' " /></td><td><el-input v-model=" row . name " :readonly=" props . service === 'disposal' " :placeholder=" props . service === 'transfer' ? '选填' : '请输入中文品名' " /></td><td v-if=" props . service === 'disposal' "><strong>{{ row.availableQuantity }} 箱</strong></td><td v-if=" props . service === 'relabel' "><el-input v-model=" row . shippingMark " placeholder=" 请输入FNSKU或箱唛 " /></td><td><el-input-number v-model=" row . quantity " :disabled=" props . service === 'disposal' " :min=" props . service === 'disposal' ? 0 : 1 " :max=" row . availableQuantity " controls-position=" right " placeholder=" 请输入箱数 " /></td><td v-if=" props . service !== 'disposal' "><button type=" button " class=" warehouse - delete " title=" 删除 " @click=" removeCargoRow ( row . id ) "><Trash2 :size=" 16 " />删除</button></td></tr><tr v-if=" cargoRows . length === 0 "><td :colspan=" props . service === 'disposal' ? 4 : props . service === 'relabel' ? 5 : 4 "><div class=" warehouse - empty ">{{ props.service === 'disposal' ? '请先选择入库单,系统将自动带出 SKU 和可用箱数' : '暂无货物明细,请点击“添加行”' }}</div></td></tr></tbody></table></div>
</template>
</div>
<div v-if=" props . service === 'transfer' && form . relabel " class=" warehouse - card warehouse - form - section warehouse - transfer - relabel ">
<div class=" warehouse - section - heading warehouse - labeling - heading warehouse - step - heading "><div><h3><span class=" warehouse - step - number ">2</span>换标申请</h3></div></div>
<div class=" warehouse - form - grid warehouse - transfer - relabel - fields "><label><span><i>*</i>换标截止时间</span><el-date-picker v-model=" form . labelDeadline " type=" datetime " value-format=" YYYY - MM - DDTHH : mm : ss " placeholder=" 请选择截止时间 " /></label><label><span><i>*</i>换标联系人</span><el-input v-model=" form . relabelContact " placeholder=" 选择海外仓后自动带出 , 也可手动填写 " maxlength=" 128 " /></label><label><span><i>*</i>换标联系人电话</span><el-input v-model=" form . relabelPhone " placeholder=" 选择海外仓后自动带出 , 也可手动填写 " maxlength=" 64 " /></label><label v-if=" labelingMode !== 'sku' "><span><i>*</i>换标后派送方式</span><el-select v-model=" form . relabelDeliveryMethod " placeholder=" 请选择派送方式 "><el-option label=" 卡车派送 " value=" TRUCK " /><el-option label=" 快递派送 " value=" EXPRESS " /><el-option label=" 卡车 自提" value=" TRUCK _PICKUP " /><el-option label=" 快递 自提" value=" EXPRESS _PICKUP " /></el-select></label><label class=" warehouse - span - 2 "><span><i>*</i>整票换标货物目的 FBA 仓</span><el-select v-model=" transferRelabelDefaultDestination " :loading=" fbaWarehousesLoading " :disabled=" Boolean ( fbaWarehousesError ) " filterable placeholder=" 请选择整票货物的目的仓 "><el-option v-for=" warehouse in fbaWarehouses " :key=" warehouse . id " :label=" warehouseLabel ( warehouse ) " :value=" warehouse . warehouseCode " /></el-select><small class=" warehouse - hint ">本票所有换标货物统一前往该目的仓,暂不支持一票多个目的仓。</small></label><label class=" warehouse - span - 2 "><span>贴标要求</span><el-input v-model=" form . requirement " type=" textarea " :rows=" 2 " maxlength=" 1000 " show-word-limit placeholder=" 请输入贴标要求 ( 选填 ) " /></label></div>
<div class=" warehouse - form - grid warehouse - transfer - relabel - fields "><label><span><i>*</i>换标截止时间</span><el-date-picker v-model=" form . labelDeadline " type=" datetime " value-format=" YYYY - MM - DDTHH : mm : ss " placeholder=" 请选择截止时间 " /></label><label><span><i>*</i>换标联系人</span><el-input v-model=" form . relabelContact " placeholder=" 选择海外仓后自动带出 , 也可手动填写 " maxlength=" 128 " /></label><label><span><i>*</i>换标联系人电话</span><el-input v-model=" form . relabelPhone " placeholder=" 选择海外仓后自动带出 , 也可手动填写 " maxlength=" 64 " /></label><label v-if=" labelingMode !== 'sku' "><span><i>*</i>换标后派送方式</span><el-select v-model=" form . relabelDeliveryMethod " placeholder=" 请选择派送方式 "><el-option label=" 卡车派送 " value=" TRUCK " /><el-option label=" 快递派送 " value=" EXPRESS " /><el-option label=" 亚马逊 自提" value=" AMAZON _PICKUP " /><el-option label=" 客户 自提" value=" CUSTOMER _PICKUP " /></el-select></label><label class=" warehouse - span - 2 "><span><i>*</i>整票换标货物目的 FBA 仓</span><el-select v-model=" transferRelabelDefaultDestination " :loading=" fbaWarehousesLoading " :disabled=" Boolean ( fbaWarehousesError ) " filterable placeholder=" 请选择整票货物的目的仓 "><el-option v-for=" warehouse in fbaWarehouses " :key=" warehouse . id " :label=" warehouseLabel ( warehouse ) " :value=" warehouse . warehouseCode " /></el-select><small class=" warehouse - hint ">本票所有换标货物统一前往该目的仓,暂不支持一票多个目的仓。</small></label><label class=" warehouse - span - 2 "><span>贴标要求</span><el-input v-model=" form . requirement " type=" textarea " :rows=" 2 " maxlength=" 1000 " show-word-limit placeholder=" 请输入贴标要求 ( 选填 ) " /></label></div>
<div class=" warehouse - labeling - mode "><span>换标类型</span><el-radio-group v-model=" labelingMode " aria-label=" 换标类型 "><el-radio-button value=" carton ">只换箱唛</el-radio-button><el-radio-button value=" sku ">只换 SKU 标签</el-radio-button><el-radio-button value=" carton _and _sku ">SKU 和箱唛都换</el-radio-button></el-radio-group></div>
<div class=" warehouse - transfer - file - groups warehouse - file - groups -- compact ">
<section v-if=" canManageRelabelFiles " class=" warehouse - file - group warehouse - transfer - total - files ">
@@ -1682,6 +1714,7 @@ function addReturnBoxBinding(source: RelabelInventoryCandidate) {
<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 || '平台未填写驳回备注' " />
<div v-else class=" warehouse - card warehouse - form - section "><h3>订单概览</h3><div v-if=" props . service === 'labeling' " class=" warehouse - detail - grid "><div><span>操作仓</span><strong>{{ warehouseDisplay(selectedRelabelDetail?.application.overseasWarehouseId) }}</strong></div><div><span>客户参考号</span><strong>{{ selectedRelabelDetail?.application.customerOrderNo || '—' }}</strong></div><div><span>SKU 数(种)</span><strong>{{ selectedRelabelDetail ? relabelSkuCount(selectedRelabelDetail.items) : '—' }}</strong></div><div><span>总箱数</span><strong>{{ selectedRelabelDetail?.application.totalPackageCount ?? '—' }}</strong></div><div><span>贴标截止时间</span><strong>{{ formatDateTime(selectedRelabelDetail?.application.labelDeadline) }}</strong></div><div><span>仓库联系人</span><strong>{{ selectedRelabelDetail?.application.contactName || '—' }}</strong></div><div><span>联系电话</span><strong>{{ selectedRelabelDetail?.application.contactPhone || '—' }}</strong></div><div><span>创建时间</span><strong>{{ formatDateTime(selectedRelabelDetail?.application.createdAt) }}</strong></div></div><div v-else-if=" props . service === 'transfer' " class=" warehouse - detail - grid warehouse - transfer - overview - grid "><div><span>状态</span><el-tag :type=" transferStatusTagType ( selectedDetail ? . status || '' ) " effect=" light ">{{ statusLabel(selectedDetail?.status || '') }}</el-tag></div><div><span>目的去向</span><strong>{{ destinationTypeLabel(selectedDetail?.destinationType, selectedDetail?.fbaWarehouseCode) }}</strong></div><div v-if=" selectedDetail ? . destinationType !== 'PRIVATE' "><span>FBA 目的仓</span><strong>{{ selectedDetail?.destinationWarehouseName || warehouseDisplay(selectedDetail?.destinationWarehouseCode || selectedDetail?.fbaWarehouseCode) }}</strong></div><div v-if=" selectedDetail ? . destinationType !== 'PRIVATE' "><span>FBA 货件号</span><strong>{{ selectedDetail?.fbaShipmentNo || '—' }}</strong></div><div v-if=" selectedDetail ? . destinationType === 'PRIVATE' " class=" warehouse - overview - wide "><span>私人仓地址</span><strong>{{ selectedDetail?.privateDestinationAddress || '—' }}</strong></div><div v-if=" selectedDetail ? . destinationType === 'PRIVATE' "><span>邮编</span><strong>{{ selectedDetail?.privateDestinationPostalCode || '—' }}</strong></div><div v-if=" selectedDetail ? . destinationType === 'PRIVATE' "><span>私人仓联系人</span><strong>{{ selectedDetail?.privateDestinationContactName || '—' }}</strong></div><div v-if=" selectedDetail ? . destinationType === 'PRIVATE' "><span>联系电话</span><strong>{{ selectedDetail?.privateDestinationContactPhone || '—' }}</strong></div><div><span>海外仓</span><strong>{{ warehouseDisplay(selectedDetail?.operatingWarehouseId) }}</strong></div><div><span>箱数</span><strong>{{ selectedDetail?.packageCount ?? '—' }}</strong></div><div><span>入仓方式</span><strong>{{ deliveryMethodLabel(selectedDetail?.deliveryMethod || '') }}</strong></div><div><span>期望发货时间</span><strong>{{ formatDateTime(selectedDetail?.estimatedShipAt) }}</strong></div><div><span>实际发货时间</span><strong>{{ formatDateTime(selectedDetail?.actualShipAt) }}</strong></div><div><span>是否换标</span><strong>{{ selectedDetail?.needRelabel ? '是' : '否' }}</strong></div><div><span>换标状态</span><el-tag :type=" transferRelabelStatusTagType ( transferRelabelStatus ( selectedDetail || { } ) . code ) " effect=" light ">{{ transferRelabelStatus(selectedDetail || {}).label }}</el-tag></div><div><span>换标单号</span><strong>{{ selectedDetail?.relabelNo || '—' }}</strong></div><div><span>创建时间</span><strong>{{ formatDateTime(selectedDetail?.createdAt) }}</strong></div><div><span>转运单号</span><strong>{{ selectedDetail?.transferNo || '—' }}</strong></div></div><div v-else class=" warehouse - detail - grid "><div v-for=" ( column , index ) in config . columns . slice ( 1 ) " :key=" column "><span>{{ column }}</span><strong>{{ selectedDetail?.[String(index)] || '—' }}</strong></div></div></div>
<div v-if=" props . service === 'disposal' " class=" warehouse - card warehouse - form - section ">
<h3>本次废弃箱子</h3><div class=" warehouse - table - scroll "><table class=" warehouse - edit - table "><thead><tr><th>入仓码</th><th>SKU</th><th>品名</th><th>申请数量</th><th>所选箱唛</th></tr></thead><tbody><tr v-for=" ( item , index ) in selectedDetail ? . items || [ ] " :key=" index "><td>{{ item.inboundCode || '—' }}</td><td>{{ item.skuCode || '—' }}</td><td>{{ item.productName || '—' }}</td><td>{{ item.discardPackages ?? '—' }}</td><td>{{ item.cartonMarks?.join('、') || '全部库存(历史记录无箱唛快照)' }}</td></tr></tbody></table></div>
<h3>废弃现场照片</h3><div v-if=" selectedDetail ? . photos ? . length " class=" warehouse - photo - grid "><div v-for=" photo in selectedDetail . photos " :key=" photo . fileId " class=" warehouse - photo - item "><el-image v-if=" discardPhotoUrls [ photo . fileId ] " :src=" discardPhotoUrls [ photo . fileId ] " :preview-src-list=" discardPhotoPreviewUrls " :initial-index=" discardPhotoPreviewUrls . indexOf ( discardPhotoUrls [ photo . fileId ] ) " fit=" cover " preview-teleported /><div v-else-if=" discardPhotoLoading [ photo . fileId ] " class=" warehouse - photo - placeholder ">照片加载中…</div><button v-else class=" warehouse - photo - placeholder warehouse - photo - retry " type=" button " @click=" loadDiscardPhoto ( photo . fileId ) ">{{ discardPhotoErrors[photo.fileId] ? '加载失败,点击重试' : '加载照片' }}</button><small>{{ formatDateTime(photo.submittedAt) }}</small></div></div><div v-else class=" warehouse - empty ">暂无已提交的废弃现场照片</div>
</div>
<div v-if=" props . service === 'transfer' " class=" warehouse - card warehouse - form - section warehouse - transfer - measurement - card ">
@@ -1897,3 +1930,9 @@ function addReturnBoxBinding(source: RelabelInventoryCandidate) {
. warehouse - transfer - form . warehouse - choice - field -- relabel { grid - template - columns : 1 fr ; align - items : start ; }
}
< / style >
< style scoped >
. warehouse - discard - boxes { margin - bottom : 24 px ; }
. warehouse - discard - box - toolbar { display : flex ; align - items : center ; gap : 20 px ; margin - bottom : 16 px ; }
. warehouse - discard - box - toolbar . el - input { max - width : 340 px ; }
< / style >