@@ -2,16 +2,17 @@
import { computed , nextTick , onBeforeUnmount , onMounted , reactive , ref , watch } from 'vue'
import { computed , nextTick , onBeforeUnmount , onMounted , reactive , ref , watch } from 'vue'
import { useRoute , useRouter } from 'vue-router'
import { useRoute , useRouter } from 'vue-router'
import { ElMessage , ElMessageBox } from 'element-plus'
import { ElMessage , ElMessageBox } from 'element-plus'
import { ArrowLeft , Download , Plus , Printer , Trash2 , Upload , X } from '@lucide/vue'
import { ArrowLeft , ChevronDown , Download , Plus , Printer , Trash2 , Upload , X } from '@lucide/vue'
import QueryToolbar , { type QueryField , type QueryModel } from '@/components/common/QueryToolbar.vue'
import QueryToolbar , { type QueryField , type QueryModel } from '@/components/common/QueryToolbar.vue'
import DataTable , { type DataTableColumn } from '@/components/common/DataTable.vue'
import DataTable , { type DataTableColumn } from '@/components/common/DataTable.vue'
import type { TableAction } from '@/components/common/tableActions'
import type { TableAction } from '@/components/common/tableActions'
import { useAuthStore } from '@/modules/auth/public'
import { useAuthStore } from '@/modules/auth/public'
import CompletionConfirmationDialog from '../components/CompletionConfirmationDialog.vue'
import { returnRelabelCandidate } from '../utils/returnRelabelCandidates'
import { returnRelabelCandidate } from '../utils/returnRelabelCandidates'
import {
import {
createDiscard , createRelabel , createTransfer , getDiscard , getRelabel , getTransfer , resubmitRelabel ,
createDiscard , createRelabel , createTransfer , getDiscard , getRelabel , getTransfer , resubmitRelabel ,
getTransferCartonLabel , submitTransfer , updateTransfer ,
getTransferCartonLabel , submitTransfer , updateTransfer ,
confirmRelabelCompletion , confirmTransferMeasurement , downloadRelabelCompletionPhoto , downloadTransferMeasurementPhoto , downloadDiscardPhoto , type DiscardPhoto , uploadRelabelShippingLabel ,
confirmTransferMeasurement , downloadRelabelCompletionPhoto , downloadTransferMeasurementPhoto , downloadDiscardPhoto , type DiscardPhoto , uploadRelabelShippingLabel ,
uploadRelabelFile ,
uploadRelabelFile ,
listDiscards , listDiscardCandidates , listRelabelInventoryCandidates , listRelabelHistoricalOrders , listRelabels , listTransfers , cancelDiscard ,
listDiscards , listDiscardCandidates , listRelabelInventoryCandidates , listRelabelHistoricalOrders , listRelabels , listTransfers , cancelDiscard ,
type RelabelInventoryCandidate ,
type RelabelInventoryCandidate ,
@@ -33,11 +34,11 @@ type ServiceType = 'labeling' | 'transfer' | 'disposal' | 'relabel'
type PageMode = 'list' | 'create' | 'edit' | 'detail'
type PageMode = 'list' | 'create' | 'edit' | 'detail'
type LabelingMode = 'carton' | 'sku' | 'carton_and_sku'
type LabelingMode = 'carton' | 'sku' | 'carton_and_sku'
type RelabelTemplateType = 'carton' | 'sku' | 'mixed'
type RelabelTemplateType = 'carton' | 'sku' | 'mixed'
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 CargoRow { discardCartonIds ? : string [ ] ; id : number ; included ? : boolean ; sku : string ; name : string ; quantity : number ; expectedPackageCount ? : 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 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 ImportedRelabelBinding extends SkuRelabelRow { templateType : RelabelTemplateType ; selected : boolean ; shipmentNo : string }
interface ServiceConfig { title : string ; createTitle : string ; numberLabel : string ; emptyText : string ; columns : string [ ] }
interface ServiceConfig { title : string ; createTitle : string ; numberLabel : string ; emptyText : string ; columns : string [ ] }
interface TransferCarton { cartonMark : string ; customerCartonMark ? : string ; cartonIndex : number ; totalCartons : number ; status : string }
interface TransferCarton { cartonMark : string ; customerCartonMark ? : string ; cartonIndex : number ; totalCartons : number ; status : string ; transferItemId ? : string | number ; transferPalletId ? : string | number ; length ? : number ; width ? : number ; height ? : number ; weight ? : number ; quantity ? : number ; discrepancyReason ? : string ; countedAt ? : string }
const props = withDefaults ( defineProps < { service : ServiceType ; createPage ? : boolean ; detailPage ? : boolean ; detailNo ? : string } > ( ) , { createPage : false , detailPage : false , detailNo : '' } )
const props = withDefaults ( defineProps < { service : ServiceType ; createPage ? : boolean ; detailPage ? : boolean ; detailNo ? : string } > ( ) , { createPage : false , detailPage : false , detailNo : '' } )
const auth = useAuthStore ( )
const auth = useAuthStore ( )
@@ -53,10 +54,22 @@ const canCreate = computed(() => auth.hasPermission(({
const canManageRelabelFiles = computed ( ( ) => auth . hasPermission ( 'merchant:overseas-warehouse:relabel:files' ) )
const canManageRelabelFiles = computed ( ( ) => auth . hasPermission ( 'merchant:overseas-warehouse:relabel:files' ) )
const canConfirmRelabel = computed ( ( ) => auth . hasPermission ( 'merchant:overseas-warehouse:relabel:confirm' ) )
const canConfirmRelabel = computed ( ( ) => auth . hasPermission ( 'merchant:overseas-warehouse:relabel:confirm' ) )
const canConfirmTransfer = computed ( ( ) => auth . hasPermission ( 'merchant:overseas-warehouse:transfer:confirm' ) )
const canConfirmTransfer = computed ( ( ) => auth . hasPermission ( 'merchant:overseas-warehouse:transfer:confirm' ) )
const canConfirmDiscard = computed ( ( ) => auth . hasPermission ( 'merchant:overseas-warehouse:discard:confirm' ) )
const completionConfirmOpen = ref ( false )
const completionConfirmNo = ref ( '' )
const completionConfirmKind = ref < 'disposal' | 'labeling' > ( 'disposal' )
function openCompletionConfirmation ( no : string , kind : 'disposal' | 'labeling' ) {
if ( ! no ) return
completionConfirmNo . value = no ; completionConfirmKind . value = kind ; completionConfirmOpen . value = true
}
async function refreshAfterCompletion ( no : string ) {
if ( mode . value === 'detail' ) await openDetail ( { '0' : no } )
else await load ( )
}
const canCancelDiscard = computed ( ( ) => auth . hasPermission ( 'merchant:overseas-warehouse:discard:cancel' ) )
const canCancelDiscard = computed ( ( ) => auth . hasPermission ( 'merchant:overseas-warehouse:discard:cancel' ) )
const configs : Record < ServiceType , ServiceConfig > = {
const configs : Record < ServiceType , ServiceConfig > = {
labeling : { title : '贴标申请' , createTitle : '新增贴标申请' , numberLabel : '贴标申请单号' , emptyText : '暂无贴标申请' , columns : [ '操作' , '贴标申请单号' , '关联业务单号' , '海外操作仓' , 'SKU 数(种)' , '总箱数/包裹' , '换标类型' , '状态' , '审核时间' , '贴标完成时间' , '贴标出库时间' , '创建时间' ] } ,
labeling : { title : '贴标申请' , createTitle : '新增贴标申请' , numberLabel : '贴标申请单号' , emptyText : '暂无贴标申请' , columns : [ '操作' , '贴标申请单号' , '关联业务单号' , '海外操作仓' , 'SKU 数(种)' , '总箱数/包裹' , '换标类型' , '状态' , '审核时间' , '贴标完成时间' , '贴标出库时间' , '创建时间' ] } ,
transfer : { title : '仓储转运单' , createTitle : '新增仓储转运单' , numberLabel : '转运单号' , emptyText : '暂无仓储转运订单' , columns : [ '操作' , '转运单号' , '箱数' , '海外仓' , 'FBA 目的仓' , '入仓方式' , '是否换标' , '换标单号' , '换标状态' , '状态' , '审核时间' , '测量时间' , '出库时间' , '创建时间' ] } ,
transfer : { title : '仓储转运单' , createTitle : '新增仓储转运单' , numberLabel : '转运单号' , emptyText : '暂无仓储转运订单' , columns : [ '操作' , '转运单号' , '预计 箱数' , '海外仓' , 'FBA 目的仓' , '入仓方式' , '是否换标' , '换标单号' , '换标状态' , '状态' , '审核时间' , '测量时间' , '出库时间' , '创建时间' ] } ,
disposal : { title : '货物废弃' , createTitle : '新增货物废弃申请' , numberLabel : '废弃申请单号' , emptyText : '暂无货物废弃申请' , columns : [ '操作' , '废弃申请单号' , '废弃出库单号' , '进仓编号' , '操作仓' , 'SKU 汇总' , '废弃数量' , '预估费用' , '状态' , '废弃完成时间' , '创建时间' ] } ,
disposal : { title : '货物废弃' , createTitle : '新增货物废弃申请' , numberLabel : '废弃申请单号' , emptyText : '暂无货物废弃申请' , columns : [ '操作' , '废弃申请单号' , '废弃出库单号' , '进仓编号' , '操作仓' , 'SKU 汇总' , '废弃数量' , '预估费用' , '状态' , '废弃完成时间' , '创建时间' ] } ,
relabel : { title : '退货换标' , createTitle : '新建退货换标单' , numberLabel : '退货换标单号' , emptyText : '暂无退货换标订单' , columns : [ '操作' , '退货换标单号' , '来源 FBA 仓' , '目的 FBA 仓' , 'SKU 汇总' , '退件总数' , '是否换标' , '换标状态' , '尾程状态' , '审核时间' , '测量时间' , '出库时间' , '创建时间' ] } ,
relabel : { title : '退货换标' , createTitle : '新建退货换标单' , numberLabel : '退货换标单号' , emptyText : '暂无退货换标订单' , columns : [ '操作' , '退货换标单号' , '来源 FBA 仓' , '目的 FBA 仓' , 'SKU 汇总' , '退件总数' , '是否换标' , '换标状态' , '尾程状态' , '审核时间' , '测量时间' , '出库时间' , '创建时间' ] } ,
}
}
@@ -81,7 +94,7 @@ const listQueryModel = computed<QueryModel>({
const statusOptions = computed ( ( ) => props . service === 'labeling'
const statusOptions = computed ( ( ) => props . service === 'labeling'
? [ { label : '待审核' , value : 'PENDING_REVIEW' } , { label : '待处理' , value : 'PENDING_PROCESSING' } , { label : '处理中' , value : 'PROCESSING' } , { label : '换标完成' , value : 'COMPLETED' } , { label : '已驳回' , value : 'REJECTED' } ]
? [ { label : '待审核' , value : 'PENDING_REVIEW' } , { label : '待处理' , value : 'PENDING_PROCESSING' } , { label : '处理中' , value : 'PROCESSING' } , { label : '换标完成' , value : 'COMPLETED' } , { label : '已驳回' , value : 'REJECTED' } ]
: props . service === 'disposal'
: props . service === 'disposal'
? [ { label : '待审核' , value : 'PENDING_REVIEW' } , { label : '待 PDA 废弃' , value : 'APPROVED' } , { label : '待仓库确认完成 ' , value : 'PDA_COMPLETED' } , { label : '已完成' , value : 'COMPLETED' } , { label : '已拒绝' , value : 'REJECTED' } , { label : '已取消' , value : 'CANCELLED' } ]
? [ { label : '待审核' , value : 'PENDING_REVIEW' } , { label : '待 PDA 废弃' , value : 'APPROVED' } , { label : '待客户确认 ' , value : 'PDA_COMPLETED' } , { label : '已完成' , value : 'COMPLETED' } , { label : '已拒绝' , value : 'REJECTED' } , { label : '已取消' , value : 'CANCELLED' } ]
: props . service === 'transfer'
: props . service === 'transfer'
? [ { label : '草稿' , value : 'DRAFT' } , { label : '待审核' , value : 'PENDING_REVIEW' } ,
? [ { label : '草稿' , value : 'DRAFT' } , { label : '待审核' , value : 'PENDING_REVIEW' } ,
{ label : '待PDA收货' , value : 'WAIT_PDA_RECEIVING' } , { label : '入库测量中' , value : 'INBOUND_MEASURING' } ,
{ label : '待PDA收货' , value : 'WAIT_PDA_RECEIVING' } , { label : '入库测量中' , value : 'INBOUND_MEASURING' } ,
@@ -130,11 +143,35 @@ interface ServiceDetail extends Record<string, unknown> {
relabelContactPhone ? : string ; relabelDeliveryMethod ? : string ; relabelNo ? : string
relabelContactPhone ? : string ; relabelDeliveryMethod ? : string ; relabelNo ? : string
relabelStatus ? : string ; relabelFulfillmentStatus ? : string ; createdAt ? : string
relabelStatus ? : string ; relabelFulfillmentStatus ? : string ; createdAt ? : string
inboundCode ? : string ; sourceOrders ? : Array < { lastlegOrderId ? : string | number } >
inboundCode ? : string ; sourceOrders ? : Array < { lastlegOrderId ? : string | number } >
items ? : Array < { cartonMarks ? : string [ ] ; inboundCode ? : string ; productName ? : string ; discardPackages ? : number ; skuCode ? : string ; productNameZh ? : string ; packageCount ? : number ; shippingMark ? : string } >
items ? : Array < { id ? : string | number ; cartonMarks ? : string [ ] ; inboundCode ? : string ; productName ? : string ; discardPackages ? : number ; skuCode ? : string ; productNameZh ? : string ; quantity ? : number | null ; packageCount ? : number ; shippingMark ? : string } >
relabelBindings ? : Array < { operationType ? : string ; customerCartonMark ? : string ; originalSku ? : string ; newSku ? : string ; quantity ? : number ; fbaCartonMark ? : string ; destinationWarehouseCode ? : string ; remark ? : 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
cartons ? : TransferCarton [ ] ; pallets ? : TransferPallet [ ] ; measurementPhotos ? : TransferMeasurementPhoto [ ] ; customerConfirmedAt ? : string
}
}
const selectedDetail = ref < ServiceDetail | null > ( null )
const selectedDetail = ref < ServiceDetail | null > ( null )
const showTransferCartons = ref ( false )
function transferCartonItem ( carton : TransferCarton ) {
return selectedDetail . value ? . items ? . find ( ( item ) => carton . transferItemId != null && String ( item . id ) === String ( carton . transferItemId ) )
}
function palletCartons ( pallet : TransferPallet ) {
return ( selectedDetail . value ? . cartons || [ ] ) . filter ( ( carton ) => carton . status === 'COUNTED' && pallet . id != null && carton . transferPalletId != null && String ( carton . transferPalletId ) === String ( pallet . id ) )
}
function palletSkuRows ( pallet : TransferPallet ) {
const summary = new Map < string , number > ( )
for ( const carton of palletCartons ( pallet ) ) {
const item = transferCartonItem ( carton ) ; const sku = item ? . skuCode || '未标记 SKU'
summary . set ( sku , ( summary . get ( sku ) || 0 ) + Number ( carton . quantity || 0 ) )
}
return [ ... summary . entries ( ) ] . map ( ( [ sku , quantity ] ) => ( { sku , quantity } ) )
}
function palletQuantity ( pallet : TransferPallet ) { return palletCartons ( pallet ) . reduce ( ( sum , carton ) => sum + Number ( carton . quantity || 0 ) , 0 ) }
function countedTransferCartons ( ) { return ( selectedDetail . value ? . cartons || [ ] ) . filter ( ( carton ) => carton . status === 'COUNTED' ) }
function receivedItemCartons ( itemId : string | number | undefined ) {
return countedTransferCartons ( ) . filter ( carton => itemId != null && carton . transferItemId != null && String ( carton . transferItemId ) === String ( itemId ) )
}
function receivedItemQuantity ( itemId : string | number | undefined ) {
return receivedItemCartons ( itemId ) . reduce ( ( sum , carton ) => sum + Number ( carton . quantity || 0 ) , 0 )
}
const detailError = ref ( '' )
const detailError = ref ( '' )
const selectedRelabelDetail = ref < RelabelDetail | null > ( null )
const selectedRelabelDetail = ref < RelabelDetail | null > ( null )
const completionPhotoUrls = reactive < Record < string , string > > ( { } )
const completionPhotoUrls = reactive < Record < string , string > > ( { } )
@@ -463,7 +500,7 @@ async function openEdit(transferNo: string) {
transferDestinationType . value = detail . destinationType || ( detail . fbaWarehouseCode ? 'FBA' : 'NONE' )
transferDestinationType . value = detail . destinationType || ( detail . fbaWarehouseCode ? 'FBA' : 'NONE' )
cargoRows . value = ( detail . items || [ ] ) . map ( ( item ) => ( {
cargoRows . value = ( detail . items || [ ] ) . map ( ( item ) => ( {
... createCargoRow ( ) , sku : item . skuCode || '' , name : item . productNameZh || '' ,
... createCargoRow ( ) , sku : item . skuCode || '' , name : item . productNameZh || '' ,
quantity : Number ( item . packageCount || 0 ) , shippingMark : item . shippingMark || '' ,
quantity : Number ( item . quantity || 0 ) , expectedPackageCount : Number ( item . packageCount || 0 ) , shippingMark : item . shippingMark || '' ,
customerCartonMarks : item . shippingMark || '' ,
customerCartonMarks : item . shippingMark || '' ,
} ) )
} ) )
const bindings = detail . relabelBindings || [ ]
const bindings = detail . relabelBindings || [ ]
@@ -520,26 +557,26 @@ async function selectDiscardWarehouse() {
} catch ( error ) { ElMessage . error ( error instanceof Error ? error . message : '可废弃入库单加载失败' ) }
} catch ( error ) { ElMessage . error ( error instanceof Error ? error . message : '可废弃入库单加载失败' ) }
finally { discardCandidatesLoading . value = false }
finally { discardCandidatesLoading . value = false }
}
}
function createCargoRow ( ) : CargoRow { return { id : ++ cargoSequence , sku : '' , name : '' , quantity : 1 , fnsku : '' , shipmentNo : '' , newSkuCode : '' , shippingMark : '' , customerCartonMarks : '' , cartonMark : '' , fbaCartonMark : '' , destinationWarehouseId : '' , remark : '' } }
function createCargoRow ( ) : CargoRow { return { id : ++ cargoSequence , sku : '' , name : '' , quantity : 1 , expectedPackageCount : 1 , fnsku : '' , shipmentNo : '' , newSkuCode : '' , shippingMark : '' , customerCartonMarks : '' , cartonMark : '' , fbaCartonMark : '' , destinationWarehouseId : '' , remark : '' } }
function addCargoRow ( ) { cargoRows . value . push ( createCargoRow ( ) ) }
function addCargoRow ( ) { cargoRows . value . push ( createCargoRow ( ) ) }
function escapePrintHtml ( value : string ) {
function escapePrintHtml ( value : string ) {
return value . replaceAll ( '&' , '&' ) . replaceAll ( '<' , '<' ) . replaceAll ( '>' , '>' ) . replaceAll ( '"' , '"' ) . replaceAll ( "'" , ''' )
return value . replaceAll ( '&' , '&' ) . replaceAll ( '<' , '<' ) . replaceAll ( '>' , '>' ) . replaceAll ( '"' , '"' ) . replaceAll ( "'" , ''' )
}
}
function printTransferDraftCartonMarks ( ) {
function printTransferDraftCartonMarks ( ) {
if ( props . service !== 'transfer' ) return
if ( props . service !== 'transfer' ) return
const validRows = cargoRows . value . filter ( ( row ) => row . sku . trim ( ) && Number ( row . quantity ) > 0 )
const validRows = cargoRows . value . filter ( ( row ) => row . sku . trim ( ) && Number ( row . expectedPackageCount ) > 0 )
if ( ! validRows . length || validRows . some ( ( row ) => ! Number . isInteger ( Number ( row . quantity ) ) ) ) {
if ( ! validRows . length || validRows . some ( ( row ) => ! Number . isInteger ( Number ( row . expectedPackageCount ) ) ) ) {
ElMessage . warning ( '请先填写商品 SKU 和对应的整数箱数' ) ; return
ElMessage . warning ( '请先填写商品 SKU 和对应的整数箱数' ) ; return
}
}
const totalCartons = validRows . reduce ( ( sum , row ) => sum + Number ( row . quantity ) , 0 )
const totalCartons = validRows . reduce ( ( sum , row ) => sum + Number ( row . expectedPackageCount ) , 0 )
if ( totalCartons > 500 ) { ElMessage . warning ( '单次最多打印 500 张箱唛' ) ; return }
if ( totalCartons > 500 ) { ElMessage . warning ( '单次最多打印 500 张箱唛' ) ; return }
const batch = new Date ( ) . toISOString ( ) . replace ( /\D/g , '' ) . slice ( 2 , 14 )
const batch = new Date ( ) . toISOString ( ) . replace ( /\D/g , '' ) . slice ( 2 , 14 )
const labels : Array < { mark : string ; sku : string ; index : number } > = [ ]
const labels : Array < { mark : string ; sku : string ; index : number } > = [ ]
let cartonIndex = 0
let cartonIndex = 0
for ( const [ rowIndex , row ] of validRows . entries ( ) ) {
for ( const [ rowIndex , row ] of validRows . entries ( ) ) {
let marks = transferCustomerMarks ( row )
let marks = transferCustomerMarks ( row )
if ( marks . length !== Number ( row . quantity ) ) {
if ( marks . length !== Number ( row . expectedPackageCount ) ) {
marks = Array . from ( { length : Number ( row . quantity ) } , ( _ , index ) => ` CT- ${ batch } - ${ String ( rowIndex + 1 ) . padStart ( 2 , '0' ) } - ${ String ( index + 1 ) . padStart ( 3 , '0' ) } ` )
marks = Array . from ( { length : Number ( row . expectedPackageCount ) } , ( _ , index ) => ` CT- ${ batch } - ${ String ( rowIndex + 1 ) . padStart ( 2 , '0' ) } - ${ String ( index + 1 ) . padStart ( 3 , '0' ) } ` )
row . customerCartonMarks = marks . join ( '\n' )
row . customerCartonMarks = marks . join ( '\n' )
}
}
for ( const mark of marks ) labels . push ( { mark , sku : row . sku . trim ( ) , index : ++ cartonIndex } )
for ( const mark of marks ) labels . push ( { mark , sku : row . sku . trim ( ) , index : ++ cartonIndex } )
@@ -696,7 +733,7 @@ function downloadTransferCartonMarkTemplate() {
ElMessage . warning ( '请先填写完整的 SKU 明细' ) ; return
ElMessage . warning ( '请先填写完整的 SKU 明细' ) ; return
}
}
const header = [ 'SKU' , '客户箱唛号' ]
const header = [ 'SKU' , '客户箱唛号' ]
const rows = cargoRows . value . flatMap ( ( row ) => Array . from ( { length : row . quantity } , ( ) => [ row . sku , '' ] ) )
const rows = cargoRows . value . flatMap ( ( row ) => Array . from ( { length : Number ( row . expectedPackageCount || 0 ) } , ( ) => [ row . sku , '' ] ) )
const blob = new Blob ( [ ` \ uFEFF ${ [ header , ... rows ] . map ( ( row ) => row . map ( csvCell ) . join ( ',' ) ) . join ( '\r\n' ) } \ r \ n ` ] , { type : 'text/csv;charset=utf-8' } )
const blob = new Blob ( [ ` \ uFEFF ${ [ header , ... rows ] . map ( ( row ) => row . map ( csvCell ) . join ( ',' ) ) . join ( '\r\n' ) } \ r \ n ` ] , { type : 'text/csv;charset=utf-8' } )
const url = URL . createObjectURL ( blob ) ; const anchor = document . createElement ( 'a' )
const url = URL . createObjectURL ( blob ) ; const anchor = document . createElement ( 'a' )
anchor . href = url ; anchor . download = '仓储转运-客户箱唛模板.csv' ; anchor . click ( ) ; URL . revokeObjectURL ( url )
anchor . href = url ; anchor . download = '仓储转运-客户箱唛模板.csv' ; anchor . click ( ) ; URL . revokeObjectURL ( url )
@@ -932,7 +969,7 @@ function statusLabel(value: string) {
PENDING _PROCESSING : '待处理' , PROCESSING : '处理中' , CUSTOMER _CONFIRM _PENDING : '待客户确认' ,
PENDING _PROCESSING : '待处理' , PROCESSING : '处理中' , CUSTOMER _CONFIRM _PENDING : '待客户确认' ,
READY _FOR _OUTBOUND : '换标完成' , COMPLETED : '已完成' , CANCELLED : '已取消' ,
READY _FOR _OUTBOUND : '换标完成' , COMPLETED : '已完成' , CANCELLED : '已取消' ,
DRAFT : '草稿' , PENDING _REVIEW : '待审核' , APPROVED : '待 PDA 废弃' , PDA _COMPLETED : '待仓库确认完成 ' , REJECTED : '已拒绝' , PENDING _ASSIGNMENT : '待分配' ,
DRAFT : '草稿' , PENDING _REVIEW : '待审核' , APPROVED : '待 PDA 废弃' , PDA _COMPLETED : '待客户确认 ' , REJECTED : '已拒绝' , PENDING _ASSIGNMENT : '待分配' ,
WAIT _PDA _RECEIVING : '待PDA收货' , WAIT _CUSTOMER _CONFIRMATION : '待客户确认实测' , WAIT _FBA _DESTINATION : '待确认目的地' , WAIT _RELABEL : '待换标' , READY _FOR _DISPATCH : '待排车' , WAIT _OUTBOUND : '待出库' ,
WAIT _PDA _RECEIVING : '待PDA收货' , WAIT _CUSTOMER _CONFIRMATION : '待客户确认实测' , WAIT _FBA _DESTINATION : '待确认目的地' , WAIT _RELABEL : '待换标' , READY _FOR _DISPATCH : '待排车' , WAIT _OUTBOUND : '待出库' ,
ALLOCATED : '已分配' , OUTBOUNDED : '已出库' , PARTIALLY _RECEIVED : '部分收货' , RECEIVED : '已收货' , INBOUND _COMPLETED : '入库完成' , PENDING _DELIVERY : '待派送' , DELIVERING : '派送中' , DELIVERED : '派送完成' ,
ALLOCATED : '已分配' , OUTBOUNDED : '已出库' , PARTIALLY _RECEIVED : '部分收货' , RECEIVED : '已收货' , INBOUND _COMPLETED : '入库完成' , PENDING _DELIVERY : '待派送' , DELIVERING : '派送中' , DELIVERED : '派送完成' ,
@@ -1031,7 +1068,7 @@ async function load() {
} else if ( props . service === 'disposal' ) {
} else if ( props . service === 'disposal' ) {
const result = await listDiscards ( { page : page . value , size : pageSize . value , discardNo : filters . number || undefined , inboundCode : filters . inboundNo || undefined , overseasWarehouseId : filters . warehouse || undefined , status : filters . status || undefined , createdFrom : dateTime ( filters . start ) , createdTo : dateTime ( filters . end , true ) , completedFrom : dateTime ( filters . completedStart ) , completedTo : dateTime ( filters . completedEnd , true ) } )
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
total . value = Number . isFinite ( Number ( result . total ) ) ? Number ( result . total ) : result . records . length
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 ) ] ) )
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 . customerConfirmedAt ) , formatDateTime ( item . createdAt ) ] ) , _ _status : item . status } ) )
} else {
} 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 ) , 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 ) } )
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
total . value = Number . isFinite ( Number ( result . total ) ) ? Number ( result . total ) : result . records . length
@@ -1054,6 +1091,7 @@ function listRowActions(candidate: object): TableAction[] {
if ( canCreate . value && props . service === 'transfer' && [ 'DRAFT' , 'REJECTED' ] . includes ( item . _ _status ) ) 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 ( 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' ] ) } )
if ( canConfirmRelabel . value && props . service === 'labeling' && item . _ _status === 'CUSTOMER_CONFIRM_PENDING' && ! item . _ _confirmedAt ) actions . push ( { key : 'confirm-relabel' , label : '确认换标' , run : ( ) => confirmRelabelFromList ( item [ '0' ] ) } )
if ( canConfirmDiscard . value && props . service === 'disposal' && item . _ _status === 'PDA_COMPLETED' ) actions . push ( { key : 'confirm-discard' , label : '废弃确认' , run : ( ) => openCompletionConfirmation ( item [ '0' ] , 'disposal' ) } )
return actions
return actions
}
}
watch ( [ page , pageSize ] , ( [ nextPage , nextSize ] , [ previousPage , previousSize ] ) => {
watch ( [ page , pageSize ] , ( [ nextPage , nextSize ] , [ previousPage , previousSize ] ) => {
@@ -1069,7 +1107,7 @@ async function submit(action: 'draft' | 'submit' = 'submit') {
: ( props . service === 'labeling' || props . service === 'relabel' ) && ( relabelRows . length === 0 || ( props . service === 'labeling' && labelingMode . value !== 'carton'
: ( props . service === 'labeling' || props . service === 'relabel' ) && ( relabelRows . length === 0 || ( props . service === 'labeling' && labelingMode . value !== 'carton'
? skuRelabelRows . value . some ( ( item ) => ! item . cartonMark || ! item . originalSku || ! item . newSku || item . quantity < 1
? skuRelabelRows . value . some ( ( item ) => ! item . cartonMark || ! item . originalSku || ! item . newSku || item . quantity < 1
|| ( labelingMode . value === 'carton_and_sku' && ( ! item . fbaCartonMark || ! item . shipmentNo . trim ( ) ) ) )
|| ( labelingMode . value === 'carton_and_sku' && ( ! item . fbaCartonMark || ! item . shipmentNo . trim ( ) ) ) )
: cargoRows . value . some ( ( item ) => ( props . service !== 'labeling' && ! item . sku ) || ! item . name
: ( props . service === 'labeling' ? cartonDetailRows . value : cargoRows . value ) . some ( ( item ) => ( props . service !== 'labeling' && ! item . sku ) || ! item . name
|| ( props . service !== 'labeling' && item . quantity < 1 ) ) ) )
|| ( props . service !== 'labeling' && item . quantity < 1 ) ) ) )
if ( ( props . service === 'relabel' && ! form . inboundNo ) || ! primaryWarehouse || invalidCargo ) {
if ( ( props . service === 'relabel' && ! form . inboundNo ) || ! primaryWarehouse || invalidCargo ) {
ElMessage . warning ( '请填写必填信息和完整的货物明细' ) ; return
ElMessage . warning ( '请填写必填信息和完整的货物明细' ) ; return
@@ -1105,10 +1143,14 @@ async function submit(action: 'draft' | 'submit' = 'submit') {
if ( props . service === 'relabel' && ( ! form . destination || ! form . address || ! form . deliveryMethod || ! form . estimatedArrivalAt || ! form . fbaShipmentNo || form . volumeCbm <= 0 ) ) {
if ( props . service === 'relabel' && ( ! form . destination || ! form . address || ! form . deliveryMethod || ! form . estimatedArrivalAt || ! form . fbaShipmentNo || form . volumeCbm <= 0 ) ) {
ElMessage . warning ( '请填写完整的派送信息、体积、预计到达时间和 FBA 货件号' ) ; return
ElMessage . warning ( '请填写完整的派送信息、体积、预计到达时间和 FBA 货件号' ) ; return
}
}
if ( props . service === 'transfer' && ( ! form . warehouse || ! form . deliveryMethod || ! form . address || ! form . estimatedArrivalAt || form . volumeCbm < 0 || ( form . deliveryMethod === 'WAREHOUSE_PICKUP' && ( ! form . pickupContact || ! form . pickupPhone ) ) ) ) {
if ( props . service === 'transfer' && ( ! form . warehouse || ! form . deliveryMethod || ! form . address || ! form . estimatedArrivalAt || ( form . deliveryMethod === 'WAREHOUSE_PICKUP' && ( ! form . pickupContact || ! form . pickupPhone ) ) ) ) {
const missing = [ ! form . warehouse && '海外仓' , ! form . deliveryMethod && '送货方式' , ! form . address && '地址' , form . deliveryMethod === 'WAREHOUSE_PICKUP' && ! form . pickupContact && '提货联系人' , form . deliveryMethod === 'WAREHOUSE_PICKUP' && ! form . pickupPhone && '联系人电话' , ! form . estimatedArrivalAt && '预计入仓时间' , form . volumeCbm < 0 && '预估总体积' ] . filter ( Boolean ) . join ( '、' )
const missing = [ ! form . warehouse && '海外仓' , ! form . deliveryMethod && '送货方式' , ! form . address && '地址' , form . deliveryMethod === 'WAREHOUSE_PICKUP' && ! form . pickupContact && '提货联系人' , form . deliveryMethod === 'WAREHOUSE_PICKUP' && ! form . pickupPhone && '联系人电话' , ! form . estimatedArrivalAt && '预计入仓时间' ] . filter ( Boolean ) . join ( '、' )
ElMessage . warning ( ` 请填写: ${ missing || '必填信息' } ` ) ; return
ElMessage . warning ( ` 请填写: ${ missing || '必填信息' } ` ) ; return
}
}
if ( props . service === 'transfer' && ( ! cargoRows . value . length || cargoRows . value . some ( ( row ) =>
! Number . isSafeInteger ( row . quantity ) || row . quantity < 1 || ! Number . isSafeInteger ( row . expectedPackageCount ) || Number ( row . expectedPackageCount ) < 1 ) ) ) {
ElMessage . warning ( '请填写每个 SKU 的数量(件)和预计箱数,均须为正整数' ) ; return
}
if ( props . service === 'transfer' && transferDestinationType . value === 'FBA' && ! form . destination ) {
if ( props . service === 'transfer' && transferDestinationType . value === 'FBA' && ! form . destination ) {
ElMessage . warning ( '请选择 FBA 目的仓' ) ; return
ElMessage . warning ( '请选择 FBA 目的仓' ) ; return
}
}
@@ -1169,12 +1211,12 @@ async function submit(action: 'draft' | 'submit' = 'submit') {
} )
} )
await createDiscard ( { items , idempotencyKey , remark : [ form . reason , form . remark ] . filter ( Boolean ) . join ( '; ' ) || undefined } )
await createDiscard ( { items , idempotencyKey , remark : [ form . reason , form . remark ] . filter ( Boolean ) . join ( '; ' ) || undefined } )
} else {
} else {
const packageCount = cargoRows . value . reduce ( ( sum , item ) => sum + item . quantity , 0 )
const packageCount = cargoRows . value . reduce ( ( sum , item ) => sum + Number ( item . expectedPackageCount || 0 ) , 0 )
const customerCartonMarks = ( form . relabel ? transferRelabelRows . value : cargoRows . value ) . flatMap ( transferCustomerMarks )
const customerCartonMarks = ( form . relabel ? transferRelabelRows . value : cargoRows . value ) . flatMap ( transferCustomerMarks )
const transferRemark = [ form . remark , form . relabel && form . requirement ? ` 换标要求: ${ form . requirement } ` : '' ] . filter ( Boolean ) . join ( '; ' )
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 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 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' | '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 }
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 : 0 , 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 , quantity : item . quantity , packageCount : Number ( item . expectedPackageCount ) , 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 ) {
if ( props . service === 'transfer' && selectedTransferInventory . value ) {
; ( payload as TransferCreate ) . inboundCode = selectedTransferInventory . value
; ( payload as TransferCreate ) . inboundCode = selectedTransferInventory . value
; ( payload as TransferCreate ) . sourceLastlegOrderIds = selectedTransferSourceOrderIds . value
; ( payload as TransferCreate ) . sourceLastlegOrderIds = selectedTransferSourceOrderIds . value
@@ -1223,6 +1265,7 @@ async function openDetail(value: Record<string, string>) {
try {
try {
const detail = props . service === 'labeling' ? await getRelabel ( value [ '0' ] ) : props . service === 'disposal' ? await getDiscard ( value [ '0' ] ) : await getTransfer ( value [ '0' ] )
const detail = props . service === 'labeling' ? await getRelabel ( value [ '0' ] ) : props . service === 'disposal' ? await getDiscard ( value [ '0' ] ) : await getTransfer ( value [ '0' ] )
const discardSummary = props . service === 'disposal' ? ( detail as { summary ? : ServiceDetail } ) . summary : undefined
const discardSummary = props . service === 'disposal' ? ( detail as { summary ? : ServiceDetail } ) . summary : undefined
showTransferCartons . value = false
selectedDetail . value = { ... value , ... ( detail as ServiceDetail ) , ... ( discardSummary || { } ) }
selectedDetail . value = { ... value , ... ( detail as ServiceDetail ) , ... ( discardSummary || { } ) }
selectedRelabelDetail . value = props . service === 'labeling' ? detail as RelabelDetail : null
selectedRelabelDetail . value = props . service === 'labeling' ? detail as RelabelDetail : null
mode . value = 'detail'
mode . value = 'detail'
@@ -1307,12 +1350,7 @@ async function loadMeasurementPhotos() {
await Promise . all ( ( selectedDetail . value ? . measurementPhotos || [ ] ) . map ( ( photo ) => loadMeasurementPhoto ( photo . fileId ) ) )
await Promise . all ( ( selectedDetail . value ? . measurementPhotos || [ ] ) . map ( ( photo ) => loadMeasurementPhoto ( photo . fileId ) ) )
}
}
async function confirmRelabelFromList ( relabelNo : string ) {
async function confirmRelabelFromList ( relabelNo : string ) {
try {
openCompletionConfirmation ( relabelNo , 'labeling' )
await ElMessageBox . confirm ( '确认仓库上传的换标结果无误并继续出库流程吗?' , '换标完成确认' , { confirmButtonText : '确认并继续' , cancelButtonText : '取消' } )
await confirmRelabelCompletion ( relabelNo )
ElMessage . success ( '已确认换标完成,订单已进入后续出库流程' )
await load ( )
} catch ( error ) { if ( error !== 'cancel' ) ElMessage . error ( error instanceof Error ? error . message : '确认失败' ) }
}
}
async function cancelSelectedDiscard ( ) {
async function cancelSelectedDiscard ( ) {
const no = String ( selectedDetail . value ? . discardNo || selectedDetail . value ? . [ '0' ] || '' )
const no = String ( selectedDetail . value ? . discardNo || selectedDetail . value ? . [ '0' ] || '' )
@@ -1386,13 +1424,7 @@ async function selectTransferWarehouse() {
}
}
async function confirmSelectedRelabel ( ) {
async function confirmSelectedRelabel ( ) {
const relabelNo = selectedRelabelDetail . value ? . application . relabelNo
const relabelNo = selectedRelabelDetail . value ? . application . relabelNo
if ( ! relabelNo ) return
if ( relabelNo ) openCompletionConfirmation ( relabelNo , 'labeling' )
try {
await ElMessageBox . confirm ( '确认仓库上传的换标结果无误并继续出库流程吗?' , '换标完成确认' , { confirmButtonText : '确认并继续' , cancelButtonText : '取消' } )
await confirmRelabelCompletion ( relabelNo )
ElMessage . success ( '已确认换标完成,订单已进入后续出库流程' )
await openDetail ( { '0' : relabelNo } )
} catch ( error ) { if ( error !== 'cancel' ) ElMessage . error ( error instanceof Error ? error . message : '确认失败' ) }
}
}
async function resubmitRejectedRelabel ( ) {
async function resubmitRejectedRelabel ( ) {
const application = selectedRelabelDetail . value ? . application
const application = selectedRelabelDetail . value ? . application
@@ -1524,12 +1556,12 @@ function addReturnBoxBinding(source: RelabelInventoryCandidate) {
return
return
}
}
if ( cartonDetailRows . value . some ( item => item . cartonMark === source . cartonMark ) ) return
if ( cartonDetailRows . value . some ( item => item . cartonMark === source . cartonMark ) ) return
cargoRows . value . push ( { id : ++ cargoSequence , included : true , sku : source . skuItems ? . map ( item => item . skuCode ) . join ( ',' ) || '' , name : source . productNameZh || '' , shipmentNo : '' , cartonMark : source . cartonMark || '' , fbaCartonMark : '' , remark : '' , sourceInventoryBalanceId : undefined , sourceInventoryPositionId : undefined , sourceInboundOrderId : undefined , returnCartonId : String ( source . returnCartonId ) , quantity : 1 , fnsku : '' , newSkuCode : '' , shippingMark : '' , customerCartonMarks : '' , destinationWarehouseId : '' } )
cargoRows . value . push ( { id : ++ cargoSequence , included : true , sku : source . skuItems ? . map ( item => item . skuCode ) . join ( ',' ) || '' , name : source . productNameZh || '' , shipmentNo : '' , cartonMark : source . cartonMark || '' , fbaCartonMark : '' , remark : '' , sourceInventoryBalanceId : undefined , sourceInventoryPositionId : undefined , sourceInboundOrderId : undefined , returnCartonId : String ( source . returnCartonId ) , quantity : 1 , expectedPackageCount : 1 , fnsku : '' , newSkuCode : '' , shippingMark : '' , customerCartonMarks : '' , destinationWarehouseId : '' } )
}
}
< / script >
< / script >
< template >
< template >
< section class = "warehouse-service-page" : class = "{ 'warehouse-service-page--detail': mode === 'detail', 'warehouse-service-page--return-relabel': isReturnRelabelSource }" >
< section class = "warehouse-service-page" : class = "{ 'warehouse-service-page--detail': mode === 'detail', 'warehouse-service-page--transfer': props.service === 'transfer', 'warehouse-service-page--return-relabel': isReturnRelabelSource }" >
< template v-if = "mode === 'list'" >
< template v-if = "mode === 'list'" >
< QueryToolbar v-model = "listQueryModel" :class="{ 'labeling-query': props.service === 'labeling', 'transfer-query': props.service === 'transfer' || props.service === 'disposal', 'disposal-query': props.service === 'disposal' }" layout="grid" :ariaLabel="`${config.title}筛选`" :fields="listQueryFields" :search-on-reset="false" @search="query" @reset="resetListQuery" / >
< QueryToolbar v-model = "listQueryModel" :class="{ 'labeling-query': props.service === 'labeling', 'transfer-query': props.service === 'transfer' || props.service === 'disposal', 'disposal-query': props.service === 'disposal' }" layout="grid" :ariaLabel="`${config.title}筛选`" :fields="listQueryFields" :search-on-reset="false" @search="query" @reset="resetListQuery" / >
< div class = "warehouse-card warehouse-table-card" >
< div class = "warehouse-card warehouse-table-card" >
@@ -1587,7 +1619,7 @@ function addReturnBoxBinding(source: RelabelInventoryCandidate) {
< 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 >
< 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 = "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 === '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 === 'relabel'"><span><i>*</i> 预估总体积(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' || 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 >
< 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>目的去向</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 >
@@ -1671,8 +1703,8 @@ function addReturnBoxBinding(source: RelabelInventoryCandidate) {
</template>
</template>
<el-alert v-else type=" info " :closable=" false " :title=" discardSelectedCandidate . cartonSelectionBlockReason || '当前库存缺少完整箱级记录,暂时只能选择全部库存废弃。' " />
<el-alert v-else type=" info " :closable=" false " :title=" discardSelectedCandidate . cartonSelectionBlockReason || '当前库存缺少完整箱级记录,暂时只能选择全部库存废弃。' " />
</section>
</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 - 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 ( ) && Number ( row . expectedPackageCount ) > 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>
<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 v-if=" props . service === 'transfer' ">SKU 数量(件)*</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 v-if=" props . service === 'transfer' "><el-input-number v-model=" row . quantity " :min=" 1 " :precision=" 0 " controls-position=" right " placeholder=" 请输入件数 " /></td><td v-if=" props . service === 'transfer' "><el-input-number v-model=" row . expectedPackageCount " :min=" 1 " :precision=" 0 " controls-position=" right " placeholder=" 请输入预计箱数 " /></td><td v-else ><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' || props . service === 'transfer' ? 5 : 4 "><div class=" warehouse - empty ">{{ props.service === 'disposal' ? '请先选择入库单,系统将自动带出 SKU 和可用箱数' : '暂无货物明细,请点击“添加行”' }}</div></td></tr></tbody></table></div>
</template>
</template>
</div>
</div>
<div v-if=" props . service === 'transfer' && form . relabel " class=" warehouse - card warehouse - form - section warehouse - transfer - relabel ">
<div v-if=" props . service === 'transfer' && form . relabel " class=" warehouse - card warehouse - form - section warehouse - transfer - relabel ">
@@ -1708,24 +1740,57 @@ function addReturnBoxBinding(source: RelabelInventoryCandidate) {
<header class=" warehouse - detail - header ">
<header class=" warehouse - detail - header ">
<button type=" button " class=" warehouse - detail - back " @click=" returnToList "><ArrowLeft :size=" 16 " />返回列表</button>
<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 - 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' && [ '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>
<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=" canConfirmDiscard && props . service === 'disposal' && selectedDetail ? . status === 'PDA_COMPLETED' " type=" primary " @click=" openCompletionConfirmation ( String ( selectedDetail ? . discardNo || '' ) , 'disposal' ) ">废弃确认</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 v-if=" props . service !== 'transfer' " :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>
</header>
<div v-if=" detailError " class=" warehouse - detail - error " role=" alert "><strong>{{ detailError }}</strong><el-button @click=" loadDetailPage ">重新加载</el-button></div>
<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 || '平台未填写驳回备注' " />
<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-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>预报 SKU 种数</span><strong>{{ new Set((selectedDetail?.items || []).map(item => item.skuCode).filter(Boolean)).size }}</strong></div><div><span>预计箱数</span><strong>{{ selectedDetail?.packageCount ?? '—' }}</strong></div><div><span>实际签收箱数</span><strong>{{ selectedDetail?.cartons?.filter((carton) => carton.status === 'COUNTED').length ?? '—' }}</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><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 ">
<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 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>
<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>
<div v-if=" props . service === 'transfer' " class=" warehouse - card warehouse - form - section warehouse - transfer - measurement - card ">
<div v-if=" props . service === 'transfer' " class=" warehouse - card warehouse - form - section warehouse - transfer - cargo - card ">
<div class=" warehouse - section - heading "><h3>PDA 打托实测数据</h3><span class=" warehouse - hint ">PDA 作业完成即显示入库完成,客户可确认仓库实测数据</span ></div>
<div class=" warehouse - section - heading "><h3>预报与实际签收</h3 ></div>
<div v-if=" selectedDetail ? . pallets ? . length " class=" warehouse - table - scroll "><table><thead><tr><th>托盘号</th><th>库位</th><th>箱数</th><th>长 × 宽 × 高(cm) </th><th>毛重(kg) </th><th>体积(m³) </th><th>状态 </th><th>完成时间 </th></tr></thead><tbody><tr v-for="pallet in selectedDetail . pallets " :key=" String ( pallet . id || pallet . sequenceNo ) "><td>{{ pallet.palletNo || `第${pallet.sequenceNo || '—'}托` }}</td><td>{{ pallet.warehouseLocationCode || '—' }}</td><td>{{ pallet.cartonCount ?? '—' }}</td><td>{{ [pallet.lengthCm, pallet.widthCm, pallet.heightCm].every((value) => value != null) ? `${pallet.lengthCm} × ${pallet.widthCm} × ${pallet.heightCm}` : '—' }}</td><td>{{ pallet.grossWeightKg ?? '—' }}</td><td>{{ pallet.volumeCbm ?? '—' }}</td><td>{{ statusLabel(pallet.status || '') }}</td><td>{{ formatDateTime(pallet.completedAt) }} </td></tr></tbody></table></div>
<div class=" warehouse - table - scroll "><table class=" warehouse - edit - table warehouse - transfer - comparison - table "><colgroup><col style=" width : 500 px " /><col style=" width : 140 px " /><col style=" width : 130 px " /><col style=" width : 100 px " /><col style=" width : 130 px " /><col style=" width : 130 px " /></colgroup><thead><tr><th>商品 SKU</th><th>中文品名 </th><th>预报数量(件) </th><th>预计箱数 </th><th>实际签收件数 </th><th>实际签收箱数 </th></tr></thead><tbody><tr v-for="( item , index ) in selectedDetail ? . items || [ ] " :key=" String ( item . id || index ) "><td>{{ item.skuCode || '—' }}</td><td>{{ item.productNameZh || '—' }}</td><td>{{ item.quantity ?? '—' }}</td><td>{{ item.packageCount ?? '—' }}</td><td>{{ receivedItemQuantity(item.id) }}</td><td>{{ receivedItemCartons(item.id).length }}</td></tr><tr v-if=" ! selectedDetail ? . items ? . length "><td colspan=" 6 "><div class=" warehouse - empty ">暂无货物明细</div> </td></tr></tbody></table></div>
<div v-e lse class=" warehouse - empty ">暂无 PDA 打托测量数据</div >
<detai ls class=" warehouse - transfer - pallet - group " open >
<summary class=" warehouse - section - heading warehouse - transfer - pallet - heading warehouse - transfer - pallet - summary "><h3>PDA 打托实测数据</h3><span class=" warehouse - transfer - pallet - count ">{{ selectedDetail?.pallets?.length || 0 }} 托</span><span class=" warehouse - transfer - pallet - toggle "><span class=" when - collapsed ">展开托盘</span><span class=" when - expanded ">收起托盘</span><ChevronDown :size=" 14 " /></span></summary>
<div v-if=" selectedDetail ? . pallets ? . length " class=" warehouse - transfer - pallet - list ">
<article v-for=" pallet in selectedDetail . pallets " :key=" String ( pallet . id || pallet . sequenceNo ) " class=" warehouse - transfer - pallet - card ">
<header class=" warehouse - transfer - pallet - header ">
<strong><span>托盘号</span> {{ pallet.palletNo || `第${pallet.sequenceNo || '—'}托` }}</strong>
<dl class=" warehouse - transfer - pallet - metrics ">
<div><dt>箱数</dt><dd>{{ palletCartons(pallet).length }} 箱</dd></div><div><dt>件数</dt><dd>{{ palletQuantity(pallet) }} 件</dd></div><div><dt>SKU 种数</dt><dd>{{ palletSkuRows(pallet).length }} 种</dd></div><div><dt>毛重(kg)</dt><dd>{{ pallet.grossWeightKg ?? '—' }}</dd></div><div><dt>体积(m³)</dt><dd>{{ pallet.volumeCbm ?? '—' }}</dd></div>
</dl>
</header>
<div class=" warehouse - transfer - pallet - skus "><span>SKU 汇总</span><div><div v-for=" item in palletSkuRows ( pallet ) " :key=" item . sku " class=" warehouse - transfer - pallet - sku "><strong>{{ item.sku }}</strong><span>{{ item.quantity }} 件</span></div><span v-if=" ! palletSkuRows ( pallet ) . length ">暂无已清点 SKU</span></div></div>
<div class=" warehouse - transfer - pallet - meta "><span>库位:{{ pallet.warehouseLocationCode || '—' }}</span><span>完成时间:{{ formatDateTime(pallet.completedAt) }}</span></div>
</article>
</div>
<div v-else class=" warehouse - empty ">暂无 PDA 打托测量数据</div>
</details>
<div class=" warehouse - evidence warehouse - measurement - evidence "><span>测量打托现场照片</span><div v-if=" selectedDetail ? . measurementPhotos ? . length " class=" warehouse - photo - grid warehouse - measurement - photo - grid "><div v-for=" ( photo , index ) in selectedDetail . measurementPhotos " :key=" photo . fileId " class=" warehouse - photo - item warehouse - measurement - photo - item " :title=" photo . fileName || ` 测量照片 ${ index + 1 } ` "><el-image v-if=" measurementPhotoUrls [ photo . fileId ] " :src=" measurementPhotoUrls [ photo . fileId ] " :preview-src-list=" measurementPhotoPreviewUrls " :initial-index=" measurementPhotoPreviewUrls . indexOf ( measurementPhotoUrls [ photo . fileId ] ) " :alt=" photo . fileName || ` 测量照片 ${ index + 1 } ` " fit=" cover " preview-teleported /><div v-else-if=" measurementPhotoLoading [ photo . fileId ] " class=" warehouse - photo - placeholder ">照片 {{ index + 1 }} 加载中…</div><button v-else class=" warehouse - photo - placeholder warehouse - photo - retry " type=" button " @click=" loadMeasurementPhoto ( photo . fileId ) ">{{ measurementPhotoErrors[photo.fileId] ? `照片 ${index + 1} 加载失败,点击重试` : `加载照片 ${index + 1}` }}</button></div></div><strong v-else>仓库尚未上传测量打托照片</strong></div>
<div class=" warehouse - evidence warehouse - measurement - evidence "><span>测量打托现场照片</span><div v-if=" selectedDetail ? . measurementPhotos ? . length " class=" warehouse - photo - grid warehouse - measurement - photo - grid "><div v-for=" ( photo , index ) in selectedDetail . measurementPhotos " :key=" photo . fileId " class=" warehouse - photo - item warehouse - measurement - photo - item " :title=" photo . fileName || ` 测量照片 ${ index + 1 } ` "><el-image v-if=" measurementPhotoUrls [ photo . fileId ] " :src=" measurementPhotoUrls [ photo . fileId ] " :preview-src-list=" measurementPhotoPreviewUrls " :initial-index=" measurementPhotoPreviewUrls . indexOf ( measurementPhotoUrls [ photo . fileId ] ) " :alt=" photo . fileName || ` 测量照片 ${ index + 1 } ` " fit=" cover " preview-teleported /><div v-else-if=" measurementPhotoLoading [ photo . fileId ] " class=" warehouse - photo - placeholder ">照片 {{ index + 1 }} 加载中…</div><button v-else class=" warehouse - photo - placeholder warehouse - photo - retry " type=" button " @click=" loadMeasurementPhoto ( photo . fileId ) ">{{ measurementPhotoErrors[photo.fileId] ? `照片 ${index + 1} 加载失败,点击重试` : `加载照片 ${index + 1}` }}</button></div></div><strong v-else>仓库尚未上传测量打托照片</strong></div>
</div>
</div>
<div v-if=" props . service === 'transfer' " class=" warehouse - card warehouse - form - section warehouse - transfer - cargo - card ">
<div v-if=" props . service === 'transfer' " class=" warehouse - card warehouse - form - section warehouse - transfer - received - card ">
<div class=" warehouse - section - heading "><h3>货物明细</h3 ><el-button v-if=" selectedDetail ? . status !== 'DRAFT' " native-type=" button " type=" primary " @click=" printTransferCartons ( String ( selectedDetail ? . transferNo || selectedDetail ? . [ '0' ] || '' ) ) "><Printer :size=" 16 " />打印箱唛</el-button></div>
<div class=" warehouse - section - heading warehouse - transfer - receipt - heading "><h3>签收箱号明细({{ countedTransferCartons().length }} 箱)</h3><div class=" warehouse - transfer - receipt - actions "><el-button v-if=" selectedDetail ? . status !== 'DRAFT' " native-type=" button " type=" primary " size=" small " @click="printTransferCartons ( String ( selectedDetail ? . transferNo || selectedDetail ? . [ '0' ] || '' ) ) "><Printer :size=" 14 " />打印箱唛</el-button><el-button type=" primary " size=" small " :disabled=" ! countedTransferCartons ( ) . length " @click=" showTransferCartons = true ">查看箱号明细</el-button></div>< /div>
<div class=" warehouse - table - scroll "><table class=" warehouse - edit - table "><thead><tr><th>商品 SKU</th><th>中文品名</th><th>预计箱数</th><th>客户箱唛号</th></tr></thead><tbody><tr v-for=" ( item , index ) in selectedDetail ? . items || [ ] " :key=" ` ${ item . skuCode } - ${ index } ` "><td>{{ item.skuCode || '—' }}</td><td>{{ item.productNameZh || '—' }}</td><td>{{ item.packageCount ?? '—' }}</td><td>{{ item.shippingMark || '—' }}</td></tr><tr v-if=" ! selectedDetail ? . items ? . length "><td colspan=" 4 "><div class=" warehouse - empty ">暂无货物明细</div></td></tr></tbody></table> </div>
<div v-if=" ! countedTransferCartons ( ) . length " class=" warehouse - empty ">暂无实际签收箱,等待 PDA 清点。 </div>
<el-dialog v-model=" showTransferCartons " title=" 签收箱号明细 " width=" 1120 px " align-center destroy-on-close append-to-body>
<div class=" warehouse - cartons - dialog - body ">
<el-collapse>
<el-collapse-item v-for=" carton in countedTransferCartons ( ) " :key=" carton . cartonMark " :name=" carton . cartonMark ">
<template #title><strong>{{ carton.cartonMark }}</strong><span class=" warehouse - carton - summary ">已确认 · {{ transferCartonItem(carton)?.skuCode ? 1 : 0 }} 种 SKU · {{ carton.quantity ?? '—' }} 件</span></template>
<div class=" warehouse - carton - measurements ">所属托盘:{{ selectedDetail?.pallets?.find((pallet) => carton.transferPalletId != null && String(pallet.id) === String(carton.transferPalletId))?.palletNo || '散箱' }} · 清点时间:{{ formatDateTime(carton.countedAt) }}</div>
<div class=" warehouse - carton - measurements ">长 {{ carton.length ?? '—' }} cm · 宽 {{ carton.width ?? '—' }} cm · 高 {{ carton.height ?? '—' }} cm · 重量 {{ carton.weight ?? '—' }} kg · 体积 {{ [carton.length, carton.width, carton.height].every(value => value != null) ? (Number(carton.length) * Number(carton.width) * Number(carton.height) / 1000000).toFixed(6) : '—' }} m³</div>
<el-table :data=" [ { skuCode : transferCartonItem ( carton ) ? . skuCode || '—' , productNameZh : transferCartonItem ( carton ) ? . productNameZh || '—' , quantity : carton . quantity ? ? '—' } ] " size=" small " border>
<el-table-column prop=" skuCode " label=" SKU " min-width=" 220 " />
<el-table-column prop=" productNameZh " label=" 中文品名 " min-width=" 180 " />
<el-table-column prop=" quantity " label=" 实际签收数量 " width=" 130 " />
</el-table>
<p v-if=" carton . discrepancyReason " class=" warehouse - carton - measurements ">差异说明:{{ carton.discrepancyReason }}</p>
</el-collapse-item>
</el-collapse>
</div>
<template #footer><el-button @click=" showTransferCartons = false ">关闭</el-button></template>
</el-dialog>
</div>
</div>
<div v-if=" props . service === 'transfer' && selectedDetail ? . needRelabel " class=" warehouse - card warehouse - form - section ">
<div v-if=" props . service === 'transfer' && selectedDetail ? . needRelabel " class=" warehouse - card warehouse - form - section ">
<h3>换标箱级去向</h3>
<h3>换标箱级去向</h3>
@@ -1758,6 +1823,7 @@ function addReturnBoxBinding(source: RelabelInventoryCandidate) {
</div>
</div>
</template>
</template>
</section>
</section>
<CompletionConfirmationDialog v-model=" completionConfirmOpen " :kind=" completionConfirmKind " :document-no=" completionConfirmNo " @confirmed=" refreshAfterCompletion " / >
< / template >
< / template >
< style scoped >
< style scoped >
@@ -1901,7 +1967,7 @@ function addReturnBoxBinding(source: RelabelInventoryCandidate) {
. warehouse - native - file { min - height : 32 px ; padding : 5 px 8 px ; border : 1 px solid # dcdfe6 ; border - radius : 4 px ; background : # fff ; color : # 606266 } . warehouse - fulfillment - grid { margin - bottom : 18 px } . warehouse - evidence { display : flex ; align - items : flex - start ; gap : 16 px ; padding : 14 px ; border : 1 px solid # e7ebf1 ; border - radius : 4 px ; background : # f8fafc } . warehouse - evidence > span { padding - top : 6 px ; color : # 71809 a ; font - size : 12 px ; white - space : nowrap } . warehouse - evidence > strong { padding - top : 6 px ; color : # 8 a95a5 ; font - size : 12 px ; font - weight : 400 } . warehouse - photo - grid { display : grid ; grid - template - columns : repeat ( 4 , 140 px ) ; gap : 12 px } . warehouse - photo - item { width : 140 px ; height : 105 px ; overflow : hidden ; border : 1 px solid # dce3ed ; border - radius : 5 px ; background : # fff } . warehouse - photo - item : deep ( . el - image ) { display : block ; width : 100 % ; height : 100 % ; cursor : zoom - in } . warehouse - photo - placeholder { display : flex ; width : 100 % ; height : 100 % ; align - items : center ; justify - content : center ; padding : 10 px ; border : 0 ; background : # f4f7fb ; color : # 8792 a2 ; font - size : 12 px ; text - align : center } . warehouse - photo - retry { cursor : pointer } . warehouse - completion - remark { margin : 10 px 0 0 ; color : # 596579 ; font - size : 12 px }
. warehouse - native - file { min - height : 32 px ; padding : 5 px 8 px ; border : 1 px solid # dcdfe6 ; border - radius : 4 px ; background : # fff ; color : # 606266 } . warehouse - fulfillment - grid { margin - bottom : 18 px } . warehouse - evidence { display : flex ; align - items : flex - start ; gap : 16 px ; padding : 14 px ; border : 1 px solid # e7ebf1 ; border - radius : 4 px ; background : # f8fafc } . warehouse - evidence > span { padding - top : 6 px ; color : # 71809 a ; font - size : 12 px ; white - space : nowrap } . warehouse - evidence > strong { padding - top : 6 px ; color : # 8 a95a5 ; font - size : 12 px ; font - weight : 400 } . warehouse - photo - grid { display : grid ; grid - template - columns : repeat ( 4 , 140 px ) ; gap : 12 px } . warehouse - photo - item { width : 140 px ; height : 105 px ; overflow : hidden ; border : 1 px solid # dce3ed ; border - radius : 5 px ; background : # fff } . warehouse - photo - item : deep ( . el - image ) { display : block ; width : 100 % ; height : 100 % ; cursor : zoom - in } . warehouse - photo - placeholder { display : flex ; width : 100 % ; height : 100 % ; align - items : center ; justify - content : center ; padding : 10 px ; border : 0 ; background : # f4f7fb ; color : # 8792 a2 ; font - size : 12 px ; text - align : center } . warehouse - photo - retry { cursor : pointer } . warehouse - completion - remark { margin : 10 px 0 0 ; color : # 596579 ; font - size : 12 px }
. warehouse - measurement - evidence { margin - top : 16 px }
. warehouse - measurement - evidence { margin - top : 16 px }
. warehouse - measurement - photo - grid { flex : 1 ; grid - template - columns : repeat ( auto - fill , minmax ( 240 px , 1 fr ) ) }
. warehouse - measurement - photo - grid { flex : 1 ; grid - template - columns : repeat ( auto - fill , minmax ( 240 px , 1 fr ) ) }
. warehouse - measurement - photo - item { width : 100 % ; height : 180 px }
. warehouse - measurement - photo - item { width : 100 % ; height : 120 px }
. warehouse - template - actions { flex - wrap : wrap ; justify - content : flex - end } . warehouse - import - preview { margin : 0 0 16 px ; padding : 14 px ; border : 1 px solid # cfe0f5 ; border - radius : 5 px ; background : # f8fbff } . warehouse - import - preview . warehouse - section - heading { margin - bottom : 10 px } . warehouse - import - preview . warehouse - section - heading > div { display : flex ; align - items : center ; gap : 10 px } . warehouse - import - preview p { margin : 0 ; color : # 7 d8797 ; font - size : 12 px } . warehouse - binding - preview - table { min - width : 980 px ; background : # fff }
. warehouse - template - actions { flex - wrap : wrap ; justify - content : flex - end } . warehouse - import - preview { margin : 0 0 16 px ; padding : 14 px ; border : 1 px solid # cfe0f5 ; border - radius : 5 px ; background : # f8fbff } . warehouse - import - preview . warehouse - section - heading { margin - bottom : 10 px } . warehouse - import - preview . warehouse - section - heading > div { display : flex ; align - items : center ; gap : 10 px } . warehouse - import - preview p { margin : 0 ; color : # 7 d8797 ; font - size : 12 px } . warehouse - binding - preview - table { min - width : 980 px ; background : # fff }
. warehouse - transfer - relabel . warehouse - labeling - heading > div : first - child { min - width : 0 } . warehouse - transfer - relabel . warehouse - labeling - heading h3 { margin - bottom : 6 px } . warehouse - transfer - relabel . warehouse - labeling - heading p { margin : 0 0 16 px ; color : # 7 d8797 ; font - size : 12 px } . warehouse - transfer - relabel - fields { margin - bottom : 18 px } . warehouse - transfer - file - groups { display : grid ; grid - template - columns : minmax ( 0 , 1.15 fr ) minmax ( 360 px , .85 fr ) ; align - items : stretch ; gap : 14 px ; margin - bottom : 14 px } . warehouse - transfer - total - files , . warehouse - transfer - template - group { height : 100 % ; margin : 0 } . warehouse - transfer - mark - table { min - width : 1420 px } . warehouse - transfer - mark - table td { vertical - align : top } . warehouse - transfer - mark - table : deep ( . el - textarea ) { min - width : 220 px } . warehouse - transfer - mark - table : deep ( . el - input - number ) { width : 150 px } . warehouse - count - error { color : # d64545 }
. warehouse - transfer - relabel . warehouse - labeling - heading > div : first - child { min - width : 0 } . warehouse - transfer - relabel . warehouse - labeling - heading h3 { margin - bottom : 6 px } . warehouse - transfer - relabel . warehouse - labeling - heading p { margin : 0 0 16 px ; color : # 7 d8797 ; font - size : 12 px } . warehouse - transfer - relabel - fields { margin - bottom : 18 px } . warehouse - transfer - file - groups { display : grid ; grid - template - columns : minmax ( 0 , 1.15 fr ) minmax ( 360 px , .85 fr ) ; align - items : stretch ; gap : 14 px ; margin - bottom : 14 px } . warehouse - transfer - total - files , . warehouse - transfer - template - group { height : 100 % ; margin : 0 } . warehouse - transfer - mark - table { min - width : 1420 px } . warehouse - transfer - mark - table td { vertical - align : top } . warehouse - transfer - mark - table : deep ( . el - textarea ) { min - width : 220 px } . warehouse - transfer - mark - table : deep ( . el - input - number ) { width : 150 px } . warehouse - count - error { color : # d64545 }
. warehouse - transfer - form { display : grid ; grid - template - columns : minmax ( 0 , 1 fr ) ; gap : 12 px } . warehouse - transfer - form -- relabel { grid - template - columns : minmax ( 0 , 1 fr ) minmax ( 0 , 1.35 fr ) ; align - items : start } . warehouse - transfer - form -- relabel . warehouse - transfer - main - card { grid - column : 1 ; grid - row : 1 } . warehouse - transfer - form -- relabel . warehouse - transfer - cargo - card { grid - column : 1 ; grid - row : 2 } . warehouse - transfer - form -- relabel . warehouse - transfer - relabel { grid - column : 2 ; grid - row : 1 / span 2 ; margin - bottom : 0 } . warehouse - transfer - form -- relabel . warehouse - transfer - submit - bar { grid - column : 1 / - 1 } . warehouse - transfer - form : deep ( . el - input _ _wrapper ) , . warehouse - transfer - form : deep ( . el - textarea _ _inner ) , . warehouse - transfer - form : deep ( . el - select _ _wrapper ) , . warehouse - transfer - form : deep ( . el - date - editor . el - input _ _wrapper ) { min - height : 32 px } . warehouse - transfer - form . warehouse - form - section { padding : 16 px } . warehouse - transfer - form . warehouse - form - section h3 { margin - bottom : 12 px ; padding - bottom : 9 px ; font - size : 14 px } . warehouse - transfer - form . warehouse - form - grid { gap : 12 px 16 px } . warehouse - transfer - form . warehouse - choice - field { min - height : 34 px ; padding : 8 px 10 px ; grid - template - columns : 118 px minmax ( 0 , 1 fr ) ; gap : 10 px } . warehouse - transfer - form . warehouse - choice - field > span { font - size : 12 px } . warehouse - transfer - form . warehouse - receiving - card { gap : 12 px ; padding : 10 px 12 px } . warehouse - transfer - form . warehouse - section - heading { margin - bottom : 8 px } . warehouse - transfer - form . warehouse - edit - table { font - size : 11 px } . warehouse - transfer - form . warehouse - edit - table th { height : 36 px ; padding : 0 9 px } . warehouse - transfer - form . warehouse - edit - table td { height : 42 px ; padding : 6 px 7 px } . warehouse - transfer - form . warehouse - transfer - mark - table : deep ( . el - textarea ) { min - width : 150 px } . warehouse - transfer - form . warehouse - transfer - mark - table : deep ( . el - input - number ) { width : 112 px } . warehouse - transfer - form . warehouse - hint { margin - top : 5 px ; font - size : 11 px } . warehouse - transfer - form . warehouse - labeling - mode { gap : 9 px ; margin - bottom : 10 px ; padding : 9 px 10 px } . warehouse - transfer - form . warehouse - labeling - mode > span { font - size : 12 px } . warehouse - transfer - form . warehouse - labeling - mode : deep ( . el - radio - button _ _inner ) { padding : 6 px 9 px } . warehouse - transfer - form . warehouse - template - actions { display : flex ; flex - wrap : wrap ; justify - content : flex - end ; gap : 5 px ; padding - bottom : 8 px } . warehouse - transfer - form . warehouse - template - actions : deep ( . el - button ) { padding : 6 px 8 px ; font - size : 11 px } . warehouse - transfer - form . warehouse - submit - bar { padding : 4 px 0 18 px } . warehouse - transfer - form . warehouse - submit - bar : deep ( . el - button ) { min - height : 32 px ; padding : 7 px 14 px } . warehouse - transfer - form . warehouse - transfer - relabel { overflow : hidden } . warehouse - transfer - form . warehouse - transfer - relabel - fields { margin - bottom : 10 px } . warehouse - step - heading { align - items : flex - start } . warehouse - step - heading h3 { display : flex ; align - items : center ; gap : 9 px } . warehouse - step - number { display : inline - grid ; flex : 0 0 auto ; width : 24 px ; height : 24 px ; border - radius : 50 % ; background : # 3478 c8 ; color : # fff ; font - size : 12 px ; place - items : center } . warehouse - heading - actions { display : flex ; flex - wrap : wrap ; justify - content : flex - end ; gap : 8 px } . warehouse - transfer - template - group { margin : 0 0 14 px ; padding : 12 px 14 px ; border : 1 px solid # dde5f0 ; border - radius : 7 px ; background : # fbfcfe } . warehouse - binding - heading { margin - top : 14 px ; padding : 12 px 14 px ; border : 1 px solid # dde5f0 ; border - radius : 7 px ; background : # f8faff } . warehouse - binding - heading strong { display : flex ; align - items : center ; gap : 8 px ; color : # 34445 c ; font - size : 13 px }
. warehouse - transfer - form { display : grid ; grid - template - columns : minmax ( 0 , 1 fr ) ; gap : 12 px } . warehouse - transfer - form -- relabel { grid - template - columns : minmax ( 0 , 1 fr ) minmax ( 0 , 1.35 fr ) ; align - items : start } . warehouse - transfer - form -- relabel . warehouse - transfer - main - card { grid - column : 1 ; grid - row : 1 } . warehouse - transfer - form -- relabel . warehouse - transfer - cargo - card { grid - column : 1 ; grid - row : 2 } . warehouse - transfer - form -- relabel . warehouse - transfer - relabel { grid - column : 2 ; grid - row : 1 / span 2 ; margin - bottom : 0 } . warehouse - transfer - form -- relabel . warehouse - transfer - submit - bar { grid - column : 1 / - 1 } . warehouse - transfer - form : deep ( . el - input _ _wrapper ) , . warehouse - transfer - form : deep ( . el - textarea _ _inner ) , . warehouse - transfer - form : deep ( . el - select _ _wrapper ) , . warehouse - transfer - form : deep ( . el - date - editor . el - input _ _wrapper ) { min - height : 32 px } . warehouse - transfer - form . warehouse - form - section { padding : 16 px } . warehouse - transfer - form . warehouse - form - section h3 { margin - bottom : 12 px ; padding - bottom : 9 px ; font - size : 14 px } . warehouse - transfer - form . warehouse - form - grid { gap : 12 px 16 px } . warehouse - transfer - form . warehouse - choice - field { min - height : 34 px ; padding : 8 px 10 px ; grid - template - columns : 118 px minmax ( 0 , 1 fr ) ; gap : 10 px } . warehouse - transfer - form . warehouse - choice - field > span { font - size : 12 px } . warehouse - transfer - form . warehouse - receiving - card { gap : 12 px ; padding : 10 px 12 px } . warehouse - transfer - form . warehouse - section - heading { margin - bottom : 8 px } . warehouse - transfer - form . warehouse - edit - table { font - size : 11 px } . warehouse - transfer - form . warehouse - edit - table th { height : 36 px ; padding : 0 9 px } . warehouse - transfer - form . warehouse - edit - table td { height : 42 px ; padding : 6 px 7 px } . warehouse - transfer - form . warehouse - transfer - mark - table : deep ( . el - textarea ) { min - width : 150 px } . warehouse - transfer - form . warehouse - transfer - mark - table : deep ( . el - input - number ) { width : 112 px } . warehouse - transfer - form . warehouse - hint { margin - top : 5 px ; font - size : 11 px } . warehouse - transfer - form . warehouse - labeling - mode { gap : 9 px ; margin - bottom : 10 px ; padding : 9 px 10 px } . warehouse - transfer - form . warehouse - labeling - mode > span { font - size : 12 px } . warehouse - transfer - form . warehouse - labeling - mode : deep ( . el - radio - button _ _inner ) { padding : 6 px 9 px } . warehouse - transfer - form . warehouse - template - actions { display : flex ; flex - wrap : wrap ; justify - content : flex - end ; gap : 5 px ; padding - bottom : 8 px } . warehouse - transfer - form . warehouse - template - actions : deep ( . el - button ) { padding : 6 px 8 px ; font - size : 11 px } . warehouse - transfer - form . warehouse - submit - bar { padding : 4 px 0 18 px } . warehouse - transfer - form . warehouse - submit - bar : deep ( . el - button ) { min - height : 32 px ; padding : 7 px 14 px } . warehouse - transfer - form . warehouse - transfer - relabel { overflow : hidden } . warehouse - transfer - form . warehouse - transfer - relabel - fields { margin - bottom : 10 px } . warehouse - step - heading { align - items : flex - start } . warehouse - step - heading h3 { display : flex ; align - items : center ; gap : 9 px } . warehouse - step - number { display : inline - grid ; flex : 0 0 auto ; width : 24 px ; height : 24 px ; border - radius : 50 % ; background : # 3478 c8 ; color : # fff ; font - size : 12 px ; place - items : center } . warehouse - heading - actions { display : flex ; flex - wrap : wrap ; justify - content : flex - end ; gap : 8 px } . warehouse - transfer - template - group { margin : 0 0 14 px ; padding : 12 px 14 px ; border : 1 px solid # dde5f0 ; border - radius : 7 px ; background : # fbfcfe } . warehouse - binding - heading { margin - top : 14 px ; padding : 12 px 14 px ; border : 1 px solid # dde5f0 ; border - radius : 7 px ; background : # f8faff } . warehouse - binding - heading strong { display : flex ; align - items : center ; gap : 8 px ; color : # 34445 c ; font - size : 13 px }
@@ -1914,6 +1980,7 @@ function addReturnBoxBinding(source: RelabelInventoryCandidate) {
. warehouse - service - page -- detail . warehouse - transfer - overview - grid { grid - template - columns : repeat ( 4 , minmax ( 180 px , 1 fr ) ) ; gap : 6 px 28 px ; padding : 10 px 16 px 12 px } . warehouse - service - page -- detail . warehouse - transfer - overview - grid > div { min - height : 26 px ; padding : 2 px 0 ; border : 0 } . warehouse - service - page -- detail . warehouse - transfer - overview - grid span { flex : 0 0 auto ; min - width : 76 px } . warehouse - service - page -- detail . warehouse - transfer - overview - grid : deep ( . el - tag ) { height : 24 px }
. warehouse - service - page -- detail . warehouse - transfer - overview - grid { grid - template - columns : repeat ( 4 , minmax ( 180 px , 1 fr ) ) ; gap : 6 px 28 px ; padding : 10 px 16 px 12 px } . warehouse - service - page -- detail . warehouse - transfer - overview - grid > div { min - height : 26 px ; padding : 2 px 0 ; border : 0 } . warehouse - service - page -- detail . warehouse - transfer - overview - grid span { flex : 0 0 auto ; min - width : 76 px } . warehouse - service - page -- detail . warehouse - transfer - overview - grid : deep ( . el - tag ) { height : 24 px }
. warehouse - service - page -- detail > . warehouse - detail - header + . warehouse - card . warehouse - detail - grid { grid - template - columns : repeat ( 4 , minmax ( 180 px , 1 fr ) ) ; gap : 6 px 28 px ; padding : 10 px 16 px 12 px } . warehouse - service - page -- detail > . warehouse - detail - header + . warehouse - card . warehouse - detail - grid > div { min - height : 26 px ; padding : 2 px 0 ; border : 0 } . warehouse - service - page -- detail > . warehouse - detail - header + . warehouse - card . warehouse - detail - grid span { flex : 0 0 auto ; min - width : 76 px }
. warehouse - service - page -- detail > . warehouse - detail - header + . warehouse - card . warehouse - detail - grid { grid - template - columns : repeat ( 4 , minmax ( 180 px , 1 fr ) ) ; gap : 6 px 28 px ; padding : 10 px 16 px 12 px } . warehouse - service - page -- detail > . warehouse - detail - header + . warehouse - card . warehouse - detail - grid > div { min - height : 26 px ; padding : 2 px 0 ; border : 0 } . warehouse - service - page -- detail > . warehouse - detail - header + . warehouse - card . warehouse - detail - grid span { flex : 0 0 auto ; min - width : 76 px }
. warehouse - service - page -- detail . warehouse - transfer - overview - grid > . warehouse - overview - wide { grid - column : span 2 }
. warehouse - service - page -- detail . warehouse - transfer - overview - grid > . warehouse - overview - wide { grid - column : span 2 }
. warehouse - transfer - received - card { margin - top : 14 px } . warehouse - cartons - dialog - body { max - height : 65 vh ; overflow : auto } . warehouse - carton - summary { margin - left : 16 px ; color : var ( -- muted ) ; font - size : 12 px } . warehouse - carton - measurements { margin : 10 px 0 ; color : var ( -- muted ) ; font - size : 13 px ; line - height : 1.8 }
@ media ( max - width : 1100 px ) { . warehouse - service - page -- detail . warehouse - transfer - overview - grid { grid - template - columns : repeat ( 2 , minmax ( 180 px , 1 fr ) ) } } @ media ( max - width : 720 px ) { . warehouse - service - page -- detail . warehouse - transfer - overview - grid { grid - template - columns : 1 fr ; gap : 4 px ; padding : 10 px 14 px } }
@ media ( max - width : 1100 px ) { . warehouse - service - page -- detail . warehouse - transfer - overview - grid { grid - template - columns : repeat ( 2 , minmax ( 180 px , 1 fr ) ) } } @ media ( max - width : 720 px ) { . warehouse - service - page -- detail . warehouse - transfer - overview - grid { grid - template - columns : 1 fr ; gap : 4 px ; padding : 10 px 14 px } }
/* Keep the count and controls visible together on narrow list pages. */
/* Keep the count and controls visible together on narrow list pages. */
. warehouse - pagination { justify - content : flex - end ; }
. warehouse - pagination { justify - content : flex - end ; }
@@ -1929,10 +1996,55 @@ function addReturnBoxBinding(source: RelabelInventoryCandidate) {
. warehouse - transfer - cargo - card . warehouse - section - heading , . warehouse - transfer - cargo - card . warehouse - table - scroll { max - width : none ; }
. warehouse - transfer - cargo - card . warehouse - section - heading , . warehouse - transfer - cargo - card . warehouse - table - scroll { max - width : none ; }
. warehouse - transfer - form . warehouse - choice - field -- relabel { grid - template - columns : 1 fr ; align - items : start ; }
. warehouse - transfer - form . warehouse - choice - field -- relabel { grid - template - columns : 1 fr ; align - items : start ; }
}
}
< / style >
< / style >
< style scoped >
< style scoped >
. warehouse - discard - boxes { margin - bottom : 24 px ; }
. 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 { display : flex ; align - items : center ; gap : 20 px ; margin - bottom : 16 px ; }
. warehouse - discard - box - toolbar . el - input { max - width : 340 px ; }
. warehouse - discard - box - toolbar . el - input { max - width : 340 px ; }
. warehouse - service - page -- detail . warehouse - table - scroll . warehouse - transfer - comparison - table { width : 1130 px ; min - width : 1130 px ; table - layout : fixed }
. warehouse - transfer - comparison - table td { white - space : normal ; overflow - wrap : anywhere }
. warehouse - service - page -- detail . warehouse - transfer - comparison - table th { height : 36 px ; padding : 0 10 px }
. warehouse - service - page -- detail . warehouse - transfer - comparison - table td { height : 38 px ; padding : 7 px 10 px }
. warehouse - transfer - comparison - table th : nth - child ( n + 3 ) , . warehouse - transfer - comparison - table td : nth - child ( n + 3 ) { text - align : center }
. warehouse - transfer - comparison - table td : first - child { white - space : nowrap ; overflow : hidden ; text - overflow : ellipsis }
. warehouse - service - page -- detail . warehouse - transfer - cargo - card . warehouse - transfer - pallet - heading { margin - top : 12 px }
. warehouse - transfer - pallet - list { display : grid ; grid - template - columns : minmax ( 0 , 1 fr ) ; gap : 12 px ; padding : 0 14 px ; margin - bottom : 12 px ; align - items : start }
. warehouse - transfer - pallet - card { padding : 14 px ; border : 1 px solid var ( -- client - border ) ; border - radius : 10 px ; background : # fff ; min - width : 0 }
. warehouse - transfer - pallet - header { display : flex ; align - items : center ; flex - wrap : wrap ; gap : 10 px 20 px }
. warehouse - transfer - pallet - header > strong { font - size : 13 px ; color : var ( -- navy ) ; overflow - wrap : anywhere ; line - height : 1.6 }
. warehouse - transfer - pallet - header strong > span { display : inline - block ; margin - right : 8 px ; padding : 2 px 7 px ; border - radius : 4 px ; background : # eef1ff ; color : var ( -- primary ) ; font - size : 12 px ; font - weight : 500 }
. warehouse - transfer - pallet - metrics { display : flex ; flex - wrap : wrap ; gap : 8 px ; margin : 0 }
. warehouse - transfer - pallet - metrics > div { display : flex ; align - items : baseline ; gap : 8 px ; padding : 6 px 10 px ; background : # f5f7fc ; border : 1 px solid # e9edf5 ; border - radius : 6 px }
. warehouse - transfer - pallet - metrics dt { font - size : 12 px ; color : var ( -- muted ) }
. warehouse - transfer - pallet - metrics dd { margin : 0 ; font - size : 13 px ; font - weight : 600 ; color : var ( -- navy ) }
. warehouse - transfer - pallet - summary { cursor : pointer ; list - style : none ; border - radius : 4 px }
. warehouse - transfer - pallet - summary : : - webkit - details - marker { display : none }
. warehouse - transfer - pallet - summary : focus - visible { outline : 2 px solid var ( -- primary ) ; outline - offset : 4 px }
. warehouse - transfer - pallet - toggle { display : flex ; align - items : center ; gap : 8 px ; flex - shrink : 0 ; margin - left : 6 px }
. warehouse - transfer - pallet - count { font - size : 12 px ; color : var ( -- muted ) }
. warehouse - service - page -- detail . warehouse - service - page -- transfer > . warehouse - detail - header { grid - template - columns : auto minmax ( 0 , max - content ) auto ; justify - content : start ; column - gap : 14 px }
. warehouse - service - page -- detail . warehouse - transfer - pallet - heading h3 { flex : 0 0 auto }
. warehouse - service - page -- detail . warehouse - transfer - pallet - heading . warehouse - transfer - pallet - count { flex : 0 0 auto }
. warehouse - service - page -- detail . warehouse - transfer - cargo - card . warehouse - transfer - pallet - heading { display : flex ; align - items : center ; justify - content : flex - start ; gap : 10 px }
. warehouse - transfer - pallet - toggle { color : var ( -- primary ) ; font - size : 12 px ; gap : 4 px }
. warehouse - transfer - pallet - group . when - expanded { display : none }
. warehouse - transfer - pallet - group [ open ] . when - expanded { display : inline }
. warehouse - transfer - pallet - group [ open ] . when - collapsed { display : none }
. warehouse - transfer - pallet - group [ open ] . warehouse - transfer - pallet - toggle svg { transform : rotate ( 180 deg ) }
. warehouse - transfer - pallet - meta { display : flex ; flex - wrap : wrap ; gap : 4 px 20 px ; margin - top : 8 px ; color : var ( -- muted ) ; font - size : 12 px ; line - height : 1.7 ; overflow - wrap : anywhere }
. warehouse - transfer - pallet - skus { display : grid ; grid - template - columns : 64 px minmax ( 0 , 1 fr ) ; align - items : start ; gap : 10 px ; margin - top : 10 px ; padding - top : 10 px ; border - top : 1 px solid var ( -- client - border ) }
. warehouse - transfer - pallet - skus > span { font - size : 12 px ; color : var ( -- muted ) ; padding - top : 5 px }
. warehouse - transfer - pallet - skus > div { display : flex ; flex - wrap : wrap ; gap : 6 px ; min - width : 0 }
. warehouse - transfer - pallet - sku { display : inline - flex ; align - items : center ; gap : 8 px ; max - width : 100 % ; box - sizing : border - box ; padding : 4 px 8 px ; background : # f5f7ff ; border : 1 px solid # e5e9f8 ; border - radius : 5 px ; font - size : 12 px ; line - height : 1.6 }
. warehouse - transfer - pallet - sku strong { font - weight : 500 ; overflow - wrap : anywhere ; min - width : 0 ; color : var ( -- navy ) }
. warehouse - transfer - pallet - sku > span { flex - shrink : 0 ; padding : 0 7 px ; border : 1 px solid # e0e6ff ; border - radius : 4 px ; background : # eef1ff ; color : var ( -- primary ) ; font - weight : 600 ; white - space : nowrap }
. warehouse - service - page -- detail . warehouse - transfer - received - card . warehouse - transfer - receipt - heading { justify - content : flex - start ; flex - wrap : wrap ; gap : 10 px 16 px }
. warehouse - service - page -- detail . warehouse - transfer - receipt - heading h3 { flex : 0 0 auto ; margin : 0 ; padding : 0 ; border : 0 }
. warehouse - transfer - receipt - actions { display : flex ; flex - wrap : wrap ; gap : 8 px } . warehouse - transfer - receipt - actions : deep ( . el - button + . el - button ) { margin - left : 0 }
. warehouse - service - page -- detail > . warehouse - detail - header + . warehouse - card . warehouse - transfer - overview - grid { width : 100 % ; max - width : 1440 px ; box - sizing : border - box ; grid - template - columns : repeat ( 4 , minmax ( 0 , 1 fr ) ) ; column - gap : 18 px ; row - gap : 4 px ; padding : 8 px 14 px 10 px }
. warehouse - service - page -- detail > . warehouse - detail - header + . warehouse - card . warehouse - transfer - overview - grid > div { min - height : 24 px ; gap : 8 px ; padding : 1 px 0 }
. warehouse - service - page -- detail > . warehouse - detail - header + . warehouse - card . warehouse - transfer - overview - grid > div > span { flex : 0 0 84 px ; min - width : 0 }
< / style >
< / style >