优化
This commit is contained in:
@@ -98,6 +98,23 @@ describe('unified orders API contract', () => {
|
||||
expect(http.post).toHaveBeenCalledWith('/platform/v1/orders/first-leg/90001/approve', undefined, { params: { tenantId: '208' } })
|
||||
})
|
||||
|
||||
it('keeps split rows bound to their own current-container schedule and preserves effective-time semantics', async () => {
|
||||
vi.mocked(http.get).mockResolvedValue({ data: {
|
||||
records: [
|
||||
{ orderId: '90001', tenantId: '208', inboundId: '701', mainStatus: 'LOADED', relation: { role: 'PARENT', rootOrderId: '90001', parentOrderId: null, splitSequence: null, childCount: 1 }, transport: { cabinetNo: 'OLD-CAB', estimatedSailingAt: '2026-08-20T08:00:00', actualSailingAt: '2026-08-21T09:00:00', effectiveSailingAt: '2026-08-21T09:00:00', effectiveSailingAtEstimated: false, estimatedPortArrivalAt: '2026-09-01T08:00:00', actualPortArrivalAt: null, effectivePortArrivalAt: '2026-09-01T08:00:00', effectivePortArrivalAtEstimated: true } },
|
||||
{ orderId: '90002', tenantId: '208', inboundId: '702', mainStatus: 'LOADED', relation: { role: 'CHILD', rootOrderId: '90001', parentOrderId: '90001', splitSequence: 1, childCount: 0 }, transport: { cabinetNo: 'NEW-CAB', estimatedSailingAt: '2026-08-25T08:00:00', actualSailingAt: null, effectiveSailingAt: '2026-08-25T08:00:00', effectiveSailingAtEstimated: true, estimatedPortArrivalAt: null, actualPortArrivalAt: null, effectivePortArrivalAt: null, effectivePortArrivalAtEstimated: null } },
|
||||
],
|
||||
page: 1, size: 20, total: 2, pages: 1,
|
||||
} })
|
||||
|
||||
const result = await listPlatformUnifiedOrders({ page: 1, size: 20 })
|
||||
|
||||
expect(result.records.map((row) => ({ inboundId: row.inboundId, transport: row.transport }))).toMatchObject([
|
||||
{ inboundId: '701', transport: { cabinetNo: 'OLD-CAB', effectiveSailingAt: '2026-08-21T09:00:00', effectiveSailingAtEstimated: false, effectivePortArrivalAt: '2026-09-01T08:00:00', effectivePortArrivalAtEstimated: true } },
|
||||
{ inboundId: '702', transport: { cabinetNo: 'NEW-CAB', effectiveSailingAt: '2026-08-25T08:00:00', effectiveSailingAtEstimated: true, effectivePortArrivalAt: null, effectivePortArrivalAtEstimated: null } },
|
||||
])
|
||||
})
|
||||
|
||||
it('uses the tenant-scoped batch approval contract and preserves long IDs', async () => {
|
||||
vi.mocked(http.post).mockResolvedValue({ data: {
|
||||
total: 2, successCount: 1, failureCount: 1,
|
||||
|
||||
@@ -57,8 +57,14 @@ export interface UnifiedOrderTransportSummary {
|
||||
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 UnifiedOrderRelationSummary {
|
||||
|
||||
@@ -100,7 +100,7 @@ describe('UnifiedOrdersView contract', () => {
|
||||
'actualVolumeCbm', 'totalNetWeightKg', 'chargeableWeightKg', 'chargeableVolumeCbm', 'expectedShipDate',
|
||||
'expectedArrivalAt', 'feeReviewStatus', 'skuNos', 'totalQuantity', 'expressCompany', 'expressTrackingNo',
|
||||
'shipmentId', 'referenceId', 'containerNo', 'cabinetNo',
|
||||
'sailingAt', 'portArrivalAt', 'customerRemark', 'latestInternalNote', 'createdAt', 'updatedAt',
|
||||
'effectiveSailingAt', 'effectivePortArrivalAt', 'customerRemark', 'latestInternalNote', 'createdAt', 'updatedAt',
|
||||
]
|
||||
for (const key of expectedColumnKeys) expect(columnSource).toContain(`key: '${key}'`)
|
||||
for (const uncontractedKey of [
|
||||
@@ -115,10 +115,18 @@ describe('UnifiedOrdersView contract', () => {
|
||||
expect(columnSource).not.toContain("label: '配载状态'")
|
||||
expect(columnSource).not.toContain("key: 'recommendedPalletType'")
|
||||
expect(columnSource).toContain("key: 'requestedPickupAt', prop: 'requestedPickupAt', label: '预约提货时间'")
|
||||
expect(columnSource.match(/defaultVisible: false/g)).toHaveLength(16)
|
||||
expect(columnSource.match(/defaultVisible: false/g)).toHaveLength(14)
|
||||
expect(columnSource).toContain("key: 'effectiveSailingAt', prop: 'effectiveSailingAt', label: '开船时间', minWidth: 180, slotName: 'field' }")
|
||||
expect(columnSource).toContain("key: 'effectivePortArrivalAt', prop: 'effectivePortArrivalAt', label: '到港时间', minWidth: 180, slotName: 'field' }")
|
||||
expect(source).toContain('preference-key="unified-orders-table-v2"')
|
||||
expect(source).toContain("property === 'requestedPickupAt' && (row.pickupMethod !== 'PICKUP' || !value)")
|
||||
expect(source).toContain("property === 'skuNos' ? value.join('、')")
|
||||
expect(source).not.toContain('<dt>推荐托盘类型</dt>')
|
||||
expect(source).toContain("estimated === true ? '预计 ' : ''")
|
||||
expect(source).toContain('row.transport?.effectiveSailingAtEstimated')
|
||||
expect(source).toContain('row.transport?.effectivePortArrivalAtEstimated')
|
||||
expect(source).not.toContain("transport.sailingAt")
|
||||
expect(source).not.toContain("transport.portArrivalAt")
|
||||
expect(source).toContain('.table-card { overflow: hidden; }')
|
||||
expect(source).toContain('slotName: \'measurementReviewStatus\'')
|
||||
expect(source).toContain('unifiedOrderReviewStatusLabel(value, \'未录入\')')
|
||||
|
||||
@@ -266,8 +266,8 @@ const columns = computed<DataTableColumn[]>(() => [
|
||||
{ key: 'referenceId', prop: 'referenceId', label: 'Reference ID', minWidth: 160, slotName: 'field', defaultVisible: false },
|
||||
{ key: 'containerNo', prop: 'containerNo', label: '真实集装箱号', minWidth: 160, slotName: 'field', defaultVisible: false },
|
||||
{ key: 'cabinetNo', prop: 'cabinetNo', label: '内部柜次号', minWidth: 150, slotName: 'field', defaultVisible: false },
|
||||
{ key: 'sailingAt', prop: 'sailingAt', label: '开船时间', minWidth: 150, slotName: 'field', defaultVisible: false },
|
||||
{ key: 'portArrivalAt', prop: 'portArrivalAt', label: '到港时间', minWidth: 150, slotName: 'field', defaultVisible: false },
|
||||
{ key: 'effectiveSailingAt', prop: 'effectiveSailingAt', label: '开船时间', minWidth: 180, slotName: 'field' },
|
||||
{ key: 'effectivePortArrivalAt', prop: 'effectivePortArrivalAt', label: '到港时间', minWidth: 180, slotName: 'field' },
|
||||
{ key: 'customerRemark', prop: 'customerRemark', label: '客户备注', minWidth: 180, slotName: 'field', defaultVisible: false },
|
||||
{ key: 'latestInternalNote', prop: 'latestInternalNote', label: '内部备注', minWidth: 180, slotName: 'field', defaultVisible: false },
|
||||
{ key: 'createdAt', prop: 'createdAt', label: '创建时间', minWidth: 150, slotName: 'field', defaultVisible: false },
|
||||
@@ -370,13 +370,18 @@ function displayBoolean(value: boolean | null | undefined) {
|
||||
return value === null || value === undefined ? '—' : value ? '是' : '否'
|
||||
}
|
||||
|
||||
function formatEffectiveTransportTime(value: string | null | undefined, estimated: boolean | null | undefined) {
|
||||
if (!value) return '—'
|
||||
return `${estimated === true ? '预计 ' : ''}${formatDate(value)}`
|
||||
}
|
||||
|
||||
function displayField(row: UnifiedOrderResource, property: string | undefined) {
|
||||
if (!property) return '—'
|
||||
const source = row as unknown as Record<string, unknown>
|
||||
const nested: Record<string, string> = {
|
||||
measurementReviewStatus: 'measurement.reviewStatus', expectedArrivalAt: 'expectedArrivalAt',
|
||||
containerNo: 'transport.containerNo', cabinetNo: 'transport.cabinetNo',
|
||||
sailingAt: 'transport.sailingAt', portArrivalAt: 'transport.portArrivalAt',
|
||||
effectiveSailingAt: 'transport.effectiveSailingAt', effectivePortArrivalAt: 'transport.effectivePortArrivalAt',
|
||||
chargeableWeightKg: 'measurement.chargeableWeightKg', measurementCompletedAt: 'measurement.completedAt',
|
||||
expressTrackingNo: 'delivery.trackingNo',
|
||||
}
|
||||
@@ -386,6 +391,8 @@ function displayField(row: UnifiedOrderResource, property: string | undefined) {
|
||||
), source)
|
||||
if ((value === null || value === undefined || value === '') && property === 'internalOrderStatus') value = source.mainStatus
|
||||
if (property === 'requestedPickupAt' && (row.pickupMethod !== 'PICKUP' || !value)) return '—'
|
||||
if (property === 'effectiveSailingAt') return formatEffectiveTransportTime(row.transport?.effectiveSailingAt, row.transport?.effectiveSailingAtEstimated)
|
||||
if (property === 'effectivePortArrivalAt') return formatEffectiveTransportTime(row.transport?.effectivePortArrivalAt, row.transport?.effectivePortArrivalAtEstimated)
|
||||
if (value === null || value === undefined || value === '') return '—'
|
||||
if (typeof value === 'boolean') return value ? '是' : '否'
|
||||
if (Array.isArray(value)) return value.length ? (property === 'skuNos' ? value.join('、') : `${value.length} 个文件`) : '—'
|
||||
@@ -942,8 +949,8 @@ onBeforeUnmount(() => {
|
||||
<div><dt>真实集装箱号</dt><dd>{{ detail.summary.transport?.containerNo || '—' }}</dd></div>
|
||||
<div><dt>内部柜次号</dt><dd>{{ detail.summary.transport?.cabinetNo || '—' }}</dd></div>
|
||||
<div><dt>船名 / 航次</dt><dd>{{ detail.summary.transport?.vesselName || '—' }} / {{ detail.summary.transport?.voyageNo || '—' }}</dd></div>
|
||||
<div><dt>开船时间</dt><dd>{{ formatDate(detail.summary.transport?.sailingAt) }}</dd></div>
|
||||
<div><dt>到港时间</dt><dd>{{ formatDate(detail.summary.transport?.portArrivalAt) }}</dd></div>
|
||||
<div><dt>开船时间</dt><dd>{{ formatEffectiveTransportTime(detail.summary.transport?.effectiveSailingAt, detail.summary.transport?.effectiveSailingAtEstimated) }}</dd></div>
|
||||
<div><dt>到港时间</dt><dd>{{ formatEffectiveTransportTime(detail.summary.transport?.effectivePortArrivalAt, detail.summary.transport?.effectivePortArrivalAtEstimated) }}</dd></div>
|
||||
<div><dt>配载状态</dt><dd>{{ unifiedOrderLoadingStatusLabel(detail.summary.transport?.loadingStatus) }}</dd></div>
|
||||
<div><dt>目的仓类型</dt><dd>{{ firstLegDestinationTypeLabel(detail.order.order?.destinationType) }}</dd></div>
|
||||
<div><dt>是否整柜</dt><dd>{{ firstLegContainerModeLabel(detail.order.order?.containerMode) }}</dd></div>
|
||||
@@ -1168,7 +1175,7 @@ onBeforeUnmount(() => {
|
||||
|
||||
<template #default>
|
||||
<div v-if="!loadError" id="unified-orders-results" class="surface-panel table-panel table-card" role="tabpanel">
|
||||
<DataTable v-model:page="page" v-model:page-size="pageSize" :data="records" :columns="columns" preference-key="unified-orders-table" :loading="loading" :total="total" :page-sizes="[20, 50, 100]" row-key="orderId" border stripe empty-text="暂无符合条件的订单" aria-label="统一订单列表" @selection-change="handleApprovalSelectionChange">
|
||||
<DataTable v-model:page="page" v-model:page-size="pageSize" :data="records" :columns="columns" preference-key="unified-orders-table-v2" :loading="loading" :total="total" :page-sizes="[20, 50, 100]" row-key="orderId" border stripe empty-text="暂无符合条件的订单" aria-label="统一订单列表" @selection-change="handleApprovalSelectionChange">
|
||||
<template #toolbar-actions>
|
||||
<el-button v-if="can('platform:order:approve')" type="success" :loading="batchApprovalSaving" :disabled="loading || !selectedApprovalOrders.length" @click="batchApproveOrders"><CircleCheck :size="16" />批量审核通过<span v-if="selectedApprovalOrders.length">({{ selectedApprovalOrders.length }})</span></el-button>
|
||||
<el-button v-if="can('platform:firstleg:smart-loading:list')" type="primary" @click="smartLoadingOpen = true"><PackageOpen :size="16" />智能装柜</el-button>
|
||||
|
||||
Reference in New Issue
Block a user