This commit is contained in:
2026-09-23 09:06:20 +08:00
parent befd22af96
commit 25db3c1777
3 changed files with 75 additions and 9 deletions
@@ -4,6 +4,7 @@ export type RelabelDeliveryMethod = 'TRUCK' | 'EXPRESS' | 'TRUCK_PICKUP' | 'EXPR
export interface PageResult<T> { records: T[]; page: number; size: number; total: number; pages: number }
export interface LastlegOrderSummary { id: string; orderNo: string; inboundCode: string; orderType: string; packageCount: number; overseasWarehouseId?: string }
export interface HistoricalOrderSummary { id: string | null; orderNo: string; sourceOrderNo?: string; outboundOrderNo?: string; relabelOrderNo?: string; orderType?: string; status?: string; packageCount?: number }
export interface TransferSummary { id: string; transferNo: string; inboundCode: string; lastlegOrderId: string; lastlegOrderNo: string; operatingWarehouseId: string; fbaWarehouseCode?: string; packageCount: number; deliveryMethod: string; needRelabel: boolean; relabelNo?: string; status: string; estimatedArrivalAt: string; estimatedShipAt?: string; actualShipAt?: string; createdAt: string }
export interface RelabelSummary { id: string; relabelNo: string; lastlegOrderId: string; lastlegOrderNo: string; customerOrderNo: string; overseasWarehouseId: string; status: string; itemCount: number; totalPackageCount: number; notifiedPackageCount: number; skuCodes: string; createdAt: string; totalFileId?: string; totalFileName?: string; totalFileUrl?: string; cartonTotalFileId?: string; cartonTotalFileName?: string; cartonTotalFileUrl?: string; skuTotalFileId?: string; skuTotalFileName?: string; skuTotalFileUrl?: string; deliveryMethod?: RelabelDeliveryMethod; printShippingLabel?: boolean; shippingLabelFileId?: string; shippingLabelFileName?: string; shippingLabelFileUrl?: string; fulfillmentStatus?: string; outboundOrderNo?: string; customerConfirmedAt?: string }
export interface RelabelItemDetail {
@@ -36,7 +37,7 @@ export interface RelabelInventoryCandidate {
}
export interface TransferCreate {
operatingWarehouseId: string; fbaWarehouseCode?: string; destinationType?: 'NONE' | 'FBA' | 'PRIVATE'
inboundCode?: string; sourceLastlegOrderIds?: number[]; operatingWarehouseId: string; fbaWarehouseCode?: string; destinationType?: 'NONE' | 'FBA' | 'PRIVATE'
destinationWarehouseId?: string; destinationWarehouseCode?: string; destinationWarehouseName?: string
privateDestinationAddress?: string; privateDestinationPostalCode?: string; privateDestinationContactName?: string; privateDestinationContactPhone?: string
deliveryAddress: string; packageCount: number; volumeCbm: number; deliveryMethod: string; estimatedArrivalAt: string; estimatedShipAt?: string
@@ -52,8 +53,8 @@ export interface RelabelCreate {
cartonMark: string; fbaCartonMark?: string; destinationFbaWarehouseCode?: string; operationType: 'CARTON_LABEL' | 'SKU_LABEL' | 'CARTON_AND_SKU'; skuItems?: Array<{ skuCode: string; newSkuCode: string; quantity: number; remark?: string }>; remark?: string }>
}
export function listLastlegOrders(inboundCode: string) {
return overseasApiRequest<PageResult<LastlegOrderSummary>>({ method: 'GET', url: '/merchant/v1/last-leg/orders', params: { page: 1, size: 200, inboundCode } })
export function listRelabelHistoricalOrders(operationWarehouseId: string, keyword?: string) {
return overseasApiRequest<HistoricalOrderSummary[]>({ method: 'GET', url: '/merchant/v1/last-leg/relabel-applications/historical-orders', params: { operationWarehouseId, keyword: keyword || undefined } })
}
export function listTransfers(params: Record<string, unknown>) {
return overseasApiRequest<PageResult<TransferSummary>>({ method: 'GET', url: '/merchant/v1/last-leg/warehouse-transfers', params })
@@ -12,7 +12,7 @@ import {
getTransferCartonLabel, submitTransfer, updateTransfer,
confirmRelabelCompletion, confirmTransferMeasurement, downloadRelabelCompletionPhoto, downloadTransferMeasurementPhoto, uploadRelabelShippingLabel,
uploadRelabelFile,
listDiscardCandidates, listDiscards, listRelabelInventoryCandidates, listRelabels, listTransfers, cancelDiscard,
listDiscardCandidates, listDiscards, listRelabelInventoryCandidates, listRelabelHistoricalOrders, listRelabels, listTransfers, cancelDiscard,
type RelabelInventoryCandidate,
type RelabelCreate,
type RelabelDetail,
@@ -165,6 +165,9 @@ const discardCandidatesLoading = ref(false)
const relabelCandidates = ref<RelabelInventoryCandidate[]>([])
const selectedRelabelInboundOrderId = ref('')
const relabelCandidatesLoading = ref(false)
const transferInventoryCandidates = ref<RelabelInventoryCandidate[]>([])
const selectedTransferInventory = ref('')
const selectedTransferSourceOrderIds = ref<number[]>([])
const returnRelabelSkuRows = ref<ReturnRelabelSkuRow[]>([])
const isReturnRelabelSource = computed(() => props.service === 'labeling' && selectedRelabelInboundOrderId.value.startsWith('RETURN_ORDER:'))
const selectedReturnOrderNo = computed(() => isReturnRelabelSource.value ? selectedRelabelInboundOrderId.value.slice('RETURN_ORDER:'.length) : '')
@@ -262,6 +265,9 @@ function resetForm() {
discardCandidates.value = []
selectedDiscardOrderId.value = ''
relabelCandidates.value = []
transferInventoryCandidates.value = []
selectedTransferInventory.value = ''
selectedTransferSourceOrderIds.value = []
selectedRelabelInboundOrderId.value = ''
cartonTotalFile.value = null
skuTotalFile.value = null
@@ -884,6 +890,9 @@ async function submit(action: 'draft' | 'submit' = 'submit') {
if (props.service === 'transfer' && transferDestinationType.value === 'FBA' && !form.destination) {
ElMessage.warning('请选择 FBA 目的仓'); return
}
if (props.service === 'transfer' && transferDestinationType.value === 'NONE') {
ElMessage.warning('请选择 FBA 仓或私人仓'); return
}
if (props.service === 'transfer' && transferDestinationType.value === 'PRIVATE' && (!form.privateDestinationAddress || !form.privateDestinationPostalCode || !form.privateDestinationContact || !form.privateDestinationPhone)) {
ElMessage.warning('请完整填写私人仓地址、邮编、联系人和电话'); return
}
@@ -942,6 +951,10 @@ async function submit(action: 'draft' | 'submit' = 'submit') {
const operationType = labelingMode.value === 'carton' ? 'CARTON_LABEL' : labelingMode.value === 'sku' ? 'SKU_LABEL' : 'CARTON_AND_SKU'
const selectedFba = fbaWarehouses.value.find((warehouse) => warehouse.warehouseCode === form.destination)
const payload: TransferCreate = { operatingWarehouseId: primaryWarehouse, destinationType: transferDestinationType.value, fbaWarehouseCode: transferDestinationType.value === 'FBA' ? form.destination || undefined : undefined, destinationWarehouseId: transferDestinationType.value === 'FBA' ? String(selectedFba?.id || '') || undefined : undefined, destinationWarehouseCode: transferDestinationType.value === 'FBA' ? form.destination || undefined : undefined, destinationWarehouseName: transferDestinationType.value === 'FBA' ? selectedFba?.warehouseName : undefined, privateDestinationAddress: transferDestinationType.value === 'PRIVATE' ? form.privateDestinationAddress : undefined, privateDestinationPostalCode: transferDestinationType.value === 'PRIVATE' ? form.privateDestinationPostalCode : undefined, privateDestinationContactName: transferDestinationType.value === 'PRIVATE' ? form.privateDestinationContact : undefined, privateDestinationContactPhone: transferDestinationType.value === 'PRIVATE' ? form.privateDestinationPhone : undefined, deliveryAddress: form.address, packageCount, volumeCbm: form.volumeCbm, deliveryMethod: form.deliveryMethod, estimatedArrivalAt: form.estimatedArrivalAt, estimatedShipAt: form.estimatedShipAt || undefined, fbaShipmentNo: transferDestinationType.value === 'FBA' ? form.fbaShipmentNo || undefined : undefined, trackingNo: form.deliveryMethod === 'CUSTOMER_DELIVERY' ? form.trackingNo || undefined : undefined, fbaBoxNos: customerCartonMarks.length ? customerCartonMarks.join(',') : undefined, remark: transferRemark || undefined, needRelabel: props.service === 'relabel' || form.relabel, labelDeadline: form.relabel ? form.labelDeadline : undefined, pickupContactName: form.deliveryMethod === 'WAREHOUSE_PICKUP' ? form.pickupContact : undefined, pickupContactPhone: form.deliveryMethod === 'WAREHOUSE_PICKUP' ? form.pickupPhone : undefined, relabelContactName: form.relabel ? form.relabelContact : undefined, relabelContactPhone: form.relabel ? form.relabelPhone : undefined, relabelDeliveryMethod: form.relabel && labelingMode.value !== 'sku' ? form.relabelDeliveryMethod as 'TRUCK' | 'EXPRESS' | 'TRUCK_PICKUP' | 'EXPRESS_PICKUP' : undefined, idempotencyKey, items: cargoRows.value.map((item) => ({ skuCode: item.sku, productNameZh: item.name, packageCount: item.quantity, shippingMark: transferCustomerMarks(item).join(',') || undefined })), relabelItems: form.relabel ? transferRelabelRows.value.map((item) => ({ operationType, customerCartonMarks: transferCustomerMarks(item), originalSku: item.sku, newSku: item.newSkuCode || undefined, quantity: item.quantity, fbaCartonMark: item.fbaCartonMark || undefined, destinationWarehouseCode: transferRelabelDefaultDestination.value || undefined, remark: item.remark || undefined })) : undefined }
if (props.service === 'transfer' && selectedTransferInventory.value) {
;(payload as TransferCreate).inboundCode = selectedTransferInventory.value
;(payload as TransferCreate).sourceLastlegOrderIds = selectedTransferSourceOrderIds.value
}
const created = editingTransferNo.value
? await updateTransfer(editingTransferNo.value, payload)
: await createTransfer(payload)
@@ -1127,10 +1140,51 @@ async function selectTransferWarehouse() {
form.relabelContact = detail.warehouse.contactName || ''
form.relabelPhone = detail.warehouse.contactPhone || ''
if (form.deliveryMethod === 'CUSTOMER_DELIVERY') form.address = operationWarehouseAddress.value
transferInventoryCandidates.value = await listRelabelInventoryCandidates(form.warehouse)
} catch (error) {
ElMessage.error(error instanceof Error ? error.message : '中转仓联系信息加载失败')
} finally { operationWarehouseDetailLoading.value = false }
}
const transferInventoryOptions = computed(() => {
const grouped = new Map<string, { value: string; label: string; packages: number }>()
for (const item of transferInventoryCandidates.value) {
const value = String(item.inboundCode || '')
if (!value) continue
const packages = inventoryQuantity(item.availablePackages)
const current = grouped.get(value)
if (current) current.packages += packages
else grouped.set(value, { value, label: `${item.inboundOrderNo || value}|${value}`, packages })
}
return [...grouped.values()]
})
async function selectTransferInventory(value: string) {
selectedTransferInventory.value = value || ''
selectedTransferSourceOrderIds.value = []
if (!value) return
form.inboundNo = value
// The inventory-candidates response already contains the merchant-visible
// SKU/name/package data. Do not call the platform-only last-leg orders API
// here: merchants are not authorized for that endpoint (A0403), and the
// transfer service can derive source orders from inboundCode when IDs are
// omitted. Aggregate candidates for the selected inbound into cargo rows.
const candidates = transferInventoryCandidates.value.filter((item) => String(item.inboundCode || '') === value)
const grouped = new Map<string, CargoRow>()
for (const candidate of candidates) {
const sku = String(candidate.skuCode || '')
const current = grouped.get(sku)
const quantity = inventoryQuantity(candidate.availablePackages)
if (current) current.quantity += quantity
else grouped.set(sku, { ...createCargoRow(), sku, name: candidate.productNameZh || '', quantity, availableQuantity: quantity })
}
cargoRows.value = [...grouped.values()]
try {
const historical = await listRelabelHistoricalOrders(form.warehouse, value)
const sourceIds = historical.map((item) => Number(item.id)).filter((id) => Number.isInteger(id) && id > 0)
selectedTransferSourceOrderIds.value = sourceIds
} catch (error) {
ElMessage.error(error instanceof Error ? error.message : '库存来源订单加载失败')
}
}
function selectTransferInboundMethod(value: string | number | boolean | undefined) {
form.address = value === 'CUSTOMER_DELIVERY' ? operationWarehouseAddress.value : ''
}
@@ -1226,6 +1280,7 @@ function selectRelabelInventory(inboundOrderId: string) {
<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' ? '海外操作仓(中转仓)' : props.service === 'transfer' ? '入库海外仓' : '操作仓' }}</span><el-select v-model="form.warehouse" :loading="fbaWarehousesLoading || operationWarehouseDetailLoading" :disabled="Boolean(operationWarehousesError)" clearable :placeholder="props.service === 'transfer' ? '请选择海外仓' : '请选择国外中转仓'" @change="props.service === 'labeling' ? selectOperationWarehouse() : props.service === 'transfer' && selectTransferWarehouse()"><el-option v-for="warehouse in operationWarehouses" :key="warehouse.id" :label="warehouseLabel(warehouse)" :value="String(warehouse.id)" /></el-select><small v-if="operationWarehousesError" class="warehouse-hint">{{ operationWarehousesError }}</small></label>
<label v-if="props.service === 'labeling'" class="warehouse-span-2"><span><i>*</i>选择有库存的入仓单或退货单</span><el-select v-model="selectedRelabelInboundOrderId" filterable clearable :loading="relabelCandidatesLoading" :disabled="!form.warehouse" no-data-text="当前中转仓暂无可用库存" placeholder="请选择当前仍有可用库存的入仓单或退货单" @change="selectRelabelInventory"><el-option v-for="option in relabelInboundOptions" :key="option.value" :label="`${option.inboundOrderNo}|${option.inboundCode}|可用 ${option.availablePackages} ${option.quantityUnit}`" :value="option.value" /></el-select></label>
<label v-if="props.service === 'transfer'"><span>库存转运来源(可选)</span><el-select v-model="selectedTransferInventory" filterable clearable :loading="relabelCandidatesLoading" :disabled="!form.warehouse" no-data-text="当前海外仓暂无可用库存" placeholder="不选则按新货转运;选择后免 PDA 测量" @change="selectTransferInventory"><el-option v-for="option in transferInventoryOptions" :key="option.value" :label="`${option.label}|可用 ${option.packages} 箱`" :value="option.value" /></el-select><small class="warehouse-hint">库存转运可直接进入目的仓、换标和排车流程,PDA 可拆托、重打或同目的仓合托。</small></label>
<label v-if="props.service === 'labeling' && (isReturnRelabelSource || labelingMode !== 'sku')"><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>
<div v-if="props.service === 'transfer'" class="warehouse-choice-field warehouse-span-2"><span><i>*</i>入仓方式</span><el-radio-group v-model="form.deliveryMethod" @change="selectTransferInboundMethod"><el-radio-button value="WAREHOUSE_PICKUP">仓库上门提货</el-radio-button><el-radio-button value="CUSTOMER_DELIVERY">客户送货到仓</el-radio-button></el-radio-group></div>
<label v-if="props.service === 'transfer' && form.deliveryMethod === 'WAREHOUSE_PICKUP'" class="pickup-address-field"><span><i>*</i>提货地址</span><el-input v-model="form.address" placeholder="请输入仓库上门提货的详细地址" /></label>
@@ -1253,7 +1308,7 @@ function selectRelabelInventory(inboundOrderId: string) {
<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>FBA 货件编号</span><el-input v-model="form.fbaShipmentNo" placeholder="选填,可后续补充" /></label>
<label v-if="props.service === 'transfer' && transferDestinationType === 'PRIVATE'" class="warehouse-span-2"><span><i>*</i>私人仓地址</span><el-input v-model="form.privateDestinationAddress" maxlength="1000" placeholder="请输入私人仓完整地址" /></label>
<label v-if="props.service === 'transfer' && transferDestinationType === 'PRIVATE'"><span><i>*</i>私人仓地址</span><el-input v-model="form.privateDestinationAddress" maxlength="1000" placeholder="请输入私人仓完整地址" /></label>
<label v-if="props.service === 'transfer' && transferDestinationType === 'PRIVATE'"><span><i>*</i>邮编</span><el-input v-model="form.privateDestinationPostalCode" maxlength="32" placeholder="请输入邮编" /></label>
<label v-if="props.service === 'transfer' && transferDestinationType === 'PRIVATE'"><span><i>*</i>私人仓联系人</span><el-input v-model="form.privateDestinationContact" maxlength="128" placeholder="请输入联系人" /></label>
<label v-if="props.service === 'transfer' && transferDestinationType === 'PRIVATE'"><span><i>*</i>联系电话</span><el-input v-model="form.privateDestinationPhone" maxlength="64" placeholder="请输入联系电话" /></label>
@@ -1261,7 +1316,7 @@ function selectRelabelInventory(inboundOrderId: string) {
<label v-if="props.service === 'labeling'"><span>贴标要求</span><el-input v-model="form.requirement" placeholder="请输入贴标要求" /></label>
<label v-else-if="props.service === 'relabel' || (props.service === 'transfer' && form.deliveryMethod === 'CUSTOMER_DELIVERY')"><span>物流跟踪号</span><el-input v-model="form.trackingNo" placeholder="请输入物流跟踪号(选填)" /></label>
<label class="warehouse-span-2"><span>备注</span><el-input v-model="form.remark" type="textarea" :rows="2" placeholder="选填" /></label>
<div v-if="props.service === 'transfer'" class="warehouse-choice-field warehouse-span-2"><span><i>*</i>换标服务</span><el-radio-group v-model="form.relabel"><el-radio-button :value="false">不需要</el-radio-button><el-radio-button :value="true">需要</el-radio-button></el-radio-group></div>
<div v-if="props.service === 'transfer'" class="warehouse-choice-field warehouse-choice-field--relabel warehouse-span-2"><span><i>*</i>是否贴标</span><el-radio-group v-model="form.relabel"><el-radio-button :value="false">不需要贴标</el-radio-button><el-radio-button :value="true">需要贴标</el-radio-button></el-radio-group><small>{{ form.relabel ? '已开启贴标,请在右侧填写贴标资料' : 'FBA 仓和私人仓均可选择是否贴标' }}</small></div>
</div>
</div>
<div class="warehouse-card warehouse-form-section warehouse-transfer-cargo-card">
@@ -1422,9 +1477,11 @@ function selectRelabelInventory(inboundOrderId: string) {
.warehouse-template-actions{flex-wrap:wrap;justify-content:flex-end}.warehouse-import-preview{margin:0 0 16px;padding:14px;border:1px solid #cfe0f5;border-radius:5px;background:#f8fbff}.warehouse-import-preview .warehouse-section-heading{margin-bottom:10px}.warehouse-import-preview .warehouse-section-heading>div{display:flex;align-items:center;gap:10px}.warehouse-import-preview p{margin:0;color:#7d8797;font-size:12px}.warehouse-binding-preview-table{min-width:980px;background:#fff}
.warehouse-transfer-relabel .warehouse-labeling-heading>div:first-child{min-width:0}.warehouse-transfer-relabel .warehouse-labeling-heading h3{margin-bottom:6px}.warehouse-transfer-relabel .warehouse-labeling-heading p{margin:0 0 16px;color:#7d8797;font-size:12px}.warehouse-transfer-relabel-fields{margin-bottom:18px}.warehouse-transfer-file-groups{display:grid;grid-template-columns:minmax(0,1.15fr) minmax(360px,.85fr);align-items:stretch;gap:14px;margin-bottom:14px}.warehouse-transfer-total-files,.warehouse-transfer-template-group{height:100%;margin:0}.warehouse-transfer-mark-table{min-width:1420px}.warehouse-transfer-mark-table td{vertical-align:top}.warehouse-transfer-mark-table :deep(.el-textarea){min-width:220px}.warehouse-transfer-mark-table :deep(.el-input-number){width:150px}.warehouse-count-error{color:#d64545}
.warehouse-transfer-form{display:grid;grid-template-columns:minmax(0,1fr);gap:12px}.warehouse-transfer-form--relabel{grid-template-columns:minmax(0,1fr) minmax(0,1.35fr);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:32px}.warehouse-transfer-form .warehouse-form-section{padding:16px}.warehouse-transfer-form .warehouse-form-section h3{margin-bottom:12px;padding-bottom:9px;font-size:14px}.warehouse-transfer-form .warehouse-form-grid{gap:12px 16px}.warehouse-transfer-form .warehouse-choice-field{min-height:34px;padding:8px 10px;grid-template-columns:118px minmax(0,1fr);gap:10px}.warehouse-transfer-form .warehouse-choice-field>span{font-size:12px}.warehouse-transfer-form .warehouse-receiving-card{gap:12px;padding:10px 12px}.warehouse-transfer-form .warehouse-section-heading{margin-bottom:8px}.warehouse-transfer-form .warehouse-edit-table{font-size:11px}.warehouse-transfer-form .warehouse-edit-table th{height:36px;padding:0 9px}.warehouse-transfer-form .warehouse-edit-table td{height:42px;padding:6px 7px}.warehouse-transfer-form .warehouse-transfer-mark-table :deep(.el-textarea){min-width:150px}.warehouse-transfer-form .warehouse-transfer-mark-table :deep(.el-input-number){width:112px}.warehouse-transfer-form .warehouse-hint{margin-top:5px;font-size:11px}.warehouse-transfer-form .warehouse-labeling-mode{gap:9px;margin-bottom:10px;padding:9px 10px}.warehouse-transfer-form .warehouse-labeling-mode>span{font-size:12px}.warehouse-transfer-form .warehouse-labeling-mode :deep(.el-radio-button__inner){padding:6px 9px}.warehouse-transfer-form .warehouse-template-actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:5px;padding-bottom:8px}.warehouse-transfer-form .warehouse-template-actions :deep(.el-button){padding:6px 8px;font-size:11px}.warehouse-transfer-form .warehouse-submit-bar{padding:4px 0 18px}.warehouse-transfer-form .warehouse-submit-bar :deep(.el-button){min-height:32px;padding:7px 14px}.warehouse-transfer-form .warehouse-transfer-relabel{overflow:hidden}.warehouse-transfer-form .warehouse-transfer-relabel-fields{margin-bottom:10px}.warehouse-step-heading{align-items:flex-start}.warehouse-step-heading h3{display:flex;align-items:center;gap:9px}.warehouse-step-number{display:inline-grid;flex:0 0 auto;width:24px;height:24px;border-radius:50%;background:#3478c8;color:#fff;font-size:12px;place-items:center}.warehouse-heading-actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:8px}.warehouse-transfer-template-group{margin:0 0 14px;padding:12px 14px;border:1px solid #dde5f0;border-radius:7px;background:#fbfcfe}.warehouse-binding-heading{margin-top:14px;padding:12px 14px;border:1px solid #dde5f0;border-radius:7px;background:#f8faff}.warehouse-binding-heading strong{display:flex;align-items:center;gap:8px;color:#34445c;font-size:13px}
.warehouse-transfer-form .warehouse-choice-field--relabel{grid-template-columns:150px auto minmax(240px,1fr)}.warehouse-choice-field--relabel :deep(.el-radio-group){display:flex;flex-wrap:nowrap}.warehouse-choice-field--relabel>small{min-width:0;margin:0;line-height:1.5}
@media(max-width:1250px){.warehouse-transfer-form--relabel{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}.warehouse-transfer-form .warehouse-template-actions{justify-content:flex-start}.warehouse-transfer-form .warehouse-labeling-heading{align-items:flex-start;flex-direction:column}}
@media(max-width:1180px){.warehouse-transfer-file-groups{grid-template-columns:minmax(0,1fr)}}
.warehouse-transfer-form .pickup-address-field{grid-column:span 1}
.warehouse-transfer-main-card .warehouse-form-grid{grid-template-columns:repeat(2,minmax(280px,640px))}.warehouse-transfer-cargo-card .warehouse-section-heading,.warehouse-transfer-cargo-card .warehouse-table-scroll{width:100%;max-width:1296px}
/* Keep the count and controls visible together on narrow list pages. */
.warehouse-pagination { justify-content: flex-end; }
.warehouse-pagination-total { color: #526176; font-size: 14px; }
@@ -1434,5 +1491,8 @@ function selectRelabelInventory(inboundOrderId: string) {
.warehouse-list-actions { display: flex; align-items: center; gap: 8px; white-space: nowrap; }
@media (max-width: 720px) {
.warehouse-pagination :deep(.el-pagination) { width: 100%; justify-content: flex-end; }
.warehouse-transfer-main-card .warehouse-form-grid { grid-template-columns: minmax(0, 1fr); }
.warehouse-transfer-cargo-card .warehouse-section-heading, .warehouse-transfer-cargo-card .warehouse-table-scroll { max-width: none; }
.warehouse-transfer-form .warehouse-choice-field--relabel { grid-template-columns: 1fr; align-items: start; }
}
</style>
@@ -25,9 +25,14 @@ test('仓储转运支持无目的地、FBA 仓和私人仓并显式选择是否
assert.match(view, /value="FBA">FBA 仓/)
assert.match(view, /value="PRIVATE">私人仓/)
assert.match(view, /transferDestinationType === 'PRIVATE'.*私人仓地址.*邮编.*私人仓联系人.*联系电话/s)
assert.match(view, />换标服务</)
assert.match(view, /:value="false">不需要/)
assert.match(view, /:value="true">需要/)
assert.match(view, />是否贴标</)
assert.match(view, /:value="false">不需要贴标/)
assert.match(view, /:value="true">需要贴标/)
assert.match(view, /FBA 仓和私人仓均可选择是否贴标/)
assert.doesNotMatch(view, /v-model="form\.relabel" disabled/)
assert.doesNotMatch(view, /value === 'FBA'\) form\.relabel = true/)
assert.doesNotMatch(view, /value === 'PRIVATE'.*form\.relabel = false/)
assert.match(view, /v-if="props\.service === 'transfer' && form\.relabel" class="warehouse-card warehouse-form-section warehouse-transfer-relabel"/)
assert.match(api, /fbaWarehouseCode\?: string/)
assert.match(api, /privateDestinationAddress\?: string; privateDestinationPostalCode\?: string/)
assert.match(api, /fbaShipmentNo\?: string/)