@@ -0,0 +1,207 @@
< script setup lang = "ts" >
import { computed , onMounted , reactive , ref , watch } from 'vue'
import { ElMessage , ElMessageBox } from 'element-plus'
import { ArrowLeft , Eye , Plus , Search , Trash2 } from '@lucide/vue'
import {
createDiscard , createRelabel , createTransfer , getDiscard , getRelabel , getTransfer ,
listDiscardCandidates , listDiscards , listLastlegOrders , listRelabels , listTransfers ,
} from '../api/warehouseServices'
type ServiceType = 'labeling' | 'transfer' | 'disposal' | 'relabel'
type PageMode = 'list' | 'create' | 'detail'
interface CargoRow { id : number ; sku : string ; name : string ; quantity : number ; availableQuantity ? : number ; sourceOrderId ? : string ; fnsku : string ; shipmentNo : string ; newSkuCode : string ; shippingMark : string }
interface ServiceConfig { title : string ; createTitle : string ; numberLabel : string ; emptyText : string ; columns : string [ ] }
const props = defineProps < { service : ServiceType } > ( )
const configs : Record < ServiceType , ServiceConfig > = {
labeling : { title : '贴标申请' , createTitle : '新增贴标申请' , numberLabel : '贴标申请单号' , emptyText : '暂无贴标申请' , columns : [ '贴标申请单号' , '客户单号' , '目的海外仓' , '贴标要求' , 'SKU 数' , '总箱数' , '状态' , '创建时间' , '操作' ] } ,
transfer : { title : '仓储转运单' , createTitle : '新增仓储转运单' , numberLabel : '转运单号' , emptyText : '暂无仓储转运订单' , columns : [ '转运单号' , '进仓编号' , '发货箱数' , '操作仓' , 'FBA 目的仓' , '派送方式' , '是否换标' , '状态' , '创建时间' , '操作' ] } ,
disposal : { title : '货物废弃' , createTitle : '新增货物废弃申请' , numberLabel : '废弃申请单号' , emptyText : '暂无货物废弃申请' , columns : [ '废弃申请单号' , '进仓编号' , '操作仓' , 'SKU 汇总' , '废弃数量' , '预估费用' , '状态' , '创建时间' , '操作' ] } ,
relabel : { title : '退货换标' , createTitle : '新建退货换标单' , numberLabel : '退货换标单号' , emptyText : '暂无退货换标订单' , columns : [ '退货换标单号' , '来源 FBA 仓' , '目的 FBA 仓' , 'SKU 汇总' , '退件总数' , '是否换标' , '换标状态' , '尾程状态' , '创建时间' , '操作' ] } ,
}
const config = computed ( ( ) => configs [ props . service ] )
const statusOptions = computed ( ( ) => props . service === 'labeling'
? [ { label : '待处理' , value : 'PENDING_PROCESSING' } , { label : '处理中' , value : 'PROCESSING' } , { label : '已完成' , value : 'COMPLETED' } , { label : '已拒绝' , value : 'REJECTED' } , { label : '已取消' , value : 'CANCELLED' } ]
: props . service === 'disposal'
? [ { label : '待审核' , value : 'PENDING_REVIEW' } , { label : '已通过' , value : 'APPROVED' } , { label : '已拒绝' , value : 'REJECTED' } ]
: [ { label : '待审核' , value : 'PENDING_REVIEW' } , { label : '已通过' , value : 'APPROVED' } , { label : '待分配' , value : 'PENDING_ASSIGNMENT' } , { label : '已分配' , value : 'ALLOCATED' } , { label : '已出库' , value : 'OUTBOUNDED' } , { label : '部分收货' , value : 'PARTIALLY_RECEIVED' } , { label : '已收货' , value : 'RECEIVED' } , { label : '已拒绝' , value : 'REJECTED' } ] )
const mode = ref < PageMode > ( 'list' )
const filters = reactive ( { number : '' , inboundNo : '' , warehouse : '' , destination : '' , status : '' , start : '' , end : '' } )
const form = reactive ( { customerNo : '' , inboundNo : '' , sourceWarehouse : '' , warehouse : '' , destination : '' , address : '' , contact : '' , phone : '' , deliveryMethod : '' , trackingNo : '' , reason : '' , requirement : '' , relabel : false , remark : '' , labelDeadline : '' , estimatedArrivalAt : '' , fbaShipmentNo : '' , volumeCbm : 0.01 } )
const cargoRows = ref < CargoRow [ ] > ( [ ] )
const submittedRows = ref < Record < string , string > [ ] > ( [ ] )
const selectedDetail = ref < Record < string , string > | null > ( null )
const discardCandidates = ref < import ( ' .. / api / warehouseServices ' ) .DiscardCandidate [ ] > ( [ ] )
const selectedDiscardOrderId = ref ( '' )
const discardCandidatesLoading = ref ( false )
const loading = ref ( false )
const saving = ref ( false )
let cargoSequence = 0
watch ( ( ) => props . service , ( ) => { mode . value = 'list' ; resetFilters ( ) ; resetForm ( ) ; void load ( ) } )
onMounted ( ( ) => { void load ( ) } )
function resetFilters ( ) { Object . assign ( filters , { number : '' , inboundNo : '' , warehouse : '' , destination : '' , status : '' , start : '' , end : '' } ) }
function resetForm ( ) {
Object . assign ( form , { customerNo : '' , inboundNo : '' , sourceWarehouse : '' , warehouse : '' , destination : '' , address : '' , contact : '' , phone : '' , deliveryMethod : '' , trackingNo : '' , reason : '' , requirement : '' , relabel : false , remark : '' , labelDeadline : '' , estimatedArrivalAt : '' , fbaShipmentNo : '' , volumeCbm : 0.01 } )
cargoRows . value = [ ]
discardCandidates . value = [ ]
selectedDiscardOrderId . value = ''
}
async function openCreate ( ) {
resetForm ( ) ; mode . value = 'create'
if ( props . service !== 'disposal' ) { addCargoRow ( ) ; return }
discardCandidatesLoading . value = true
try { discardCandidates . value = await listDiscardCandidates ( ) }
catch ( error ) { ElMessage . error ( error instanceof Error ? error . message : '可废弃入库单加载失败' ) }
finally { discardCandidatesLoading . value = false }
}
function addCargoRow ( ) { cargoRows . value . push ( { id : ++ cargoSequence , sku : '' , name : '' , quantity : 1 , fnsku : '' , shipmentNo : '' , newSkuCode : '' , shippingMark : '' } ) }
function removeCargoRow ( id : number ) { cargoRows . value = cargoRows . value . filter ( ( row ) => row . id !== id ) }
function selectDiscardInbound ( orderId : string ) {
const candidate = discardCandidates . value . find ( ( item ) => item . orderId === orderId )
if ( ! candidate ) { cargoRows . value = [ ] ; return }
form . inboundNo = candidate . inboundCode
form . warehouse = candidate . overseasWarehouseId
cargoRows . value = candidate . skus . map ( ( sku ) => ( {
id : ++ cargoSequence , sku : sku . skuCode , name : sku . productName , quantity : 0 ,
availableQuantity : sku . availablePackages , sourceOrderId : candidate . orderId ,
fnsku : '' , shipmentNo : '' , newSkuCode : '' , shippingMark : '' ,
} ) )
}
function dateTime ( value : string , end = false ) { return value ? ` ${ value } T ${ end ? '23:59:59' : '00:00:00' } ` : undefined }
function row ( values : Array < string | number | undefined | null > ) { return Object . fromEntries ( values . map ( ( value , index ) => [ String ( index ) , value == null || value === '' ? '-' : String ( value ) ] ) ) }
async function load ( ) {
loading . value = true
try {
if ( props . service === 'labeling' ) {
const result = await listRelabels ( { page : 1 , size : 20 , relabelNo : filters . number || undefined , overseasWarehouseId : filters . warehouse || undefined , status : filters . status || undefined , createdFrom : dateTime ( filters . start ) , createdTo : dateTime ( filters . end , true ) } )
submittedRows . value = result . records . map ( ( item ) => row ( [ item . relabelNo , item . customerOrderNo , item . overseasWarehouseId , '-' , item . itemCount , item . totalPackageCount , item . status , item . createdAt ] ) )
} else if ( props . service === 'disposal' ) {
const result = await listDiscards ( { page : 1 , size : 20 , 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 ) } )
submittedRows . value = result . records . map ( ( item ) => row ( [ item . discardNo , item . inboundCodes , item . overseasWarehouseName || item . overseasWarehouseId , '-' , item . totalPackages , ` ${ item . currency } ${ item . totalFee } ` , item . status , item . createdAt ] ) )
} else {
const result = await listTransfers ( { page : 1 , size : 20 , transferNo : filters . number || undefined , inboundCode : filters . inboundNo || undefined , operatingWarehouseId : filters . warehouse || undefined , fbaWarehouseCode : filters . destination || undefined , status : filters . status || undefined , needRelabel : props . service === 'relabel' , createdFrom : dateTime ( filters . start ) , createdTo : dateTime ( filters . end , true ) } )
submittedRows . value = result . records . map ( ( item ) => props . service === 'transfer'
? row ( [ item . transferNo , item . inboundCode , item . packageCount , item . operatingWarehouseId , item . fbaWarehouseCode , item . deliveryMethod , item . needRelabel ? '是' : '否' , item . status , item . createdAt ] )
: row ( [ item . transferNo , item . operatingWarehouseId , item . fbaWarehouseCode , '-' , item . packageCount , '是' , item . relabelNo ? '已生成' : '待生成' , item . status , item . createdAt ] ) )
}
} catch ( error ) { ElMessage . error ( error instanceof Error ? error . message : '列表加载失败' ) }
finally { loading . value = false }
}
function query ( ) { void load ( ) }
async function submit ( ) {
const primaryWarehouse = props . service === 'relabel' ? form . sourceWarehouse : form . warehouse
const invalidCargo = props . service === 'disposal'
? ! selectedDiscardOrderId . value || cargoRows . value . every ( ( item ) => item . quantity < 1 )
|| cargoRows . value . some ( ( item ) => item . quantity < 0 || item . quantity > Number ( item . availableQuantity || 0 ) )
: cargoRows . value . length === 0 || cargoRows . value . some ( ( item ) => ! item . sku || ! item . name || item . quantity < 1 )
if ( ( ! form . customerNo && ! form . inboundNo ) || ! primaryWarehouse || invalidCargo ) {
ElMessage . warning ( '请填写必填信息和完整的货物明细' ) ; return
}
if ( props . service === 'labeling' && ( ! form . labelDeadline || ! form . contact || ! form . phone || cargoRows . value . some ( ( item ) => ! item . shipmentNo || ! item . newSkuCode ) ) ) {
ElMessage . warning ( '请填写贴标截止时间、联系人以及货物的货件编号和新 SKU' ) ; return
}
if ( props . service === 'disposal' && ! form . reason ) {
ElMessage . warning ( '请选择废弃原因' ) ; return
}
if ( ( props . service === 'transfer' || props . service === 'relabel' ) && ( ! form . destination || ! form . address || ! form . deliveryMethod || ! form . estimatedArrivalAt || ! form . fbaShipmentNo || form . volumeCbm <= 0 ) ) {
ElMessage . warning ( '请填写完整的派送信息、体积、预计到达时间和 FBA 货件号' ) ; return
}
try { await ElMessageBox . confirm ( ` 确认提交 ${ config . value . title } 吗? ` , '提交确认' , { confirmButtonText : '提交' , cancelButtonText : '取消' } ) } catch { return }
saving . value = true
try {
const idempotencyKey = crypto . randomUUID ( )
if ( props . service === 'labeling' ) {
await createRelabel ( { customerOrderNo : form . customerNo , overseasWarehouseId : form . warehouse , labelDeadline : form . labelDeadline , contactName : form . contact , contactPhone : form . phone , remark : form . remark || undefined , idempotencyKey , items : cargoRows . value . map ( ( item ) => ( { skuCode : item . sku , productNameZh : item . name , totalPackageCount : item . quantity , notifiedPackageCount : item . quantity , shipmentNo : item . shipmentNo , trackingCode : form . trackingNo || undefined , newSkuCode : item . newSkuCode , labelRequirement : form . requirement || undefined } ) ) } )
} else if ( props . service === 'disposal' ) {
const items = cargoRows . value . filter ( ( item ) => item . quantity > 0 ) . map ( ( item ) => {
if ( ! item . sourceOrderId || item . sourceOrderId !== selectedDiscardOrderId . value || item . quantity > Number ( item . availableQuantity || 0 ) ) {
throw new Error ( ` SKU ${ item . sku } 的可废弃库存不足 ` )
}
return { orderId : item . sourceOrderId , skuCode : item . sku , discardPackages : item . quantity }
} )
await createDiscard ( { items , idempotencyKey , remark : [ form . reason , form . remark ] . filter ( Boolean ) . join ( '; ' ) || undefined } )
} else {
const sources = await listLastlegOrders ( form . inboundNo )
if ( sources . records . length === 0 ) throw new Error ( '未找到该入仓码对应的尾程订单' )
const packageCount = cargoRows . value . reduce ( ( sum , item ) => sum + item . quantity , 0 )
await createTransfer ( { inboundCode : form . inboundNo , sourceLastlegOrderIds : sources . records . map ( ( item ) => item . id ) , operatingWarehouseId : primaryWarehouse , fbaWarehouseCode : form . destination , deliveryAddress : form . address , packageCount , volumeCbm : form . volumeCbm , deliveryMethod : form . deliveryMethod , estimatedArrivalAt : form . estimatedArrivalAt , fbaShipmentNo : form . fbaShipmentNo , trackingNo : form . trackingNo || undefined , remark : form . remark || undefined , needRelabel : props . service === 'relabel' || form . relabel , idempotencyKey , items : cargoRows . value . map ( ( item ) => ( { skuCode : item . sku , productNameZh : item . name , packageCount : item . quantity , shippingMark : item . shippingMark || undefined } ) ) } )
}
ElMessage . success ( ` ${ config . value . title } 提交成功 ` ) ; mode . value = 'list' ; await load ( )
} catch ( error ) { ElMessage . error ( error instanceof Error ? error . message : '提交失败' ) }
finally { saving . value = false }
}
async function openDetail ( value : Record < string , string > ) {
try {
const detail = props . service === 'labeling' ? await getRelabel ( value [ '0' ] ) : props . service === 'disposal' ? await getDiscard ( value [ '0' ] ) : await getTransfer ( value [ '0' ] )
selectedDetail . value = { ... value , raw : JSON . stringify ( detail ) } ; mode . value = 'detail'
} catch ( error ) { ElMessage . error ( error instanceof Error ? error . message : '详情加载失败' ) }
}
< / script >
< template >
< section class = "warehouse-service-page" >
< template v-if = "mode === 'list'" >
< div class = "warehouse-breadcrumb" > < span > 首页 < / span > < b > / < / b > < strong > { { config . title } } < / strong > < / div >
< div class = "warehouse-page-heading" > < h2 > { { config . title } } < / h2 > < el-button type = "primary" @click ="openCreate" > < Plus :size = "16" / > { { props . service === 'relabel' ? '新建退货换标单' : props . service === 'transfer' ? '新增订单' : ` 新增 ${ config . title } ` } } < / el-button > < / div >
< div class = "warehouse-card warehouse-filter-card" >
< div class = "warehouse-filter-grid" >
< label > < span > { { config . numberLabel } } < / span > < el-input v-model = "filters.number" :placeholder="`请输入${config.numberLabel}`" clearable / > < / label >
< label v-if = "props.service !== 'labeling' && props.service !== 'relabel'"><span>进仓编号</span><el-input v-model="filters.inboundNo" placeholder="请输入进仓编号" clearable / > < / label >
< label > < span > { { props . service === 'relabel' ? '来源 FBA 仓' : '操作仓' } } < / span > < el-select v-model = "filters.warehouse" placeholder="全部"><el-option label="美西洛杉矶仓" value="LAX" /><el-option label="美东新泽西仓" value="EWR" /><el-option label="美中芝加哥仓" value="ORD" / > < / el-select > < / label >
< label v-if = "props.service === 'transfer' || props.service === 'relabel'"><span>目的 FBA 仓</span><el-input v-model="filters.destination" placeholder="请输入仓代码" clearable / > < / label >
< label > < span > 订单状态 < / span > < el-select v-model = "filters.status" placeholder="全部状态" clearable><el-option v-for="option in statusOptions" :key="option.value" :label="option.label" :value="option.value" / > < / el-select > < / label >
< label > < span > 创建时间 < / span > < el-date-picker v-model = "filters.start" type="date" placeholder="开始日期" value-format="YYYY-MM-DD" / > < / label >
< label > < span > & nbsp ; < / span > < el-date-picker v-model = "filters.end" type="date" placeholder="结束日期" value-format="YYYY-MM-DD" / > < / label >
< / div >
< div class = "warehouse-filter-actions" > < el-button type = "primary" @click ="query" > < Search :size = "16" / > 查询 < / el-button > < el-button @click ="resetFilters(); query()" > 重置 < / el -button > < / div >
< / div >
< div v-loading = "loading" class="warehouse-card warehouse-table-card" >
< div class = "warehouse-table-scroll" > < table > < thead > < tr > < th v-for = "column in config.columns" :key="column">{{ column }}</th></tr></thead><tbody><tr v-for="(row, rowIndex) in submittedRows" :key="rowIndex"><td v-for="(_, index) in config.columns.slice(0, -1)" :key="index">{{ row[String(index)] }}</td><td><button class="warehouse-link" type="button" @click="openDetail(row)"><Eye :size="15" />查看</button></td></tr><tr v-if="submittedRows.length === 0"><td :colspan="config.columns.length"><div class="warehouse-empty" > {{ config.emptyText }} < / div > < / td > < / tr > < / tbody > < / table > < / div >
< div class = "warehouse-pagination" > < span > 共 < strong > { { submittedRows . length } } < / strong > 条 < / span > < el-button size = "small" disabled > 上一页 < / el-button > < button type = "button" class = "warehouse-page-number" > 1 < / button > < el-button size = "small" disabled > 下一页 < / el-button > < / div >
< / div >
< / template >
< template v-else-if = "mode === 'create'" >
< div class = "warehouse-breadcrumb" > < button type = "button" @click ="mode = 'list'" > {{ config.title }} < / button > < b > / < / b > < strong > 新增 < / strong > < / div >
< div class = "warehouse-page-heading" > < h2 > { { config . createTitle } } < / h2 > < el-button @click ="mode = 'list'" > < ArrowLeft :size = "16" / > 返回列表 < / el-button > < / div >
< form @submit.prevent ="submit" >
< div class = "warehouse-card warehouse-form-section" > < h3 > { { props . service === 'relabel' ? '退货信息' : props . service === 'labeling' ? '订单信息' : '基础与派送信息' } } < / h3 >
< div class = "warehouse-form-grid" >
< label v-if = "props.service === 'labeling'"><span><i>*</i>客户单号</span><el-input v-model="form.customerNo" placeholder="请输入客户单号" / > < / label >
< label v-if = "props.service === 'disposal'" class="warehouse-span-2"><span><i>*</i>选择入库单</span><el-select v-model="selectedDiscardOrderId" filterable :loading="discardCandidatesLoading" placeholder="请选择有可用库存的入库单" @change="selectDiscardInbound"><el-option v-for="candidate in discardCandidates" :key="candidate.orderId" :label="`${candidate.inboundOrderNo || candidate.inboundCode}| ${candidate.inboundCode}| ${candidate.overseasWarehouseName || candidate.overseasWarehouseId}|可用 ${candidate.availablePackages} 箱`" :value="candidate.orderId" /></el-select><small class="warehouse-hint" > 仅显示一件代发中仍有可用箱数的入库单 。 < / small > < / label >
< label v-if = "props.service === 'transfer' || props.service === 'relabel'"><span><i>*</i>进仓编号</span><el-input v-model="form.inboundNo" placeholder="请输入已有入仓编号" / > < / label >
< label v-if = "props.service === 'relabel'"><span><i>*</i>来源 FBA 仓库</span><el-select v-model="form.sourceWarehouse" placeholder="请选择"><el-option label="ONT8" value="ONT8" /><el-option label="ABE8" value="ABE8" /><el-option label="CLT2" value="CLT2" / > < / el-select > < / label >
< label v-else-if = "props.service === 'disposal'"><span>进仓编号</span><el-input v-model="form.inboundNo" readonly / > < / label >
< label v-else > < span > < i > * < / i > { { props . service === 'labeling' ? '目的海外仓' : '操作仓' } } < / span > < el-select v-model = "form.warehouse" placeholder="请选择"><el-option label="美西海外仓-ONT8" value="ONT8" /><el-option label="美东海外仓-ABE8" value="ABE8" /><el-option label="美中海外仓-MDW2" value="MDW2" / > < / el-select > < / label >
< label v-if = "props.service === 'disposal'"><span>操作仓</span><el-input :model-value="discardCandidates.find((item) => item.orderId === selectedDiscardOrderId)?.overseasWarehouseName || form.warehouse" readonly / > < / label >
< label v-if = "props.service === 'relabel' || props.service === 'transfer'"><span><i>*</i>目的 FBA 仓</span><el-input v-model="form.destination" placeholder="请输入仓代码" / > < / label >
< label v-if = "props.service === 'transfer' || props.service === 'relabel'"><span><i>*</i>派送方式</span><el-select v-model="form.deliveryMethod" placeholder="请选择"><el-option label="卡车派送" value="卡车派送" /><el-option label="快递派送" value="快递派送" / > < / el-select > < / label >
< label v-if = "props.service === 'relabel' || props.service === 'disposal'"><span><i>*</i>{{ props.service === 'relabel' ? '退货原因' : '废弃原因' }}</span><el-select v-model="form.reason" placeholder="请选择"><el-option label="客户退货" value="客户退货" /><el-option label="亚马逊移除" value="亚马逊移除" /><el-option label="残损不可售" value="残损不可售" / > < / el-select > < / label >
< label v-if = "props.service === 'labeling'"><span><i>*</i>联系人</span><el-input v-model="form.contact" placeholder="请输入联系人" / > < / label >
< label v-if = "props.service === 'labeling'"><span><i>*</i>联系电话</span><el-input v-model="form.phone" placeholder="请输入联系电话" / > < / label >
< label v-if = "props.service === 'labeling'"><span><i>*</i>贴标截止时间</span><el-date-picker v-model="form.labelDeadline" type="datetime" value-format="YYYY-MM-DDTHH:mm:ss" placeholder="请选择截止时间" / > < / label >
< label v-if = "props.service === 'transfer' || 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>总体积(CBM) </span><el-input-number v-model="form.volumeCbm" :min="0.0001" :precision="4" controls-position="right" / > < / label >
< label v-if = "props.service === 'transfer' || props.service === 'relabel'"><span><i>*</i>预计到达时间</span><el-date-picker v-model="form.estimatedArrivalAt" type="datetime" value-format="YYYY-MM-DDTHH:mm:ss" placeholder="请选择预计到达时间" / > < / label >
< label v-if = "props.service === 'transfer' || props.service === 'relabel'"><span><i>*</i>FBA 货件编号</span><el-input v-model="form.fbaShipmentNo" placeholder="请输入 FBA 货件编号" / > < / label >
< label v-if = "props.service === 'labeling'"><span>贴标要求</span><el-input v-model="form.requirement" / > < / label >
< label v-else-if = "props.service !== 'disposal'"><span>物流跟踪号</span><el-input v-model="form.trackingNo" / > < / label >
< label class = "warehouse-span-2" > < span > 订单备注 < / span > < el-input v-model = "form.remark" type="textarea" :rows="3" placeholder="请输入备注信息" / > < / label >
< / div >
< / div >
< div class = "warehouse-card warehouse-form-section" > < div class = "warehouse-section-heading" > < h3 > 货物明细 < / h3 > < div v-if = "props.service !== 'disposal'"><el-button type="primary" size="small" @click="addCargoRow"><Plus :size="15" />添加行</el-button><el-button size="small" @click="cargoRows = []" > 清空 < / el -button > < / div > < / div >
< div class = "warehouse-table-scroll" > < table class = "warehouse-edit-table" > < thead > < tr > < th > 商品 SKU * < / th > < th > 中文品名 * < / th > < th v-if = "props.service === 'disposal'">可用箱数</th><th v-else>{{ props.service === 'labeling' ? '货件编号 *' : 'FNSKU / 箱唛' }}</th><th v-if="props.service === 'labeling'">换标新 SKU *</th><th>{{ props.service === 'disposal' ? '本次废弃箱数' : props.service === 'relabel' ? '退件数量' : '箱数' }} *</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'" /></td><td><el-input v-model="row.name" :readonly="props.service === 'disposal'" /></td><td v-if="props.service === 'disposal'"><strong>{{ row.availableQuantity }} 箱</strong></td><td v-else><el-input v-if="props.service === 'labeling'" v-model="row.shipmentNo" /><el-input v-else v-model="row.shippingMark" /></td><td v-if="props.service === 'labeling'"><el-input v-model="row.newSkuCode" /></td><td><el-input-number v-model="row.quantity" :min="props.service === 'disposal' ? 0 : 1" :max="props.service === 'disposal' ? row.availableQuantity : undefined" controls-position="right" /></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 === 'labeling' ? 6 : 5"><div class="warehouse-empty" > {{ props.service = = = ' disposal ' ? ' 请先选择入库单 , 系统将自动带出 SKU 和可用箱数 ' : ' 暂无货物明细 , 请点击 “ 添加行 ” ' }} < / div > < / td > < / tr > < / tbody > < / table > < / div >
< / div >
< div v-if = "props.service === 'transfer' || props.service === 'relabel'" class="warehouse-card warehouse-form-section"><h3>换标设置</h3><el-checkbox v-model="form.relabel">是否换标</el-checkbox><p v-if="form.relabel" class="warehouse-hint" > 请确认 SKU 与 FNSKU 信息准确 , 提交后海外仓将按明细执行换标 。 < / p > < / div >
< div class = "warehouse-submit-bar" > < el-button @click ="resetForm" > 重置 < / el -button > < el-button type = "primary" native -type = " submit " :loading = "saving" > 提交 { { config . title } } < / el-button > < / div >
< / form >
< / template >
< template v-else > < div class = "warehouse-breadcrumb" > < button type = "button" @click ="mode = 'list'" > {{ config.title }} < / button > < b > / < / b > < strong > 订单详情 < / strong > < / div > < div class = "warehouse-page-heading" > < h2 > { { config . title } } 详情 < / h2 > < el-button @click ="mode = 'list'" > < ArrowLeft :size = "16" / > 返回列表 < / el-button > < / div > < div class = "warehouse-card warehouse-form-section" > < h3 > 订单概览 < / h3 > < div class = "warehouse-detail-grid" > < div v-for = "(column, index) in config.columns.slice(0, -1)" :key="column" > < span > { { column } } < / span > < strong > { { selectedDetail ? . [ String ( index ) ] || '-' } } < / strong > < / div > < / div > < / div > < / template >
< / section >
< / template >
< style scoped >
. warehouse - service - page { color : # 25324 a } . warehouse - breadcrumb { display : flex ; align - items : center ; gap : 9 px ; margin : 0 0 14 px ; color : # 9 aa3b2 ; font - size : 12 px } . warehouse - breadcrumb b { font - weight : 400 } . warehouse - breadcrumb strong { color : # 606 b7c } . warehouse - breadcrumb button { padding : 0 ; border : 0 ; background : transparent ; color : # 4376 b8 ; cursor : pointer } . warehouse - page - heading { display : flex ; align - items : center ; justify - content : space - between ; margin - bottom : 16 px } . warehouse - page - heading h2 { margin : 0 ; color : # 17233 a ; font - size : 22 px ; letter - spacing : 0 } . warehouse - card { margin - bottom : 12 px ; border : 1 px solid # e7ebf1 ; border - radius : 4 px ; background : # fff ; box - shadow : 0 1 px 3 px rgba ( 25 , 42 , 70 , .04 ) } . warehouse - filter - card { padding : 20 px 22 px 16 px } . warehouse - filter - grid { display : grid ; grid - template - columns : repeat ( 4 , minmax ( 180 px , 1 fr ) ) ; gap : 16 px 20 px } . warehouse - filter - grid label , . warehouse - form - grid label { display : flex ; min - width : 0 ; flex - direction : column ; gap : 7 px } . warehouse - filter - grid label > span , . warehouse - form - grid label > span { color : # 596579 ; font - size : 12 px } . warehouse - filter - actions { display : flex ; justify - content : flex - end ; gap : 8 px ; margin - top : 18 px } . warehouse - table - card { overflow : hidden } . warehouse - table - scroll { overflow - x : auto } table { width : 100 % ; min - width : 940 px ; border - collapse : collapse ; font - size : 12 px } th { height : 44 px ; padding : 0 14 px ; border - bottom : 1 px solid # e8edf3 ; background : # f7f9fc ; color : # 4 d5a6e ; font - weight : 650 ; text - align : left ; white - space : nowrap } td { height : 48 px ; padding : 8 px 14 px ; border - bottom : 1 px solid # eef1f5 ; color : # 526176 ; white - space : nowrap } . warehouse - empty { display : grid ; height : 150 px ; color : # a0a9b7 ; place - items : center } . warehouse - link , . warehouse - delete { display : inline - flex ; align - items : center ; gap : 5 px ; padding : 4 px ; border : 0 ; background : transparent ; color : # 2176 d2 ; cursor : pointer } . warehouse - delete { color : # d64545 } . warehouse - pagination { display : flex ; align - items : center ; justify - content : flex - end ; gap : 8 px ; padding : 14 px 18 px ; color : # 778398 ; font - size : 12 px } . warehouse - pagination span { margin - right : 8 px } . warehouse - page - number { width : 28 px ; height : 28 px ; border : 1 px solid # 2176 d2 ; border - radius : 3 px ; background : # 2176 d2 ; color : # fff } . warehouse - form - section { padding : 22 px } . warehouse - form - section h3 { margin : 0 0 18 px ; padding - bottom : 12 px ; border - bottom : 1 px solid # edf0f4 ; color : # 26364 f ; font - size : 15 px ; letter - spacing : 0 } . warehouse - form - grid { display : grid ; grid - template - columns : repeat ( 2 , minmax ( 240 px , 1 fr ) ) ; gap : 18 px 28 px } . warehouse - form - grid i { margin - right : 3 px ; color : # e24b4b ; font - style : normal } . warehouse - span - 2 { grid - column : 1 / - 1 } . warehouse - section - heading { display : flex ; align - items : center ; justify - content : space - between } . warehouse - section - heading h3 { flex : 1 } . warehouse - edit - table td { padding : 9 px 10 px } . warehouse - edit - table : deep ( . el - input - number ) { width : 130 px } . warehouse - hint { margin : 12 px 0 0 ; color : # 8792 a2 ; font - size : 12 px } . warehouse - submit - bar { display : flex ; justify - content : center ; gap : 10 px ; padding : 12 px 0 30 px } . warehouse - detail - grid { display : grid ; grid - template - columns : repeat ( 3 , 1 fr ) ; gap : 24 px } . warehouse - detail - grid div { display : flex ; flex - direction : column ; gap : 7 px } . warehouse - detail - grid span { color : # 8 a95a5 ; font - size : 12 px } . warehouse - detail - grid strong { color : # 334157 ; font - size : 13 px } : deep ( . el - button ) { border - radius : 4 px } : deep ( . el - button span ) { display : inline - flex ; align - items : center ; gap : 6 px } : deep ( . el - select ) , : deep ( . el - date - editor . el - input ) { width : 100 % } @ media ( max - width : 1100 px ) { . warehouse - filter - grid { grid - template - columns : repeat ( 2 , 1 fr ) } . warehouse - detail - grid { grid - template - columns : repeat ( 2 , 1 fr ) } }
< / style >