@@ -8,7 +8,7 @@ import DataTable, { type DataTableColumn } from '@/components/common/DataTable.v
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 {
import {
createDiscard , createRelabel , createTransfer , getDiscard , getRelabel , getTransfer ,
createDiscard , createRelabel , createTransfer , getDiscard , getRelabel , getTransfer , resubmitRelabel ,
getTransferCartonLabel , submitTransfer , updateTransfer ,
getTransferCartonLabel , submitTransfer , updateTransfer ,
confirmRelabelCompletion , confirmTransferMeasurement , downloadRelabelCompletionPhoto , downloadTransferMeasurementPhoto , uploadRelabelShippingLabel ,
confirmRelabelCompletion , confirmTransferMeasurement , downloadRelabelCompletionPhoto , downloadTransferMeasurementPhoto , uploadRelabelShippingLabel ,
uploadRelabelFile ,
uploadRelabelFile ,
@@ -55,7 +55,7 @@ const canConfirmTransfer = computed(() => auth.hasPermission('merchant:overseas-
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 汇总' , '退件总数' , '是否换标' , '换标状态' , '尾程状态' , '创建时间' ] } ,
}
}
@@ -70,7 +70,7 @@ const listQueryModel = computed<QueryModel>({
} ) ,
} ) ,
} )
} )
const statusOptions = computed ( ( ) => props . service === 'labeling'
const statusOptions = computed ( ( ) => props . service === 'labeling'
? [ { label : '待处理' , value : 'PENDING_PROCESSING' } , { label : '处理中' , value : 'PROCESSING' } , { label : '换标完成' , value : 'COMPLETED' } ]
? [ { 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'
@@ -111,13 +111,14 @@ interface TransferPallet { id?: string | number; sequenceNo?: number; palletNo?:
interface TransferMeasurementPhoto { fileId : string | number ; fileName ? : string ; contentType ? : string ; sizeBytes ? : number ; uploadedAt ? : string }
interface TransferMeasurementPhoto { fileId : string | number ; fileName ? : string ; contentType ? : string ; sizeBytes ? : number ; uploadedAt ? : string }
interface ServiceDetail extends Record < string , unknown > {
interface ServiceDetail extends Record < string , unknown > {
transferNo ? : string ; status ? : string ; operatingWarehouseId ? : string ; fbaWarehouseCode ? : string
transferNo ? : string ; status ? : string ; operatingWarehouseId ? : string ; fbaWarehouseCode ? : string
destinationType ? : 'FBA' | 'PRIVATE' ; destinationWarehouseCode ? : string ; destinationWarehouseName ? : string
destinationType ? : 'NONE' | ' FBA' | 'PRIVATE' ; destinationWarehouseCode ? : string ; destinationWarehouseName ? : string
privateDestinationAddress ? : string ; privateDestinationPostalCode ? : string ; privateDestinationContactName ? : string ; privateDestinationContactPhone ? : string
privateDestinationAddress ? : string ; privateDestinationPostalCode ? : string ; privateDestinationContactName ? : string ; privateDestinationContactPhone ? : string
deliveryAddress ? : string ; pickupContactName ? : string ; pickupContactPhone ? : string
deliveryAddress ? : string ; pickupContactName ? : string ; pickupContactPhone ? : string
packageCount ? : number ; volumeCbm ? : number ; deliveryMethod ? : string ; estimatedArrivalAt ? : string
packageCount ? : number ; volumeCbm ? : number ; deliveryMethod ? : string ; estimatedArrivalAt ? : string
estimatedShipAt ? : string ; actualShipAt ? : string ; fbaShipmentNo ? : string ; trackingNo ? : string
estimatedShipAt ? : string ; actualShipAt ? : string ; fbaShipmentNo ? : string ; trackingNo ? : string
remark ? : string ; needRelabel ? : boolean ; labelDeadline ? : string ; relabelContactName ? : string
remark ? : string ; needRelabel ? : boolean ; labelDeadline ? : string ; relabelContactName ? : string
relabelContactPhone ? : string ; relabelDeliveryMethod ? : string
relabelContactPhone ? : string ; relabelDeliveryMethod ? : string ; relabelNo ? : string
relabelStatus ? : string ; relabelFulfillmentStatus ? : string ; createdAt ? : string
items ? : Array < { skuCode ? : string ; productNameZh ? : string ; packageCount ? : number ; shippingMark ? : string } >
items ? : Array < { skuCode ? : string ; productNameZh ? : string ; packageCount ? : number ; shippingMark ? : string } >
relabelBindings ? : Array < { operationType ? : string ; customerCartonMark ? : string ; originalSku ? : string ; newSku ? : string ; quantity ? : number ; fbaCartonMark ? : string ; destinationWarehouseCode ? : string ; remark ? : string } >
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
@@ -158,7 +159,8 @@ const listQueryFields = computed<QueryField[]>(() => {
} )
} )
const listColumns = computed < DataTableColumn [ ] > ( ( ) => config . value . columns . map ( ( label , index ) => index === 0
const listColumns = computed < DataTableColumn [ ] > ( ( ) => config . value . columns . map ( ( label , index ) => index === 0
? { key : 'actions' , label , slotName : 'actions' , width : props . service === 'transfer' ? 210 : 160 , fixed : 'left' }
? { key : 'actions' , label , slotName : 'actions' , width : props . service === 'transfer' ? 210 : 160 , fixed : 'left' }
: { key : String ( index - 1 ) , prop : String ( index - 1 ) , label , minWidth : index === 1 ? 190 : 130 } ) )
: { key : String ( index - 1 ) , prop : String ( index - 1 ) , label , minWidth : index === 1 || label === '换标单号' ? 190 : 130 ,
slotName : props . service === 'transfer' && label === '换标状态' ? 'transferRelabelStatus' : props . service === 'transfer' && label === '状态' ? 'transferStatus' : undefined } ) )
const discardCandidates = ref < import ( ' .. / api / warehouseServices ' ) .DiscardCandidate [ ] > ( [ ] )
const discardCandidates = ref < import ( ' .. / api / warehouseServices ' ) .DiscardCandidate [ ] > ( [ ] )
const selectedDiscardOrderId = ref ( '' )
const selectedDiscardOrderId = ref ( '' )
const discardCandidatesLoading = ref ( false )
const discardCandidatesLoading = ref ( false )
@@ -225,6 +227,13 @@ watch(() => form.relabel, (value) => {
if ( ! transferRelabelRows . value . length ) addTransferRelabelRow ( )
if ( ! transferRelabelRows . value . length ) addTransferRelabelRow ( )
}
}
} )
} )
watch ( ( ) => cargoRows . value . map ( ( row ) => row . sku ) , ( skus ) => {
if ( props . service !== 'transfer' || ! form . relabel ) return
transferRelabelRows . value . forEach ( ( row , index ) => {
const sourceSku = skus [ Math . min ( index , Math . max ( 0 , skus . length - 1 ) ) ] ? . trim ( ) || ''
if ( sourceSku ) row . sku = sourceSku
} )
} )
watch ( labelingMode , ( value ) => { if ( value !== 'carton' && ! skuRelabelRows . value . length ) addSkuRelabelRow ( ) } )
watch ( labelingMode , ( value ) => { if ( value !== 'carton' && ! skuRelabelRows . value . length ) addSkuRelabelRow ( ) } )
watch ( ( ) => form . destination , ( value ) => {
watch ( ( ) => form . destination , ( value ) => {
if ( props . service !== 'labeling' ) return
if ( props . service !== 'labeling' ) return
@@ -397,7 +406,10 @@ function printTransferDraftCartonMarks() {
printWindow . document . write ( ` <!doctype html><html><head><meta charset="utf-8"><title>仓储转运客户箱唛</title><style>@page{size:100mm 100mm;margin:8mm}body{margin:0;font-family:Arial,'Microsoft YaHei',sans-serif;color:#111}.label{height:84mm;display:flex;flex-direction:column;align-items:center;justify-content:center;page-break-after:always;border:1px solid #222;box-sizing:border-box;padding:8mm}.title{font-size:18px;font-weight:700}.mark{margin:11mm 0 6mm;font-size:26px;font-weight:700;letter-spacing:1px;word-break:break-all;text-align:center}.sku{font-size:17px;font-weight:650}.meta{font-size:12px;margin-top:4mm}</style></head><body> ${ sections } </body></html> ` )
printWindow . document . write ( ` <!doctype html><html><head><meta charset="utf-8"><title>仓储转运客户箱唛</title><style>@page{size:100mm 100mm;margin:8mm}body{margin:0;font-family:Arial,'Microsoft YaHei',sans-serif;color:#111}.label{height:84mm;display:flex;flex-direction:column;align-items:center;justify-content:center;page-break-after:always;border:1px solid #222;box-sizing:border-box;padding:8mm}.title{font-size:18px;font-weight:700}.mark{margin:11mm 0 6mm;font-size:26px;font-weight:700;letter-spacing:1px;word-break:break-all;text-align:center}.sku{font-size:17px;font-weight:650}.meta{font-size:12px;margin-top:4mm}</style></head><body> ${ sections } </body></html> ` )
printWindow . document . close ( ) ; printWindow . focus ( ) ; printWindow . print ( )
printWindow . document . close ( ) ; printWindow . focus ( ) ; printWindow . print ( )
}
}
function addTransferRelabelRow ( ) { transferRelabelRows . value . push ( createCargoRow ( ) ) }
function addTransferRelabelRow ( ) {
const source = cargoRows . value [ Math . min ( transferRelabelRows . value . length , Math . max ( 0 , cargoRows . value . length - 1 ) ) ]
transferRelabelRows . value . push ( { ... createCargoRow ( ) , sku : source ? . sku || '' } )
}
function clearTransferRelabelRows ( ) {
function clearTransferRelabelRows ( ) {
transferRelabelRows . value = [ ]
transferRelabelRows . value = [ ]
}
}
@@ -728,9 +740,12 @@ function warehouseDisplay(value?: string) {
}
}
function statusLabel ( value : string ) {
function statusLabel ( value : string ) {
if ( props . service === 'labeling' ) {
if ( props . service === 'labeling' ) {
if ( value === 'PROCESSING' ) return '处理中'
const relabelLabels : Record < string , string > = {
if ( value === 'CUSTOMER_CONFIRM_PENDING ') return '待客户确认'
WAIT _APPROVAL : '待审核' , PENDING _REVIEW : '待审核 ', PENDING _PROCESSING : '待仓库处理' , PROCESSING : '处理中' ,
return [ 'READY_FOR_OUTBOUND' , 'COMPLETED' ] . includes ( value ) ? '换标完成' : '待处理'
CUSTOMER _CONFIRM _PENDING : '待客户确认' , READY _FOR _OUTBOUND : '换标完成' , COMPLETED : '已完成' ,
REJECTED : '已驳回' , CANCELLED : '已取消' ,
}
return relabelLabels [ value ] || value || '—'
}
}
if ( props . service === 'transfer' ) {
if ( props . service === 'transfer' ) {
const transferLabels : Record < string , string > = {
const transferLabels : Record < string , string > = {
@@ -755,6 +770,55 @@ function statusLabel(value: string) {
}
}
return labels [ value ] || value || '—'
return labels [ value ] || value || '—'
}
}
type StatusTagType = 'primary' | 'success' | 'warning' | 'danger' | 'info'
function relabelStatusTagType ( status : string ) : StatusTagType {
if ( [ 'READY_FOR_OUTBOUND' , 'COMPLETED' ] . includes ( status ) ) return 'success'
if ( [ 'REJECTED' , 'CANCELLED' ] . includes ( status ) ) return 'danger'
if ( [ 'WAIT_APPROVAL' , 'PENDING_REVIEW' , 'PENDING_PROCESSING' , 'CUSTOMER_CONFIRM_PENDING' ] . includes ( status ) ) return 'warning'
return 'primary'
}
function relabelFulfillmentLabel ( value ? : string ) {
const labels : Record < string , string > = {
WAIT _APPROVAL : '待审核' , WAIT _PROCESSING : '待仓库处理' ,
WAIT _RELABEL : '待换标' , WAIT _PRE _DISPATCH : '待排车' , READY _FOR _DISPATCH : '待排车' ,
OUTBOUND _CREATED : '换标出库单已创建' , OUTBOUNDED : '已出库' , COMPLETED : '已完成' ,
}
return labels [ value || '' ] || value || '—'
}
function relabelFulfillmentTagType ( status ? : string ) : StatusTagType {
if ( [ 'OUTBOUNDED' , 'COMPLETED' ] . includes ( status || '' ) ) return 'success'
if ( [ 'WAIT_APPROVAL' , 'WAIT_PROCESSING' , 'WAIT_RELABEL' , 'WAIT_PRE_DISPATCH' , 'READY_FOR_DISPATCH' ] . includes ( status || '' ) ) return 'warning'
return 'primary'
}
function transferStatusTagType ( status : string ) : StatusTagType {
if ( [ 'RECEIVED' , 'COMPLETED' ] . includes ( status ) ) return 'success'
if ( [ 'REJECTED' , 'CANCELLED' ] . includes ( status ) ) return 'danger'
if ( [ 'DRAFT' ] . includes ( status ) ) return 'info'
if ( [ 'PENDING_REVIEW' , 'WAIT_CUSTOMER_CONFIRMATION' , 'WAIT_RELABEL' , 'WAIT_FBA_DESTINATION' , 'READY_FOR_DISPATCH' , 'WAIT_OUTBOUND' , 'PARTIALLY_RECEIVED' ] . includes ( status ) ) return 'warning'
return 'primary'
}
function transferRelabelStatus ( item : { needRelabel ? : boolean ; relabelNo ? : string ; relabelStatus ? : string ; relabelFulfillmentStatus ? : string } ) {
if ( ! item . needRelabel ) return { code : 'NOT_REQUIRED' , label : '无需换标' }
if ( ! item . relabelNo ) return { code : 'WAIT_RELABEL' , label : '待换标' }
const code = item . relabelStatus || 'PENDING_PROCESSING'
const labels : Record < string , string > = {
PENDING _REVIEW : '待审核' , PENDING _PROCESSING : '待处理' , PROCESSING : '换标中' ,
CUSTOMER _CONFIRM _PENDING : '待客户确认' , READY _FOR _OUTBOUND : '已换标' , COMPLETED : '已换标' ,
REJECTED : '已驳回' , CANCELLED : '已取消' ,
}
const fulfillmentLabels : Record < string , string > = {
WAIT _RELABEL : '待换标' , WAIT _CUSTOMER _CONFIRMATION : '待客户确认' , READY _FOR _DISPATCH : '已换标' ,
OUTBOUND _CREATED : '已换标' , OUTBOUNDED : '已换标' , NOT _REQUIRED : '无需换标' ,
}
return { code , label : labels [ code ] || fulfillmentLabels [ item . relabelFulfillmentStatus || '' ] || statusLabel ( code ) }
}
function transferRelabelStatusTagType ( status : string ) : StatusTagType {
if ( [ 'READY_FOR_OUTBOUND' , 'COMPLETED' ] . includes ( status ) ) return 'success'
if ( [ 'REJECTED' , 'CANCELLED' ] . includes ( status ) ) return 'danger'
if ( status === 'NOT_REQUIRED' ) return 'info'
if ( [ 'WAIT_RELABEL' , 'PENDING_REVIEW' , 'CUSTOMER_CONFIRM_PENDING' ] . includes ( status ) ) return 'warning'
return 'primary'
}
function deliveryMethodLabel ( value : string ) {
function deliveryMethodLabel ( value : string ) {
const labels : Record < string , string > = {
const labels : Record < string , string > = {
TRUCK : '卡车派送' , EXPRESS : '快递派送' , TRUCK _PICKUP : '卡车自提' , EXPRESS _PICKUP : '快递自提' ,
TRUCK : '卡车派送' , EXPRESS : '快递派送' , TRUCK _PICKUP : '卡车自提' , EXPRESS _PICKUP : '快递自提' ,
@@ -764,6 +828,11 @@ function deliveryMethodLabel(value: string) {
}
}
return labels [ value ] || value || '—'
return labels [ value ] || value || '—'
}
}
function destinationTypeLabel ( value ? : string , fbaWarehouseCode ? : string ) {
if ( value === 'PRIVATE' ) return '私人仓'
if ( value === 'FBA' || fbaWarehouseCode ) return 'FBA 仓'
return '暂未确定'
}
function formatDateTime ( value ? : string | null ) {
function formatDateTime ( value ? : string | null ) {
return value ? value . replace ( 'T' , ' ' ) . slice ( 0 , 19 ) : '—'
return value ? value . replace ( 'T' , ' ' ) . slice ( 0 , 19 ) : '—'
}
}
@@ -786,8 +855,9 @@ async function load() {
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 ) => {
submittedRows . value = result . records . map ( ( item ) => {
const operationWarehouse = warehouseDisplay ( item . operatingWarehouseId )
const operationWarehouse = warehouseDisplay ( item . operatingWarehouseId )
const relabelStatus = transferRelabelStatus ( item )
return props . service === 'transfer'
return props . service === 'transfer'
? { ... row ( [ item . transferNo , item . packageCount , operationWarehouse , item . fbaWarehouseCode , deliveryMethodLabel ( item . deliveryMethod ) , item . needRelabel ? '是 · 待绑定换标单 ' : '否' , statusLabel ( item . status ) , formatDateTime ( item . createdAt ) ] ) , _ _status : item . status }
? { ... row ( [ item . transferNo , item . packageCount , operationWarehouse , item . fbaWarehouseCode , deliveryMethodLabel ( item . deliveryMethod ) , item . needRelabel ? '是' : '否' , item . relabelNo , relabelStatus . label , statusLabel ( item . status ) , formatDateTime ( item . createdAt ) ] ) , _ _status : item . status , _ _statusLabel : statusLabel ( item . status ) , _ _relabelStatus : relabelStatus . code , _ _relabelStatusLabel : relabelStatus . label }
: row ( [ item . transferNo , '—' , item . fbaWarehouseCode , '—' , item . packageCount , '是' , item . relabelNo ? '已生成' : '待生成' , statusLabel ( item . status ) , formatDateTime ( item . createdAt ) ] )
: row ( [ item . transferNo , '—' , item . fbaWarehouseCode , '—' , item . packageCount , '是' , item . relabelNo ? '已生成' : '待生成' , statusLabel ( item . status ) , formatDateTime ( item . createdAt ) ] )
} )
} )
}
}
@@ -964,8 +1034,13 @@ async function submit(action: 'draft' | 'submit' = 'submit') {
const submitted = await submitTransfer ( transferNo )
const submitted = await submitTransfer ( transferNo )
if ( form . relabel ) {
if ( form . relabel ) {
const relabelNo = String ( submitted . relabelNo || created . relabelNo || '' )
const relabelNo = String ( submitted . relabelNo || created . relabelNo || '' )
if ( ! relabelNo ) {
ElMessage . success ( '转运单已提交审核;PDA 入库确认后将自动生成关联换标单' )
await ElMessageBox . alert ( ` 转运单 ${ transferNo } 已提交。关联换标单会在 PDA 完成入库、客户确认实测库存后自动生成;届时请进入关联换标单详情上传贴标总文件。 ` , '转运单已提交' , { confirmButtonText : '知道了' } )
await openDetail ( { '0' : transferNo } )
return
}
try {
try {
if ( ! relabelNo ) throw new Error ( '转运单已提交,但未返回关联换标单号' )
if ( cartonTotalFile . value ) await uploadRelabelFile ( relabelNo , 'CARTON_TOTAL' , cartonTotalFile . value )
if ( cartonTotalFile . value ) await uploadRelabelFile ( relabelNo , 'CARTON_TOTAL' , cartonTotalFile . value )
if ( skuTotalFile . value ) await uploadRelabelFile ( relabelNo , 'SKU_TOTAL' , skuTotalFile . value )
if ( skuTotalFile . value ) await uploadRelabelFile ( relabelNo , 'SKU_TOTAL' , skuTotalFile . value )
} catch ( error ) {
} catch ( error ) {
@@ -1003,8 +1078,8 @@ async function openDetail(value: Record<string, string>) {
} catch ( error ) { selectedDetail . value = null ; selectedRelabelDetail . value = null ; detailError . value = error instanceof Error ? error . message : '详情加载失败' ; mode . value = 'detail' ; ElMessage . error ( detailError . value ) }
} catch ( error ) { selectedDetail . value = null ; selectedRelabelDetail . value = null ; detailError . value = error instanceof Error ? error . message : '详情加载失败' ; mode . value = 'detail' ; ElMessage . error ( detailError . value ) }
}
}
async function loadDetailPage ( ) {
async function loadDetailPage ( ) {
await loadWarehouses ( )
await openDetail ( { '0' : detailNumber . value } )
await openDetail ( { '0' : detailNumber . value } )
await loadWarehouses ( )
}
}
async function confirmTransferMeasurements ( ) {
async function confirmTransferMeasurements ( ) {
const transferNo = String ( selectedDetail . value ? . transferNo || selectedDetail . value ? . [ '0' ] || '' )
const transferNo = String ( selectedDetail . value ? . transferNo || selectedDetail . value ? . [ '0' ] || '' )
@@ -1145,6 +1220,34 @@ async function selectTransferWarehouse() {
ElMessage . error ( error instanceof Error ? error . message : '中转仓联系信息加载失败' )
ElMessage . error ( error instanceof Error ? error . message : '中转仓联系信息加载失败' )
} finally { operationWarehouseDetailLoading . value = false }
} finally { operationWarehouseDetailLoading . value = false }
}
}
async function confirmSelectedRelabel ( ) {
const relabelNo = selectedRelabelDetail . value ? . application . relabelNo
if ( ! relabelNo ) return
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 ( ) {
const application = selectedRelabelDetail . value ? . application
if ( ! application || application . status !== 'REJECTED' ) return
try {
const { value } = await ElMessageBox . prompt ( '可先在下方替换贴标文件,再补充本次修改说明。' , '修改后重新提交审核' , {
inputType : 'textarea' , inputValue : selectedRelabelDetail . value ? . remark || '' ,
inputPlaceholder : '修改说明(选填)' , confirmButtonText : '重新提交' , cancelButtonText : '取消' ,
} )
selectedRelabelDetail . value = await resubmitRelabel ( application . relabelNo , {
labelDeadline : application . labelDeadline || '' , contactName : application . contactName || '' ,
contactPhone : application . contactPhone || '' , remark : value ? . trim ( ) || undefined ,
idempotencyKey : crypto . randomUUID ( ) ,
} )
ElMessage . success ( '贴标单已重新提交审核' )
} catch ( error ) {
if ( error !== 'cancel' && error !== 'close' ) ElMessage . error ( error instanceof Error ? error . message : '重新提交失败' )
}
}
const transferInventoryOptions = computed ( ( ) => {
const transferInventoryOptions = computed ( ( ) => {
const grouped = new Map < string , { value : string ; label : string ; packages : number } > ( )
const grouped = new Map < string , { value : string ; label : string ; packages : number } > ( )
for ( const item of transferInventoryCandidates . value ) {
for ( const item of transferInventoryCandidates . value ) {
@@ -1264,6 +1367,8 @@ function selectRelabelInventory(inboundOrderId: string) {
< DataTable v -model :page = "page" v -model :page-size = "pageSize" :data = "submittedRows" :columns = "listColumns" :loading = "loading" :total = "total" : page -sizes = " [ 15 , 30 , 50 , 100 ] " :row-actions = "listRowActions" :empty-text = "config.emptyText" >
< DataTable v -model :page = "page" v -model :page-size = "pageSize" :data = "submittedRows" :columns = "listColumns" :loading = "loading" :total = "total" : page -sizes = " [ 15 , 30 , 50 , 100 ] " :row-actions = "listRowActions" :empty-text = "config.emptyText" >
< template # toolbar -actions > < el-button v-if = "canCreate" type="primary" @click="openCreate"><Plus :size="16" / > { { props . service === 'relabel' ? '新建退货换标单' : props . service === 'transfer' ? '新增订单' : ` 新增 ${ config . title } ` } } < / el-button > < / template >
< template # toolbar -actions > < el-button v-if = "canCreate" type="primary" @click="openCreate"><Plus :size="16" / > { { props . service === 'relabel' ? '新建退货换标单' : props . service === 'transfer' ? '新增订单' : ` 新增 ${ config . title } ` } } < / el-button > < / template >
< template v-if = "props.service === 'transfer' && submittedRows.some((item) => item.__status === 'WAIT_CUSTOMER_CONFIRMATION')" #before-table><div class="warehouse-list-notice" > 部分入库完成订单已有 PDA 实测数据 , 请在对应行确认测量数据 。 < / div > < / template >
< template v-if = "props.service === 'transfer' && submittedRows.some((item) => item.__status === 'WAIT_CUSTOMER_CONFIRMATION')" #before-table><div class="warehouse-list-notice" > 部分入库完成订单已有 PDA 实测数据 , 请在对应行确认测量数据 。 < / div > < / template >
< template # transferRelabelStatus = "{ row }" > < el-tag :type = "transferRelabelStatusTagType(row.__relabelStatus)" effect = "light" > { { row . _ _relabelStatusLabel } } < / el-tag > < / template >
< template # transferStatus = "{ row }" > < el-tag :type = "transferStatusTagType(row.__status)" effect = "light" > { { row . _ _statusLabel } } < / el-tag > < / template >
< / DataTable >
< / DataTable >
< / div >
< / div >
< / template >
< / template >
@@ -1304,7 +1409,7 @@ function selectRelabelInventory(inboundOrderId: string) {
< 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 === 'transfer' || props.service === 'relabel'"><span><i>*</i>{{ props.service === 'transfer' ? '预估体积(CBM) ' : '预估总体积(CBM) ' }}</span><el-input-number v-model="form.volumeCbm" :min="0" :precision="4" :step="0.0001" controls-position="right" placeholder="请输入预估总体积" / > < / label >
< label v-if = "props.service === 'transfer' || props.service === 'relabel'"><span><i>*</i>{{ props.service === 'transfer' ? '预计入仓时间' : '预计到达时间' }}</span><el-date-picker v-model="form.estimatedArrivalAt" type="datetime" value-format="YYYY-MM-DDTHH:mm:ss" :placeholder="props.service === 'transfer' ? '请选择预计入仓时间' : '请选择预计到达时间'" / > < / label >
< label v-if = "props.service === 'transfer' || 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><i>*</i>目的去向</span><el-radio-group v-model="transferDestinationType" @change="selectTransferDestinationType"><el-radio-button value="NONE">暂未确定</el-radio-button><el-radio-button value="FBA">FBA 仓</el-radio-button><el-radio-button value="PRIVATE" > 私人仓 < / el -radio -button > < / el-radio-group > < / div >
< div v-if = "props.service === 'transfer'" class="warehouse-choice-field warehouse-span-2"><span><i>*</i>目的去向</span><el-radio-group v-model="transferDestinationType" @change="selectTransferDestinationType"><el-radio-button value="NONE">暂未确定</el-radio-button><el-radio-button value="FBA">FBA 仓</el-radio-button><el-radio-button value="PRIVATE" > 私人仓 < / el -radio -button > < / el-radio-group > < / div >
< label v-if = "props.service === 'transfer' && transferDestinationType === 'FBA'"><span><i>*</i>FBA 目的仓</span><el-select v-model="form.destination" :loading="fbaWarehousesLoading" :disabled="Boolean(fbaWarehousesError)" filterable placeholder="请选择 FBA 仓"><el-option v-for="warehouse in fbaWarehouses" :key="warehouse.id" :label="warehouseLabel(warehouse)" :value="warehouse.warehouseCode" / > < / el-select > < / label >
< label v-if = "props.service === 'transfer' && transferDestinationType === 'FBA'"><span><i>*</i>FBA 目的仓</span><el-select v-model="form.destination" :loading="fbaWarehousesLoading" :disabled="Boolean(fbaWarehousesError)" filterable placeholder="请选择 FBA 仓"><el-option v-for="warehouse in fbaWarehouses" :key="warehouse.id" :label="warehouseLabel(warehouse)" :value="warehouse.warehouseCode" / > < / el-select > < / label >
< label v-if = "props.service === 'transfer' && transferDestinationType === 'FBA'"><span>FBA 货件编号</span><el-input v-model="form.fbaShipmentNo" placeholder="选填,可后续补充" / > < / label >
< label v-if = "props.service === 'transfer' && transferDestinationType === 'FBA'"><span>FBA 货件编号</span><el-input v-model="form.fbaShipmentNo" placeholder="选填,可后续补充" / > < / label >
@@ -1328,9 +1433,9 @@ function selectRelabelInventory(inboundOrderId: string) {
< section v-if = "canManageRelabelFiles" class="warehouse-file-group warehouse-file-group--primary" >
< section v-if = "canManageRelabelFiles" class="warehouse-file-group warehouse-file-group--primary" >
< div class = "warehouse-file-group__heading" > < div > < strong > < span > 1 < / span > 贴标总文件 < / strong > < / div > < em > { { requiresBothTotalFiles ? '必传 · 两份文件' : '必传 · 二选一' } } < / em > < / div >
< div class = "warehouse-file-group__heading" > < div > < strong > < span > 1 < / span > 贴标总文件 < / strong > < / div > < em > { { requiresBothTotalFiles ? '必传 · 两份文件' : '必传 · 二选一' } } < / em > < / div >
< div class = "warehouse-file-options" >
< div class = "warehouse-file-options" >
< div class = "warehouse-file-option" : class = "{ 'is-selected': Boolean(cartonTotalFile) }" > < div class = "warehouse-file-option__name" > < strong > 箱唛总文件 < / strong > < / div > < el-button : type = "cartonTotalFile ? 'primary' : 'default'" plain @click ="chooseCreateTotalFile('CARTON_TOTAL')" > < Upload :size = "15" / > { { cartonTotalFile ? '重新上传' : '选择文件' } } < / el-button > < div class = "warehouse-file-option__status" : class = "{ 'is-empty': !cartonTotalFile }" > < span > { { cartonTotalFile ? . name || '尚未选择文件' } } < / span > < button v-if = "cartonTotalFile" type="button" class="warehouse-file-clear" title="清除箱唛总文件" aria-label="清除箱唛总文件" :disabled="saving" @click="clearCreateTotalFile('CARTON_TOTAL')"><X :size="14" / > < / button > < / div > < / div >
< div class = "warehouse-file-option" : class = "{ 'is-selected': Boolean(cartonTotalFile) }" > < div class = "warehouse-file-option__name" > < strong > 箱唛总文件 < / strong > < / div > < el-button native -type = " button " : type = "cartonTotalFile ? 'primary' : 'default'" plain @click ="chooseCreateTotalFile('CARTON_TOTAL')" > < Upload :size = "15" / > { { cartonTotalFile ? '重新上传' : '选择文件' } } < / el-button > < div class = "warehouse-file-option__status" : class = "{ 'is-empty': !cartonTotalFile }" > < span > { { cartonTotalFile ? . name || '尚未选择文件' } } < / span > < button v-if = "cartonTotalFile" type="button" class="warehouse-file-clear" title="清除箱唛总文件" aria-label="清除箱唛总文件" :disabled="saving" @click="clearCreateTotalFile('CARTON_TOTAL')"><X :size="14" / > < / button > < / div > < / div >
< div class = "warehouse-file-choice-or" : class = "{ 'is-both': requiresBothTotalFiles }" > { { requiresBothTotalFiles ? '和' : '或' } } < / div >
< div class = "warehouse-file-choice-or" : class = "{ 'is-both': requiresBothTotalFiles }" > { { requiresBothTotalFiles ? '和' : '或' } } < / div >
< div class = "warehouse-file-option" : class = "{ 'is-selected': Boolean(skuTotalFile) }" > < div class = "warehouse-file-option__name" > < strong > SKU 总文件 < / strong > < / div > < el-button : type = "skuTotalFile ? 'primary' : 'default'" plain @click ="chooseCreateTotalFile('SKU_TOTAL')" > < Upload :size = "15" / > { { skuTotalFile ? '重新上传' : '选择文件' } } < / el-button > < div class = "warehouse-file-option__status" : class = "{ 'is-empty': !skuTotalFile }" > < span > { { skuTotalFile ? . name || '尚未选择文件' } } < / span > < button v-if = "skuTotalFile" type="button" class="warehouse-file-clear" title="清除 SKU 总文件" aria-label="清除 SKU 总文件" :disabled="saving" @click="clearCreateTotalFile('SKU_TOTAL')"><X :size="14" / > < / button > < / div > < / div >
< div class = "warehouse-file-option" : class = "{ 'is-selected': Boolean(skuTotalFile) }" > < div class = "warehouse-file-option__name" > < strong > SKU 总文件 < / strong > < / div > < el-button native -type = " button " : type = "skuTotalFile ? 'primary' : 'default'" plain @click ="chooseCreateTotalFile('SKU_TOTAL')" > < Upload :size = "15" / > { { skuTotalFile ? '重新上传' : '选择文件' } } < / el-button > < div class = "warehouse-file-option__status" : class = "{ 'is-empty': !skuTotalFile }" > < span > { { skuTotalFile ? . name || '尚未选择文件' } } < / span > < button v-if = "skuTotalFile" type="button" class="warehouse-file-clear" title="清除 SKU 总文件" aria-label="清除 SKU 总文件" :disabled="saving" @click="clearCreateTotalFile('SKU_TOTAL')"><X :size="14" / > < / button > < / div > < / div >
< / div >
< / div >
< / section >
< / section >
< section v-if = "!isReturnRelabelSource" class="warehouse-file-group" >
< section v-if = "!isReturnRelabelSource" class="warehouse-file-group" >
@@ -1346,7 +1451,7 @@ function selectRelabelInventory(inboundOrderId: string) {
< div v-if = "isReturnRelabelSource" class="warehouse-table-scroll" >
< div v-if = "isReturnRelabelSource" class="warehouse-table-scroll" >
< table class = "warehouse-edit-table warehouse-return-relabel-table" >
< table class = "warehouse-edit-table warehouse-return-relabel-table" >
< thead > < tr > < th > 选择 < / th > < th > SKU < / th > < th > 中文品名 < / th > < th > 库存总数 < / th > < th > 本次贴标数量 < / th > < / tr > < / thead >
< thead > < tr > < th > 选择 < / th > < th > SKU < / th > < th > 中文品名 < / th > < th > 库存总数 < / th > < th > 本次贴标数量 < / th > < / tr > < / thead >
< tbody > < tr v-for = "row in returnRelabelSkuRows" :key="row.skuCode"><td><el-checkbox v- model="row.selected" /></td><td><strong>{{ row.skuCode }}</strong></td><td>{{ row.productNameZh || '—' }}</td><td><strong>{{ row.availableQuantity }}</strong></td><td><el-input-number v-model="row.quantity" :disabled="!row.selected" :min="1" :max="row.availableQuantity" :precision="0" controls-position="right" placeholder="请输入贴标数量" /></td></tr><tr v-if="returnRelabelSkuRows.length === 0"><td colspan="5"><div class="warehouse-empty" > 该退货单暂无可申请贴标的 SKU 库存 < / div > < / td > < / tr > < / tbody >
< tbody > < tr v-for = "row in returnRelabelSkuRows" :key="row.skuCode"><td><el-checkbox : model-value ="row.selected" @change="row.selected = Boolean($event)" /></td><td><strong>{{ row.skuCode }}</strong></td><td>{{ row.productNameZh || '—' }}</td><td><strong>{{ row.availableQuantity }}</strong></td><td><el-input-number v-model="row.quantity" :disabled="!row.selected" :min="1" :max="row.availableQuantity" :precision="0" controls-position="right" placeholder="请输入贴标数量" /></td></tr><tr v-if="returnRelabelSkuRows.length === 0"><td colspan="5"><div class="warehouse-empty" > 该退货单暂无可申请贴标的 SKU 库存 < / div > < / td > < / tr > < / tbody >
< / table >
< / table >
< / div >
< / div >
< div v-if = "!isReturnRelabelSource" class="warehouse-labeling-mode" >
< div v-if = "!isReturnRelabelSource" class="warehouse-labeling-mode" >
@@ -1382,9 +1487,9 @@ function selectRelabelInventory(inboundOrderId: string) {
<section v-if=" canManageRelabelFiles " class=" warehouse - file - group warehouse - transfer - total - files ">
<section v-if=" canManageRelabelFiles " class=" warehouse - file - group warehouse - transfer - total - files ">
<div class=" warehouse - file - group _ _heading "><div><strong>贴标总文件</strong></div><em>{{ requiresBothTotalFiles ? '必传 · 两份文件' : '必传 · 二选一' }}</em></div>
<div class=" warehouse - file - group _ _heading "><div><strong>贴标总文件</strong></div><em>{{ requiresBothTotalFiles ? '必传 · 两份文件' : '必传 · 二选一' }}</em></div>
<div class=" warehouse - file - options ">
<div class=" warehouse - file - options ">
<div class=" warehouse - file - option " :class=" { 'is-selected' : Boolean ( cartonTotalFile ) } "><div class=" warehouse - file - option _ _name "><strong>箱唛总文件</strong></div><el-button :type=" cartonTotalFile ? 'primary' : 'default' " plain @click=" chooseCreateTotalFile ( 'CARTON_TOTAL' ) "><Upload :size=" 15 " />{{ cartonTotalFile ? '重新上传' : '选择文件' }}</el-button><div class=" warehouse - file - option _ _status " :class=" { 'is-empty' : ! cartonTotalFile } "><span>{{ cartonTotalFile?.name || '尚未选择文件' }}</span><button v-if=" cartonTotalFile " type=" button " class=" warehouse - file - clear " title=" 清除箱唛总文件 " aria-label=" 清除箱唛总文件 " :disabled=" saving " @click=" clearCreateTotalFile ( 'CARTON_TOTAL' ) "><X :size=" 14 " /></button></div></div>
<div class=" warehouse - file - option " :class=" { 'is-selected' : Boolean ( cartonTotalFile ) } "><div class=" warehouse - file - option _ _name "><strong>箱唛总文件</strong></div><el-button native-type=" button " :type="cartonTotalFile ? 'primary' : 'default' " plain @click=" chooseCreateTotalFile ( 'CARTON_TOTAL' ) "><Upload :size=" 15 " />{{ cartonTotalFile ? '重新上传' : '选择文件' }}</el-button><div class=" warehouse - file - option _ _status " :class=" { 'is-empty' : ! cartonTotalFile } "><span>{{ cartonTotalFile?.name || '尚未选择文件' }}</span><button v-if=" cartonTotalFile " type=" button " class=" warehouse - file - clear " title=" 清除箱唛总文件 " aria-label=" 清除箱唛总文件 " :disabled=" saving " @click=" clearCreateTotalFile ( 'CARTON_TOTAL' ) "><X :size=" 14 " /></button></div></div>
<div class=" warehouse - file - choice - or " :class=" { 'is-both' : requiresBothTotalFiles } ">{{ requiresBothTotalFiles ? '和' : '或' }}</div>
<div class=" warehouse - file - choice - or " :class=" { 'is-both' : requiresBothTotalFiles } ">{{ requiresBothTotalFiles ? '和' : '或' }}</div>
<div class=" warehouse - file - option " :class=" { 'is-selected' : Boolean ( skuTotalFile ) } "><div class=" warehouse - file - option _ _name "><strong>SKU 总文件</strong></div><el-button :type=" skuTotalFile ? 'primary' : 'default' " plain @click=" chooseCreateTotalFile ( 'SKU_TOTAL' ) "><Upload :size=" 15 " />{{ skuTotalFile ? '重新上传' : '选择文件' }}</el-button><div class=" warehouse - file - option _ _status " :class=" { 'is-empty' : ! skuTotalFile } "><span>{{ skuTotalFile?.name || '尚未选择文件' }}</span><button v-if=" skuTotalFile " type=" button " class=" warehouse - file - clear " title=" 清除 SKU 总文件 " aria-label=" 清除 SKU 总文件 " :disabled=" saving " @click=" clearCreateTotalFile ( 'SKU_TOTAL' ) "><X :size=" 14 " /></button></div></div>
<div class=" warehouse - file - option " :class=" { 'is-selected' : Boolean ( skuTotalFile ) } "><div class=" warehouse - file - option _ _name "><strong>SKU 总文件</strong></div><el-button native-type=" button " :type="skuTotalFile ? 'primary' : 'default' " plain @click=" chooseCreateTotalFile ( 'SKU_TOTAL' ) "><Upload :size=" 15 " />{{ skuTotalFile ? '重新上传' : '选择文件' }}</el-button><div class=" warehouse - file - option _ _status " :class=" { 'is-empty' : ! skuTotalFile } "><span>{{ skuTotalFile?.name || '尚未选择文件' }}</span><button v-if=" skuTotalFile " type=" button " class=" warehouse - file - clear " title=" 清除 SKU 总文件 " aria-label=" 清除 SKU 总文件 " :disabled=" saving " @click=" clearCreateTotalFile ( 'SKU_TOTAL' ) "><X :size=" 14 " /></button></div></div>
</div>
</div>
<input ref=" cartonTotalCreateInput " class=" warehouse - hidden - input " type=" file " accept=" application / pdf , . pdf " @change=" selectCartonTotalFile "><input ref=" skuTotalCreateInput " class=" warehouse - hidden - input " type=" file " accept=" application / pdf , . pdf " @change=" selectSkuTotalFile ">
<input ref=" cartonTotalCreateInput " class=" warehouse - hidden - input " type=" file " accept=" application / pdf , . pdf " @change=" selectCartonTotalFile "><input ref=" skuTotalCreateInput " class=" warehouse - hidden - input " type=" file " accept=" application / pdf , . pdf " @change=" selectSkuTotalFile ">
</section>
</section>
@@ -1407,10 +1512,17 @@ function selectRelabelInventory(inboundOrderId: string) {
<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=" canCancelDiscard && props . service === 'disposal' && selectedDetail ? . status === 'PENDING_REVIEW' " type=" danger " @click=" cancelSelectedDiscard ">取消申请</el-button><el-button v-if=" canCreate && props . service === 'transfer' && selectedDetail ? . status === 'DRAFT' " type=" primary " @click=" openEdit ( String ( selectedDetail ? . transferNo || selectedDetail ? . [ '0' ] || '' ) ) ">编辑</el-button><el-button v-if=" canConfirmTransfer && props . service === 'transfer' && selectedDetail ? . status === 'WAIT_CUSTOMER_CONFIRMATION' " type=" primary " @click=" confirmTransferMeasurements ">确认测量数据</el-button><el-tag effect=" light ">{{ statusLabel(props.service === 'labeling' ? selectedRelabelDetail?.application.status || '' : String(selectedDetail?.status || '')) }}</el-tag></div>
<div class=" warehouse - detail - actions "><el-button v-if=" canCreate && props . service === 'labeling' && selectedRelabelDetail ? . application . status === 'REJECTED' " type=" primary " @click=" resubmitRejectedRelabel ">修改后重新提交</el-button><el-button v-if=" canConfirmRelabel && props . service === 'labeling' && selectedRelabelDetail ? . application . status === 'CUSTOMER_CONFIRM_PENDING' && ! selectedRelabelDetail ? . application . customerConfirmedAt " type=" primary " @click=" confirmSelectedRelabel ">确认换标完成</el-button><el-button v-if="canCancelDiscard && props . service === 'disposal' && selectedDetail ? . status === 'PENDING_REVIEW' " type=" danger " @click=" cancelSelectedDiscard ">取消申请</el-button><el-button v-if=" canCreate && props . service === 'transfer' && selectedDetail ? . status === 'DRAFT' " type=" primary " @click=" openEdit ( String ( selectedDetail ? . transferNo || selectedDetail ? . [ '0' ] || '' ) ) ">编辑</el-button><el-button v-if=" canConfirmTransfer && props . service === 'transfer' && selectedDetail ? . status === 'WAIT_CUSTOMER_CONFIRMATION' " type=" primary " @click=" confirmTransferMeasurements ">确认测量数据</el-button><el-tag :type=" props . service === 'labeling' ? relabelStatusTagType ( selectedRelabelDetail ? . application . status || '' ) : undefined " effect="light ">{{ statusLabel(props.service === 'labeling' ? selectedRelabelDetail?.application.status || '' : String(selectedDetail?.status || '')) }}</el-tag></div>
</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>
<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?.application.itemCount ?? '—' }}</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 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 >
<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?.application.itemCount ?? '—' }}</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>{{ warehouseDisplay(selectedDetail?.destinationWarehouseCode || selectedDetail?.fbaWarehouseCode) }}</strong></div><div v-if=" selectedDetail ? . destinationType !== 'PRIVATE' "><span>FBA 货件号</span><strong>{{ selectedDetail?.fbaShipmentNo || '—' }}</strong></div><div v-if=" selectedDetail ? . destinationType === 'PRIVATE' " class=" warehouse - overview - wide "><span>私人仓地址</span><strong>{{ selectedDetail?.privateDestinationAddress || '—' }}</strong></div><div v-if=" selectedDetail ? . destinationType === 'PRIVATE' "><span>邮编</span><strong>{{ selectedDetail?.privateDestinationPostalCode || '—' }}</strong></div><div v-if=" selectedDetail ? . destinationType === 'PRIVATE' "><span>私人仓联系人</span><strong>{{ selectedDetail?.privateDestinationContactName || '—' }}</strong></div><div v-if=" selectedDetail ? . destinationType === 'PRIVATE' "><span>联系电话</span><strong>{{ selectedDetail?.privateDestinationContactPhone || '—' }}</strong></div><div><span>海外仓</span><strong>{{ warehouseDisplay(selectedDetail?.operatingWarehouseId) }}</strong></div><div><span>箱数</span><strong>{{ selectedDetail?.packageCount ?? '—' }}</strong></div><div><span>入仓方式</span><strong>{{ deliveryMethodLabel(selectedDetail?.deliveryMethod || '') }}</strong></div><div><span>期望发货时间</span><strong>{{ formatDateTime(selectedDetail?.estimatedShipAt) }}</strong></div><div><span>实际发货时间</span><strong>{{ formatDateTime(selectedDetail?.actualShipAt) }}</strong></div><div><span>是否换标</span><strong>{{ selectedDetail?.needRelabel ? '是' : '否' }}</strong></div><div><span>换标状态</span><el-tag :type=" transferRelabelStatusTagType ( transferRelabelStatus ( selectedDetail || { } ) . code ) " effect=" light ">{{ transferRelabelStatus(selectedDetail || {}).label }}</el-tag></div><div><span>换标单号</span><strong>{{ selectedDetail?.relabelNo || '—' }}</strong></div><div><span>创建时间</span><strong>{{ formatDateTime(selectedDetail?.createdAt) }}</strong></div><div><span>转运单号</span><strong>{{ selectedDetail?.transferNo || '—' }}</strong></div></div><div v-else class=" warehouse - detail - grid "><div v-for=" ( column , index ) in config . columns . slice ( 1 ) " :key=" column "><span>{{ column }}</span><strong>{{ selectedDetail?.[String(index)] || '—' }}</strong></div></div></div>
<div v-if=" props . service === 'transfer' " class=" warehouse - card warehouse - form - section warehouse - transfer - measurement - card ">
<div class=" warehouse - section - heading "><h3>PDA 打托实测数据</h3><span class=" warehouse - hint ">PDA 作业完成即显示入库完成,客户可确认仓库实测数据</span></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 v-else class=" warehouse - empty ">暂无 PDA 打托测量数据</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 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 - cargo - 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 "><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 - 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 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>
@@ -1419,12 +1531,11 @@ function selectRelabelInventory(inboundOrderId: string) {
<h3>换标箱级去向</h3>
<h3>换标箱级去向</h3>
<div class=" warehouse - table - scroll "><table><thead><tr><th>换标类型</th><th>客户箱唛号</th><th>旧 SKU</th><th>新 SKU</th><th>新 FBA 箱唛</th><th>实际 FBA 目的仓</th><th>数量</th><th>备注</th></tr></thead><tbody><tr v-for=" ( binding , index ) in selectedDetail ? . relabelBindings || [ ] " :key=" ` ${ binding . customerCartonMark } - ${ index } ` "><td>{{ binding.operationType === 'CARTON_AND_SKU' ? 'SKU 和箱唛都换' : binding.operationType === 'SKU_LABEL' ? '只换 SKU' : '只换箱唛' }}</td><td>{{ binding.customerCartonMark || '—' }}</td><td>{{ binding.originalSku || '—' }}</td><td>{{ binding.newSku || '—' }}</td><td>{{ binding.fbaCartonMark || '—' }}</td><td>{{ binding.operationType === 'SKU_LABEL' ? '沿用原箱去向' : warehouseDisplay(binding.destinationWarehouseCode) }}</td><td>{{ binding.quantity ?? '—' }}</td><td>{{ binding.remark || '—' }}</td></tr><tr v-if=" ! selectedDetail ? . relabelBindings ? . length "><td colspan=" 8 "><div class=" warehouse - empty ">暂无换标绑定明细</div></td></tr></tbody></table></div>
<div class=" warehouse - table - scroll "><table><thead><tr><th>换标类型</th><th>客户箱唛号</th><th>旧 SKU</th><th>新 SKU</th><th>新 FBA 箱唛</th><th>实际 FBA 目的仓</th><th>数量</th><th>备注</th></tr></thead><tbody><tr v-for=" ( binding , index ) in selectedDetail ? . relabelBindings || [ ] " :key=" ` ${ binding . customerCartonMark } - ${ index } ` "><td>{{ binding.operationType === 'CARTON_AND_SKU' ? 'SKU 和箱唛都换' : binding.operationType === 'SKU_LABEL' ? '只换 SKU' : '只换箱唛' }}</td><td>{{ binding.customerCartonMark || '—' }}</td><td>{{ binding.originalSku || '—' }}</td><td>{{ binding.newSku || '—' }}</td><td>{{ binding.fbaCartonMark || '—' }}</td><td>{{ binding.operationType === 'SKU_LABEL' ? '沿用原箱去向' : warehouseDisplay(binding.destinationWarehouseCode) }}</td><td>{{ binding.quantity ?? '—' }}</td><td>{{ binding.remark || '—' }}</td></tr><tr v-if=" ! selectedDetail ? . relabelBindings ? . length "><td colspan=" 8 "><div class=" warehouse - empty ">暂无换标绑定明细</div></td></tr></tbody></table></div>
</div>
</div>
<div v-if=" props . service === 'transfer' " class=" warehouse - card warehouse - form - section warehouse - transfer - times "><h3>发货时间</h3><div class=" warehouse - detail - grid "><div><span>预计发货时间</span><strong>{{ formatDateTime(selectedDetail?.estimatedShipAt) }}</strong></div><div><span>实际发货时间</span><strong>{{ formatDateTime(selectedDetail?.actualShipAt) }}</strong></div></div><p>实际发货时间由仓库出库或 PDA 完成后回填,客户不能手动修改。</p></div>
<div v-if=" props . service === 'labeling' && canManageRelabelFiles " class=" warehouse - card warehouse - form - section ">
<div v-if=" props . service === 'labeling' && canManageRelabelFiles " class=" warehouse - card warehouse - form - section ">
<div class=" warehouse - section - heading "><h3>换标完成与出库</h3></div>
<div class=" warehouse - section - heading "><h3>换标完成与出库</h3></div>
<div class=" warehouse - detail - grid warehouse - fulfillment - grid ">
<div class=" warehouse - detail - grid warehouse - fulfillment - grid ">
<div><span>派送方式</span><strong>{{ deliveryMethodLabel(selectedRelabelDetail?.application.deliveryMethod || '') || '无需出库' }}</strong></div>
<div><span>派送方式</span><strong>{{ deliveryMethodLabel(selectedRelabelDetail?.application.deliveryMethod || '') || '无需出库' }}</strong></div>
<div><span>履约状态</span><strong>{{ status Label(selectedRelabelDetail?.application.fulfillmentStatus || '' ) }}</stron g></div>
<div><span>履约状态</span><el-tag :type=" relabelFulfillmentTagType ( selectedRelabelDetail ? . application . fulfillmentStatus ) " effect=" light ">{{ relabelFulfillment Label(selectedRelabelDetail?.application.fulfillmentStatus) }}</el-ta g></div>
<div><span>客户确认时间</span><strong>{{ formatDateTime(selectedRelabelDetail?.application.customerConfirmedAt) }}</strong></div>
<div><span>客户确认时间</span><strong>{{ formatDateTime(selectedRelabelDetail?.application.customerConfirmedAt) }}</strong></div>
</div>
</div>
<div class=" warehouse - evidence "><span>仓库完工照片</span><div v-if=" selectedRelabelDetail ? . completionEvidence ? . photoFileIds ? . length " class=" warehouse - photo - grid "><div v-for=" ( fileId , index ) in selectedRelabelDetail . completionEvidence . photoFileIds " :key=" fileId " class=" warehouse - photo - item "><el-image v-if=" completionPhotoUrls [ fileId ] " :src=" completionPhotoUrls [ fileId ] " :preview-src-list=" completionPhotoPreviewUrls " :initial-index=" completionPhotoPreviewUrls . indexOf ( completionPhotoUrls [ fileId ] ) " fit=" cover " preview-teleported /><div v-else-if=" completionPhotoLoading [ fileId ] " class=" warehouse - photo - placeholder ">照片 {{ index + 1 }} 加载中…</div><button v-else class=" warehouse - photo - placeholder warehouse - photo - retry " type=" button " @click=" loadCompletionPhoto ( fileId ) ">{{ completionPhotoErrors[fileId] ? `照片 ${index + 1} 加载失败,点击重试` : `加载照片 ${index + 1}` }}</button></div></div><strong v-else>仓库尚未上传完工照片</strong></div>
<div class=" warehouse - evidence "><span>仓库完工照片</span><div v-if=" selectedRelabelDetail ? . completionEvidence ? . photoFileIds ? . length " class=" warehouse - photo - grid "><div v-for=" ( fileId , index ) in selectedRelabelDetail . completionEvidence . photoFileIds " :key=" fileId " class=" warehouse - photo - item "><el-image v-if=" completionPhotoUrls [ fileId ] " :src=" completionPhotoUrls [ fileId ] " :preview-src-list=" completionPhotoPreviewUrls " :initial-index=" completionPhotoPreviewUrls . indexOf ( completionPhotoUrls [ fileId ] ) " fit=" cover " preview-teleported /><div v-else-if=" completionPhotoLoading [ fileId ] " class=" warehouse - photo - placeholder ">照片 {{ index + 1 }} 加载中…</div><button v-else class=" warehouse - photo - placeholder warehouse - photo - retry " type=" button " @click=" loadCompletionPhoto ( fileId ) ">{{ completionPhotoErrors[fileId] ? `照片 ${index + 1} 加载失败,点击重试` : `加载照片 ${index + 1}` }}</button></div></div><strong v-else>仓库尚未上传完工照片</strong></div>
@@ -1438,12 +1549,6 @@ function selectRelabelInventory(inboundOrderId: string) {
<tr v-if=" ! selectedRelabelDetail ? . items ? . length "><td colspan=" 5 "><div class=" warehouse - empty ">暂无贴标明细</div></td></tr>
<tr v-if=" ! selectedRelabelDetail ? . items ? . length "><td colspan=" 5 "><div class=" warehouse - empty ">暂无贴标明细</div></td></tr>
</tbody></table></div>
</tbody></table></div>
</div>
</div>
<div v-if=" props . service === 'transfer' " class=" warehouse - card warehouse - form - section ">
<div class=" warehouse - section - heading "><h3>PDA 打托实测数据</h3><span class=" warehouse - hint ">PDA 作业完成即显示入库完成,客户可确认仓库实测数据</span></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 v-else class=" warehouse - empty ">暂无 PDA 打托测量数据</div>
<div class=" warehouse - evidence warehouse - measurement - evidence "><span>测量打托现场照片</span><div v-if=" selectedDetail ? . measurementPhotos ? . length " class=" warehouse - photo - grid "><div v-for=" ( photo , index ) in selectedDetail . measurementPhotos " :key=" photo . fileId " class=" warehouse - 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 v-if=" props . service === 'labeling' " class=" warehouse - card warehouse - form - section ">
<div v-if=" props . service === 'labeling' " class=" warehouse - card warehouse - form - section ">
<h3>换标任务明细</h3>
<h3>换标任务明细</h3>
<div class=" warehouse - table - scroll "><table class=" warehouse - relabel - detail - table "><thead><tr><th>换标模式</th><th>我方箱唛</th><th>新 FBA 箱唛</th><th>FBA 目的仓</th><th>任务箱数</th><th>商品 SKU</th><th>SKU 换标明细</th><th>FBA 货件编号</th><th>贴标要求</th><th>备注</th></tr></thead><tbody>
<div class=" warehouse - table - scroll "><table class=" warehouse - relabel - detail - table "><thead><tr><th>换标模式</th><th>我方箱唛</th><th>新 FBA 箱唛</th><th>FBA 目的仓</th><th>任务箱数</th><th>商品 SKU</th><th>SKU 换标明细</th><th>FBA 货件编号</th><th>贴标要求</th><th>备注</th></tr></thead><tbody>
@@ -1474,6 +1579,8 @@ function selectRelabelInventory(inboundOrderId: string) {
. warehouse - service - page -- return - relabel . warehouse - table - scroll : has ( > . warehouse - return - relabel - table ) ~ . warehouse - table - scroll { display : none }
. warehouse - service - page -- return - relabel . warehouse - table - scroll : has ( > . warehouse - return - relabel - table ) ~ . warehouse - table - scroll { display : none }
. 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 - item { width : 100 % ; height : 180 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 }
@@ -1482,6 +1589,11 @@ function selectRelabelInventory(inboundOrderId: string) {
@ media ( max - width : 1180 px ) { . warehouse - transfer - file - groups { grid - template - columns : minmax ( 0 , 1 fr ) } }
@ media ( max - width : 1180 px ) { . warehouse - transfer - file - groups { grid - template - columns : minmax ( 0 , 1 fr ) } }
. warehouse - transfer - form . pickup - address - field { grid - column : span 1 }
. warehouse - transfer - form . pickup - address - field { grid - column : span 1 }
. warehouse - transfer - main - card . warehouse - form - grid { grid - template - columns : repeat ( 2 , minmax ( 280 px , 640 px ) ) } . warehouse - transfer - cargo - card . warehouse - section - heading , . warehouse - transfer - cargo - card . warehouse - table - scroll { width : 100 % ; max - width : 1296 px }
. warehouse - transfer - main - card . warehouse - form - grid { grid - template - columns : repeat ( 2 , minmax ( 280 px , 640 px ) ) } . warehouse - transfer - cargo - card . warehouse - section - heading , . warehouse - transfer - cargo - card . warehouse - table - scroll { width : 100 % ; max - width : 1296 px }
. warehouse - service - page -- detail . warehouse - transfer - cargo - card . warehouse - section - heading , . warehouse - service - page -- detail . warehouse - transfer - cargo - card . warehouse - table - scroll { max - width : none }
. 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 - transfer - overview - grid > . warehouse - overview - wide { grid - column : span 2 }
@ 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 ; }
. warehouse - pagination - total { color : # 526176 ; font - size : 14 px ; }
. warehouse - pagination - total { color : # 526176 ; font - size : 14 px ; }