This commit is contained in:
2026-10-15 15:45:16 +08:00
parent 2ade6bfa3c
commit 092690791d
10 changed files with 348 additions and 16 deletions
@@ -1,10 +1,11 @@
<script setup lang="ts">
import '@/styles/adminDensity.css'
import { computed, onMounted, reactive, ref } from 'vue'
import { computed, onMounted, reactive, ref, watch } from 'vue'
import { Plus } from '@lucide/vue'
import { ElMessage } from 'element-plus'
import { useRoute, useRouter } from 'vue-router'
import { useAccessPreviewStore } from '@/core/access/accessPreview'
import { getPlatformWarehouse } from '@/modules/warehouses'
import { listSuppliers, type Supplier } from '@/modules/suppliers'
import { getApiErrorDetails, type ApiErrorDetails } from '@/shared/api/http'
import ApiErrorMeta from '@/shared/components/ApiErrorMeta.vue'
@@ -19,6 +20,7 @@ import OverseasDetailShell from '../components/OverseasDetailShell.vue'
import { businessOrderLink } from '../businessOrderLink'
import OrderReferenceText from '../components/OrderReferenceText.vue'
import WarehouseTransferFulfillmentPanel from '../components/WarehouseTransferFulfillmentPanel.vue'
import WarehouseTransferReceiptPanel from '../components/WarehouseTransferReceiptPanel.vue'
interface DetailField { label: string; value: string | number; wide?: boolean }
interface CargoRow { id: string; sku: string; productName: string; pallets: number; cartons: number; quantity: number; weight: number; volume: number }
@@ -31,6 +33,20 @@ const router = useRouter()
const access = useAccessPreviewStore()
const orderId = computed(() => String(route.params.orderId || ''))
const detail = ref<LastMileOrderDetailDto | null>(null)
const receiptSummary = ref<{ boxes: number; forecastBoxes: number; forecastSkus: number; skus: number; quantity: number; pallets: number; weight: number; volume: number; warehouseId?: string | null } | null>(null)
const resolvedWarehouseName = ref('')
const warehouseLookupFailed = ref(false)
const warehouseDisplayName = computed(() => detail.value?.order.overseasWarehouseName || resolvedWarehouseName.value || detail.value?.order.overseasWarehouseCode || (warehouseLookupFailed.value ? '仓库名称加载失败' : '—'))
watch(() => [detail.value?.order.overseasWarehouseId || receiptSummary.value?.warehouseId, detail.value?.order.overseasWarehouseName] as const, async ([warehouseId, warehouseName], _, onCleanup) => {
let current = true
onCleanup(() => { current = false })
resolvedWarehouseName.value = ''; warehouseLookupFailed.value = false
if (!warehouseId || warehouseName) return
try {
const result = await getPlatformWarehouse(String(warehouseId))
if (current) resolvedWarehouseName.value = result.warehouse.warehouseName || result.warehouse.warehouseCode
} catch { if (current) warehouseLookupFailed.value = true }
})
const loading = ref(false)
const saving = ref(false)
const loadError = ref<ApiErrorDetails | null>(null)
@@ -76,7 +92,7 @@ const orderFields = computed<DetailField[]>(() => {
{ label: '履约类型', value: text(order?.orderTypeName || lastMileOrderTypeLabel(order?.orderType)) },
{ label: '入仓码', value: text(order?.inboundCode) },
{ label: '货主', value: text(order?.customerName) },
{ label: '海外仓', value: text(order?.overseasWarehouseName) },
{ label: '海外仓', value: warehouseDisplayName.value },
{ label: '转仓目的仓', value: text(order?.transferWarehouseName) },
{ label: '目的仓', value: text(order?.fbaWarehouseCode || order?.transferWarehouseName) },
{ label: '是否暂扣', value: yesNo(order?.detained) },
@@ -115,9 +131,25 @@ const overviewFields = computed(() => {
const order = detail.value?.order
const warehouseTransfer = order?.orderType === 'WAREHOUSE_TRANSFER'
const hasReceivedPackages = warehouseTransfer && order?.receivedPackageCount != null
if (warehouseTransfer && receiptSummary.value) {
const summary = receiptSummary.value
return [
{ label: '客户', value: text(order?.customerName) },
{ label: '海外仓', value: warehouseDisplayName.value },
{ label: '预报箱数', value: `${summary.forecastBoxes} 箱` },
{ label: '预报 SKU', value: `${summary.forecastSkus} 种` },
{ label: '实收箱数', value: `${summary.boxes} 箱` },
{ label: '实收 SKU', value: `${summary.skus} 种` },
{ label: '实收件数', value: `${summary.quantity} 件` },
{ label: '总托数', value: `${summary.pallets} 托` },
{ label: '总重量', value: `${summary.weight.toFixed(3)} kg` },
{ label: '总体积', value: `${summary.volume.toFixed(6)} m³` },
{ label: '最近更新', value: formatOverseasDateTime(order?.updatedAt) },
]
}
return [
{ label: '客户', value: text(order?.customerName) },
{ label: '海外仓', value: text(order?.overseasWarehouseName) },
{ label: '海外仓', value: warehouseDisplayName.value },
...(isReturnOrder.value ? [{ label: showReturnForecast.value ? '预报总件数' : '实收总件数', value: `${showReturnForecast.value ? detail.value?.returnReceipt?.order.expectedQuantity ?? order?.productQuantity ?? 0 : detail.value?.returnReceipt?.order.receivedQuantity ?? order?.productQuantity ?? 0} 件` }]
: [{ label: warehouseTransfer ? (hasReceivedPackages ? '实收总箱数' : '客户预报总箱数') : '总箱数', value: (hasReceivedPackages ? order?.receivedPackageCount : order?.packageCount) == null ? '—' : `${hasReceivedPackages ? order?.receivedPackageCount : order?.packageCount} 箱` }]),
{ label: '总托数', value: order?.palletCount == null ? '—' : order.palletCount + ' 托' },
@@ -248,7 +280,7 @@ onMounted(load)
<div v-for="field in basicFields" :key="field.label" :class="{ wide: field.wide }"><dt>{{ field.label }}</dt><dd :class="{ 'detail-field-value--business': isSingleLineBusinessField(field), 'package-difference--nonzero': isNonzeroDifference(field) }" :title="isSingleLineBusinessField(field) ? String(field.value) : undefined">{{ field.value }}</dd></div>
</dl>
</el-tab-pane>
<el-tab-pane name="cargo"><template #label><span class="density-tab-icon" aria-hidden="true">📦</span><span>货物信息</span></template><section class="table-panel"><DataTable :data="cargo" :columns="cargoColumns" column-settings-mode="hide" row-key="id" :empty-text="isReturnOrder ? '暂无客户预报货物明细' : '暂无货物明细'"></DataTable></section></el-tab-pane>
<el-tab-pane name="cargo"><template #label><span class="density-tab-icon" aria-hidden="true">📦</span><span>货物信息</span></template><section class="table-panel"><WarehouseTransferReceiptPanel v-if="detail?.order.orderType === 'WAREHOUSE_TRANSFER' && activeTab === 'cargo'" :customer-id="String(detail.order.customerId || '')" :transfer-no="String(detail.order.sourceOrderNo || '')" @summary="receiptSummary = $event" /><DataTable v-else-if="detail?.order.orderType !== 'WAREHOUSE_TRANSFER'" :data="cargo" :columns="cargoColumns" column-settings-mode="hide" row-key="id" :empty-text="isReturnOrder ? '暂无客户预报货物明细' : '暂无货物明细'"></DataTable></section></el-tab-pane>
<el-tab-pane v-if="detail?.order.orderType === 'RETURN_RELABEL'" name="return-receipt"><template #label><span class="density-tab-icon" aria-hidden="true">📦</span><span>退货签收与箱内清点</span></template>
<section v-if="detail.returnReceipt" class="table-panel" aria-label="退货签收与箱内清点">
<h3>{{ detail.returnReceipt.order.returnNo }} · 实收 {{ detail.returnReceipt.order.receivedQuantity }} / 预报 {{ detail.returnReceipt.order.expectedQuantity }} 件</h3>