fix(client): render estimated sailing fields from unified orders

This commit is contained in:
2026-09-28 07:45:57 +08:00
parent 8c8fdbc2ab
commit 983df11081
6 changed files with 38 additions and 11 deletions
+8 -2
View File
@@ -48,8 +48,14 @@ export interface MerchantUnifiedTransportSummary {
cabinetNo: string | null
vesselName: string | null
voyageNo: string | null
sailingAt: string | null
portArrivalAt: string | null
estimatedSailingAt: string | null
actualSailingAt: string | null
effectiveSailingAt: string | null
effectiveSailingAtEstimated: boolean | null
estimatedPortArrivalAt: string | null
actualPortArrivalAt: string | null
effectivePortArrivalAt: string | null
effectivePortArrivalAtEstimated: boolean | null
}
export interface MerchantUnifiedOrderRow {
@@ -1,11 +1,32 @@
import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import test from 'node:test'
import { runInNewContext } from 'node:vm'
import ts from 'typescript'
const storeSource = readFileSync(new URL('./firstLegOrders.ts', import.meta.url), 'utf8')
const apiSource = readFileSync(new URL('../api/firstLeg.ts', import.meta.url), 'utf8')
const unifiedApiSource = readFileSync(new URL('../api/unifiedOrders.ts', import.meta.url), 'utf8')
test('renders estimated sailing from the current API without substituting actual departure', () => {
const mapper = storeSource.slice(storeSource.indexOf('function businessSummary('), storeSource.indexOf('\nconst unifiedActionLabels'))
const javascript = ts.transpileModule(mapper, { compilerOptions: { target: ts.ScriptTarget.ES2022 } }).outputText
const map = runInNewContext(`${javascript}; businessSummary`)
const transport = {
vesselName: 'REASSIGNED', voyageNo: 'V02',
estimatedSailingAt: '2026-10-07T08:00:00', actualSailingAt: '2026-10-08T09:00:00',
effectiveSailingAt: '2026-10-08T09:00:00', effectiveSailingAtEstimated: false,
estimatedPortArrivalAt: '2026-10-27T08:00:00', actualPortArrivalAt: null,
}
const mapped = map({ transport })
assert.equal(mapped.estimatedSailingAt, transport.estimatedSailingAt)
assert.equal(mapped.estimatedPortArrivalAt, transport.estimatedPortArrivalAt)
assert.equal(mapped.vesselName, 'REASSIGNED')
assert.equal(mapped.voyageNo, 'V02')
assert.equal(map().estimatedSailingAt, '')
assert.equal(map({ transport: null }).estimatedPortArrivalAt, '')
})
test('maps the first-leg aggregate list fields without loading detail rows', () => {
assert.match(apiSource, /interface MerchantFirstLegOrderRow/)
assert.match(apiSource, /skuNos: string \| string\[\] \| null/)
@@ -220,8 +220,8 @@ function businessSummary(summary?: MerchantUnifiedOrderRow): FirstLegOrder['busi
cabinetNo: summary?.transport?.cabinetNo ?? '',
vesselName: summary?.transport?.vesselName ?? '',
voyageNo: summary?.transport?.voyageNo ?? '',
sailingAt: summary?.transport?.sailingAt ?? '',
portArrivalAt: summary?.transport?.portArrivalAt ?? '',
estimatedSailingAt: summary?.transport?.estimatedSailingAt ?? '',
estimatedPortArrivalAt: summary?.transport?.estimatedPortArrivalAt ?? '',
inboundId: summary?.inboundId == null ? '' : String(summary.inboundId),
inboundVersion: summary?.inboundVersion ?? null,
inboundCode: summary?.inboundCode ?? '',
+2 -2
View File
@@ -87,8 +87,8 @@ export interface FirstLegOrderBusinessSummary {
cabinetNo: string
vesselName: string
voyageNo: string
sailingAt: string
portArrivalAt: string
estimatedSailingAt: string
estimatedPortArrivalAt: string
inboundId: string
inboundVersion: number | null
inboundCode: string
@@ -729,7 +729,7 @@ test('shows authoritative warehouse, loading and sailing fields on desktop and n
}
assert.match(source, /businessSummary\.cabinetNo \|\| .*businessSummary\.containerNo/)
assert.match(source, /businessSummary\.vesselName.*businessSummary\.voyageNo/s)
assert.match(source, /businessSummary\.sailingAt/)
assert.match(source, /businessSummary\.estimatedSailingAt/)
})
test('loads complete order detail before editing and rejects an unavailable historical warehouse', () => {
@@ -144,7 +144,7 @@ const selectableOrderColumns: DataTableColumn[] = [
{ key: 'expectedShipDate', label: '期望出运', width: 132, align: 'center', headerAlign: 'center', slotName: 'expectedShipDate' },
{ key: 'cabinetNo', label: '柜号 / 柜次', width: 150, align: 'left', headerAlign: 'left', showOverflowTooltip: true, slotName: 'cabinetNo' },
{ key: 'vesselVoyage', label: '船名 / 航次', width: 160, align: 'left', headerAlign: 'left', showOverflowTooltip: true, slotName: 'vesselVoyage' },
{ key: 'sailingAt', label: '预计船期', width: 164, align: 'center', headerAlign: 'center', slotName: 'sailingAt' },
{ key: 'estimatedSailingAt', label: '预计船期', width: 164, align: 'center', headerAlign: 'center', slotName: 'estimatedSailingAt' },
{ key: 'createdAt', label: '创建时间', width: 164, align: 'center', headerAlign: 'center', slotName: 'createdAt' },
{ key: 'updatedAt', label: '更新时间', width: 164, align: 'center', headerAlign: 'center', slotName: 'updatedAt' },
]
@@ -572,7 +572,7 @@ const progressSummaryItems = computed(() => {
const cabinetProgressItems = computed(() => {
const summary = detailOrder.value?.businessSummary
if (!summary || (!summary.cabinetNo && !summary.containerNo)) return []
return [{ cabinetId: summary.cabinetNo || summary.containerNo, cabinetNo: summary.cabinetNo || summary.containerNo, status: summary.loadingStatus || '待更新', vesselName: summary.vesselName, voyageNo: summary.voyageNo, sailingAt: summary.sailingAt }]
return [{ cabinetId: summary.cabinetNo || summary.containerNo, cabinetNo: summary.cabinetNo || summary.containerNo, status: summary.loadingStatus || '待更新', vesselName: summary.vesselName, voyageNo: summary.voyageNo, estimatedSailingAt: summary.estimatedSailingAt }]
})
const selectedWarehouse = computed(() => availableWarehouses.value.find((item) => item.id === form.receivingWarehouseId))
const selectedDestinationWarehouse = computed(() => store.warehouses.find((item) => item.id === form.destinationWarehouseId && item.warehouseType === form.destinationType))
@@ -2147,7 +2147,7 @@ async function searchOrders(model: QueryModel) {
<template #expectedShipDate="{ row }">{{ (row as FirstLegOrder).expectedDepartureDate ? formatDateTime((row as FirstLegOrder).expectedDepartureDate) : '-' }}</template>
<template #cabinetNo="{ row }"><span class="table-ellipsis table-code" :title="displayListValue((row as FirstLegOrder).businessSummary.cabinetNo || (row as FirstLegOrder).businessSummary.containerNo)">{{ displayListValue((row as FirstLegOrder).businessSummary.cabinetNo || (row as FirstLegOrder).businessSummary.containerNo) }}</span></template>
<template #vesselVoyage="{ row }"><span class="table-ellipsis" :title="displayListValue([(row as FirstLegOrder).businessSummary.vesselName, (row as FirstLegOrder).businessSummary.voyageNo].filter(Boolean).join(' · '))">{{ displayListValue([(row as FirstLegOrder).businessSummary.vesselName, (row as FirstLegOrder).businessSummary.voyageNo].filter(Boolean).join(' · ')) }}</span></template>
<template #sailingAt="{ row }">{{ (row as FirstLegOrder).businessSummary.sailingAt ? formatDateTime((row as FirstLegOrder).businessSummary.sailingAt) : '-' }}</template>
<template #estimatedSailingAt="{ row }">{{ (row as FirstLegOrder).businessSummary.estimatedSailingAt ? formatDateTime((row as FirstLegOrder).businessSummary.estimatedSailingAt) : '-' }}</template>
<template #createdAt="{ row }">{{ formatDateTime((row as FirstLegOrder).createdAt) }}</template>
<template #updatedAt="{ row }">{{ formatDateTime((row as FirstLegOrder).updatedAt) }}</template>
<template #empty>
@@ -2395,7 +2395,7 @@ async function searchOrders(model: QueryModel) {
<el-table v-if="cabinetProgressItems.length" :data="cabinetProgressItems" border size="small" class="cabinet-progress-table" row-key="cabinetId">
<el-table-column prop="cabinetNo" label="柜号 / 柜次" min-width="150"><template #default="{ row }"><span class="table-code">{{ row.cabinetNo || '—' }}</span></template></el-table-column>
<el-table-column label="船名 / 航次" min-width="180"><template #default="{ row }">{{ [row.vesselName, row.voyageNo].filter(Boolean).join(' · ') || '待更新' }}</template></el-table-column>
<el-table-column label="预计船期" width="170"><template #default="{ row }">{{ row.sailingAt ? formatDateTime(row.sailingAt) : '待更新' }}</template></el-table-column>
<el-table-column label="预计船期" width="170"><template #default="{ row }">{{ row.estimatedSailingAt ? formatDateTime(row.estimatedSailingAt) : '待更新' }}</template></el-table-column>
<el-table-column prop="status" label="当前节点" width="120"><template #default="{ row }"><el-tag size="small" effect="plain">{{ row.status || '待更新' }}</el-tag></template></el-table-column>
</el-table>
<p v-else class="detail-inline-empty">尚未分配柜次,后续柜次节点将在这里同步。</p>