feat(admin): 实现排车候选分页并修正报关批次参数

This commit is contained in:
2026-10-21 07:19:57 +08:00
parent 1c771066ea
commit 2f9a3330e5
21 changed files with 411 additions and 102 deletions
@@ -16,7 +16,7 @@ vi.mock('../api/dispatches', () => ({ getDispatch: vi.fn(), getDispatchItems: vi
interface State { submit(): Promise<void>; retryError(): Promise<void>; measurements: Array<{ weightKg: number | null; volumeCbm: number | null; deliveryIndex: number | null; palletSpaces: number | null }>; form: { reason: string; decision: string; remark: string }; detail: DispatchDetail }
it('adds all hidden shared-pallet sources once and rejects a changed group composition before another command', async () => {
const [first, second] = sharedPalletCandidates()
vi.mocked(api.listDispatches).mockResolvedValue(page()); vi.mocked(api.listDispatchCandidates).mockResolvedValue({ candidates: [first!], complete: true, snapshotAt: '' })
vi.mocked(api.listDispatches).mockResolvedValue(page()); vi.mocked(api.listDispatchCandidates).mockResolvedValue({ total: 1, page: 1, size: 20, pages: 1, candidates: [first!], complete: true, snapshotAt: '' })
const { wrapper, state } = await open('ADD_CARGO')
const selection = wrapper.vm as unknown as { selectCargo(row: NonNullable<typeof first>, checked: boolean): void }
selection.selectCargo(first!, true)
@@ -25,6 +25,7 @@ it('adds all hidden shared-pallet sources once and rejects a changed group compo
first!.selectionGroup.complete = false; selection.selectCargo(first!, false)
await state.submit(); expect(api.addDispatchCargo).not.toHaveBeenCalled(); expect(wrapper.text()).toContain('请选择来源完整')
first!.selectionGroup.complete = true
await (wrapper.vm as unknown as { searchCargo(): Promise<void> }).searchCargo()
selection.selectCargo(first!, true)
vi.mocked(api.addDispatchCargo).mockImplementation(async (_, request) => command('ADD_CARGO', request.idempotencyKey)); vi.mocked(api.getDispatch).mockResolvedValue(detail({ recordVersion: 1 }))
await state.submit(); expect(api.addDispatchCargo).toHaveBeenCalledExactlyOnceWith('PC/1', expect.objectContaining({ cargo: [first!.selection, second!.selection] }))
@@ -32,7 +33,7 @@ it('adds all hidden shared-pallet sources once and rejects a changed group compo
})
it.each([0, '0', '9007199254740993'])('adds cargo with original batch version %s while keeping the parent dispatch version independent', async recordVersion => {
const value = candidate({ appointmentBatches: [{ batchKey: 'FBA_APPOINTMENT:1', batchNo: 'AP-1', customerId: 'C1', recordVersion, sources: [{ sourceKey: 'ORDER:1', destinationKey: 'FBA:ONT8', packageCount: 2 }] }] })
vi.mocked(api.listDispatches).mockResolvedValue(page()); vi.mocked(api.listDispatchCandidates).mockResolvedValue({ candidates: [value], complete: true, snapshotAt: '' })
vi.mocked(api.listDispatches).mockResolvedValue(page()); vi.mocked(api.listDispatchCandidates).mockResolvedValue({ total: 1, page: 1, size: 20, pages: 1, candidates: [value], complete: true, snapshotAt: '' })
const { wrapper, state } = await open('ADD_CARGO')
try {
(wrapper.vm as unknown as { selectCargo(row: typeof value, checked: boolean): void }).selectCargo(value, true)
@@ -43,7 +44,7 @@ it.each([0, '0', '9007199254740993'])('adds cargo with original batch version %s
})
it('blocks cargo addition when the authoritative batch version is absent', async () => {
const value = candidate({ appointmentBatches: [{ batchKey: 'FBA_APPOINTMENT:1', batchNo: 'AP-1', customerId: 'C1', recordVersion: undefined as unknown as number, sources: [] }] })
vi.mocked(api.listDispatches).mockResolvedValue(page()); vi.mocked(api.listDispatchCandidates).mockResolvedValue({ candidates: [value], complete: true, snapshotAt: '' })
vi.mocked(api.listDispatches).mockResolvedValue(page()); vi.mocked(api.listDispatchCandidates).mockResolvedValue({ total: 1, page: 1, size: 20, pages: 1, candidates: [value], complete: true, snapshotAt: '' })
const { wrapper, state } = await open('ADD_CARGO')
try {
(wrapper.vm as unknown as { selectCargo(row: typeof value, checked: boolean): void }).selectCargo(value, true); await state.submit()
@@ -234,3 +235,30 @@ it('describes a proven success with a failed GET as completed and retries only i
expect(wrapper.emitted('saved')).toHaveLength(1)
wrapper.unmount()
})
it('preserves selected complete groups across pages and submits hidden sources with their original versions', async () => {
const [first, hidden] = sharedPalletCandidates(), second = candidate({ sourceKey: 'next-page' })
vi.mocked(api.listDispatches).mockResolvedValue(page())
vi.mocked(api.listDispatchCandidates).mockImplementation(async query => ({ candidates: query.page === 2 ? [second] : [first!], page: query.page!, size: query.size!, total: 21, pages: 2, complete: false, snapshotAt: '' }))
const { wrapper, state } = await open('ADD_CARGO')
try {
const selection = wrapper.vm as unknown as { selectCargo(row: typeof second, checked: boolean): void; changeCandidatePage(value: number): void; candidatePageNumber: number; candidateKeyword: string; searchCargo(): Promise<void> }
selection.selectCargo(first!, true); selection.changeCandidatePage(2); await flushPromises(); selection.selectCargo(second, true); await flushPromises()
expect(wrapper.text()).toContain('已选择 2 个完整来源组')
selection.candidateKeyword = 'next'; await selection.searchCargo()
expect(api.listDispatchCandidates).toHaveBeenLastCalledWith(expect.objectContaining({ page: 1, orderNo: 'next' }))
expect(api.listDispatches).toHaveBeenCalledTimes(1)
vi.mocked(api.addDispatchCargo).mockImplementation(async (_, request) => command('ADD_CARGO', request.idempotencyKey)); vi.mocked(api.getDispatch).mockResolvedValue(detail({ recordVersion: 1 }))
await state.submit()
expect(api.addDispatchCargo).toHaveBeenCalledWith('PC/1', expect.objectContaining({ cargo: [first!.selection, hidden!.selection, second.selection] }))
} finally { wrapper.unmount() }
})
it('shows authoritative warehouse lookup failures when retrying a cargo search', async () => {
vi.mocked(api.listDispatches).mockRejectedValue(new Error('warehouse lookup unavailable'))
const { wrapper } = await open('ADD_CARGO')
try {
await (wrapper.vm as unknown as { searchCargo(): Promise<void> }).searchCargo()
expect(wrapper.text()).toContain('货物候选读取失败')
expect(api.listDispatchCandidates).not.toHaveBeenCalled()
} finally { wrapper.unmount() }
})