统一履约状态和节点日志展示并整理装柜与尾程界面
This commit is contained in:
@@ -10,6 +10,7 @@ import { useAuthStore } from '@/core/auth/auth'
|
||||
import { ApiResponseError } from '@/shared/api/http'
|
||||
import { h } from 'vue'
|
||||
import AppDialog from '@/shared/components/AppDialog.vue'
|
||||
import type { CarrierScheduleNodeFact } from '../types'
|
||||
|
||||
enableAutoUnmount(afterEach)
|
||||
|
||||
@@ -251,6 +252,39 @@ describe('CarrierScheduleQueryPanel loading states', () => {
|
||||
expect(wrapper.text()).toContain('船运跟踪 · HEDE · hede-hold')
|
||||
})
|
||||
|
||||
it.each([false, true])('shows recorded usernames for human nodes and retains automatic sources (dense=%s)', async dense => {
|
||||
const manual: CarrierScheduleNodeFact = {
|
||||
id: 'manual-1', providerCode: 'MANUAL', providerEventId: 'manual-event-1', providerNodeCode: 'IN_TRANSIT',
|
||||
standardNode: 'IN_TRANSIT', nodeSequence: 50, observedAt: '2026-10-28T10:00:00',
|
||||
actualAt: '2026-10-28T10:00:00', sourceReference: 'MANUAL:1000001',
|
||||
rawPayloadHash: 'a'.repeat(64), operatorName: 'platform_admin',
|
||||
}
|
||||
vi.mocked(listCarrierScheduleQueries).mockResolvedValue({ records: [], page: 1, size: 200, total: 0, pages: 0 })
|
||||
vi.mocked(getContainerShippingNodes).mockResolvedValue({ containerBookingId: '3', nodes: [
|
||||
manual,
|
||||
{ ...manual, id: 'outbound-1', providerCode: 'FIRSTLEG', providerEventId: 'OUTBOUND_CONFIRM', providerNodeCode: 'OUTBOUND_CONFIRM', standardNode: 'DEPARTED_WAREHOUSE', nodeSequence: 10, actualAt: '2026-10-28T09:00:00', sourceReference: 'OUTBOUND_CONFIRM', operatorName: 'outbound_operator' },
|
||||
{ ...manual, id: 'automatic-1', providerCode: 'COSCO', providerEventId: 'carrier-event-1', providerNodeCode: 'ARRIVED', standardNode: 'ARRIVED_DESTINATION_PORT', nodeSequence: 60, actualAt: '2026-10-28T11:00:00', sourceReference: 'carrier-event-1', operatorName: null },
|
||||
{ ...manual, id: 'missing-1', providerEventId: 'manual-event-2', providerNodeCode: 'PICKED_UP', standardNode: 'PICKED_UP', nodeSequence: 90, actualAt: '2026-10-28T12:00:00', sourceReference: 'MANUAL:2000002', operatorName: null },
|
||||
] })
|
||||
const wrapper = mount(CarrierScheduleQueryPanel, {
|
||||
props: { dense, hideTechnicalDetails: true, oceanBookingId: '2', containerBookingId: '3', warehouseTimeline: [{ eventType: 'PDA_COMPLETE_LOADING', operatorName: 'pda_operator', occurredAt: '2026-10-28T08:00:00' }] },
|
||||
global: { plugins: [ElementPlus, pinia] },
|
||||
})
|
||||
await flushPromises()
|
||||
const rows = wrapper.findAll(dense ? '.fulfillment-table tbody tr' : '.fulfillment-timeline li')
|
||||
const source = (title: string) => {
|
||||
const row = rows.find(item => item.get('strong').text() === title)!
|
||||
return dense ? row.findAll('td').at(-1)!.text() : row.get('small').text()
|
||||
}
|
||||
expect(source('海运途中')).toBe('platform_admin')
|
||||
expect(source('已离开国内仓库')).toBe('outbound_operator')
|
||||
expect(source('完成装柜')).toBe('pda_operator')
|
||||
expect(source('船舶已到目的港')).toBe('COSCO · carrier-event-1')
|
||||
expect(source('已提柜')).toBe('操作人未记录')
|
||||
expect(rows.map(row => row.text()).join('|')).not.toContain('MANUAL:')
|
||||
expect(rows.map(row => row.text()).join('|')).not.toContain('OUTBOUND_CONFIRM')
|
||||
})
|
||||
|
||||
it('merges warehouse, actual, estimated, manual and exception facts with stable ordering and deduplication', async () => {
|
||||
vi.mocked(listCarrierScheduleQueries).mockResolvedValue({ records: [{ ...queryView, query: { ...queryView.query, resultPortScheduleId: null }, expired: false }], page: 1, size: 200, total: 1, pages: 1 })
|
||||
const actual = { id: '61', providerCode: 'COSCO', providerEventId: 'actual-61', providerNodeCode: 'LOAD', standardNode: 'LOADED_ON_VESSEL' as const, nodeSequence: 30, observedAt: '2026-08-21T10:05:00', estimatedAt: null, actualAt: '2026-08-21T10:00:00', locationCode: 'CNNGB', locationName: '宁波港', sourceReference: 'actual-source', rawPayloadHash: 'e'.repeat(64), createdAt: '2026-08-21T10:06:00' }
|
||||
|
||||
Reference in New Issue
Block a user