import { flushPromises, shallowMount } from '@vue/test-utils' import { beforeEach, describe, expect, it, vi } from 'vitest' import { listPlatformUnifiedOrders, updatePlatformUnifiedOrderPlanning } from '@/modules/unified-orders/api/unifiedOrders' import { createPlatformExportTask } from '@/modules/downloads' import { showExportTaskCreatedDialog } from '@/shared/components/exportTaskCreatedDialog' import { ApiResponseError } from '@/shared/api/http' import { ElMessage } from 'element-plus' import UnifiedOrdersView from './UnifiedOrdersView.vue' vi.mock('vue-router', () => ({ useRoute: () => ({ params: {}, query: {} }), useRouter: () => ({ push: vi.fn() }), })) vi.mock('@/core/access/accessPreview', () => ({ useAccessPreviewStore: () => ({ can: () => true }) })) vi.mock('@/modules/iam', () => ({ listPlatformTenants: vi.fn().mockResolvedValue({ records: [], page: 1, size: 200, total: 0, pages: 0 }) })) vi.mock('@/modules/warehouses', () => ({ listAllPlatformWarehouses: vi.fn().mockResolvedValue([]) })) vi.mock('@/modules/inbounds', () => ({ downloadWeightImage: vi.fn(), getInboundMeasurement: vi.fn() })) vi.mock('@/modules/downloads', () => ({ createPlatformExportTask: vi.fn() })) vi.mock('@/shared/components/exportTaskCreatedDialog', () => ({ showExportTaskCreatedDialog: vi.fn() })) vi.mock('@/modules/unified-orders/workflows', () => ({ UnifiedOrderWorkflowPanel: { template: '
' } })) vi.mock('@/modules/unified-orders/api/coLoadGroups', () => ({ cancelPlatformCoLoadGroup: vi.fn(), createPlatformCoLoadGroup: vi.fn(), getPlatformCoLoadGroupByInboundCode: vi.fn(), })) vi.mock('@/modules/unified-orders/api/unifiedOrders', () => ({ updatePlatformUnifiedOrderRemarks: vi.fn(), approvePlatformUnifiedOrder: vi.fn(), getPlatformUnifiedOrder: vi.fn(), listPlatformUnifiedOrderFeeDetails: vi.fn(), listPlatformUnifiedOrders: vi.fn(), updatePlatformUnifiedOrderPlanning: vi.fn(), unifiedOrderStatusOptions: [ { value: 'PENDING_REVIEW', label: '待审核' }, { value: 'WAIT_MEASURE', label: '待测量数据' }, { value: 'WAIT_SPLIT', label: '待分柜' }, { value: 'LOADING', label: '装柜中' }, { value: 'LOADED', label: '装柜完成' }, { value: 'DELIVERED', label: '派送完成' }, { value: 'COMPLETED', label: '已完成' }, ], })) function emptyPage() { return { records: [], page: 1, size: 20, total: 0, pages: 0 } } function mountView() { return shallowMount(UnifiedOrdersView, { global: { directives: { loading: () => undefined }, stubs: { AdminPage: { template: '
' }, ListPageLayout: { template: '
' }, QueryToolbar: { name: 'QueryToolbar', props: ['modelValue'], emits: ['update:modelValue', 'search', 'reset'], template: '
' }, DataTable: { name: 'DataTable', props: ['page', 'pageSize', 'data'], emits: ['update:page', 'update:pageSize', 'selection-change'], template: '
' }, 'el-button': { template: '' }, 'el-input-number': { name: 'ElInputNumber', props: ['modelValue', 'disabled'], emits: ['update:modelValue', 'change'], template: '' }, 'el-date-picker': { name: 'ElDatePicker', props: ['modelValue', 'disabled'], emits: ['update:modelValue', 'change'], template: '' }, ApiErrorMeta: { props: ['details'], template: '{{ details.code }} {{ details.requestId }} {{ details.traceId }}' }, }, }, }) } describe('UnifiedOrdersView list request coordination', () => { beforeEach(() => { vi.mocked(listPlatformUnifiedOrders).mockReset().mockResolvedValue(emptyPage()) vi.mocked(updatePlatformUnifiedOrderPlanning).mockReset() vi.mocked(createPlatformExportTask).mockReset().mockResolvedValue({ taskName: '国内仓订单', taskNo: 'EXP-1' } as never) vi.mocked(showExportTaskCreatedDialog).mockReset().mockResolvedValue(false) }) it('loads once initially and once for query, reset, status tab and pagination', async () => { const wrapper = mountView() await flushPromises() expect(listPlatformUnifiedOrders).toHaveBeenCalledTimes(1) const toolbar = wrapper.getComponent({ name: 'QueryToolbar' }) toolbar.vm.$emit('search') await flushPromises() expect(listPlatformUnifiedOrders).toHaveBeenCalledTimes(2) toolbar.vm.$emit('reset') await flushPromises() expect(listPlatformUnifiedOrders).toHaveBeenCalledTimes(3) await wrapper.findAll('.status-tab')[1]!.trigger('click') await flushPromises() expect(listPlatformUnifiedOrders).toHaveBeenCalledTimes(4) wrapper.getComponent({ name: 'DataTable' }).vm.$emit('update:page', 2) await flushPromises() expect(listPlatformUnifiedOrders).toHaveBeenCalledTimes(5) }) it('does not apply draft filters to pagination until query is submitted', async () => { const wrapper = mountView() await flushPromises() const toolbar = wrapper.getComponent({ name: 'QueryToolbar' }) const draft = { ...toolbar.props('modelValue'), inboundCode: ' DRAFT-CODE ' } toolbar.vm.$emit('update:modelValue', draft) await wrapper.vm.$nextTick() wrapper.getComponent({ name: 'DataTable' }).vm.$emit('update:page', 2) await flushPromises() expect(listPlatformUnifiedOrders).toHaveBeenLastCalledWith(expect.objectContaining({ page: 2, inboundCode: undefined })) toolbar.vm.$emit('search') await flushPromises() expect(listPlatformUnifiedOrders).toHaveBeenCalledTimes(3) expect(listPlatformUnifiedOrders).toHaveBeenLastCalledWith(expect.objectContaining({ page: 1, inboundCode: 'DRAFT-CODE' })) }) it('resets a later page and changes page size with one request per interaction', async () => { const wrapper = mountView() await flushPromises() const table = wrapper.getComponent({ name: 'DataTable' }) const toolbar = wrapper.getComponent({ name: 'QueryToolbar' }) table.vm.$emit('update:page', 3) await flushPromises() expect(listPlatformUnifiedOrders).toHaveBeenCalledTimes(2) toolbar.vm.$emit('reset') await flushPromises() expect(listPlatformUnifiedOrders).toHaveBeenCalledTimes(3) expect(listPlatformUnifiedOrders).toHaveBeenLastCalledWith(expect.objectContaining({ page: 1 })) table.vm.$emit('update:pageSize', 50) table.vm.$emit('update:page', 1) await flushPromises() expect(listPlatformUnifiedOrders).toHaveBeenCalledTimes(4) expect(listPlatformUnifiedOrders).toHaveBeenLastCalledWith(expect.objectContaining({ page: 1, size: 50 })) }) it('does not turn a cleared full-container filter into false', async () => { const wrapper = mountView() await flushPromises() const toolbar = wrapper.getComponent({ name: 'QueryToolbar' }) const selected = { ...toolbar.props('modelValue'), fullContainer: 'true' } toolbar.vm.$emit('update:modelValue', selected) toolbar.vm.$emit('search') await flushPromises() expect(listPlatformUnifiedOrders).toHaveBeenLastCalledWith(expect.objectContaining({ fullContainer: true })) toolbar.vm.$emit('update:modelValue', { ...selected, fullContainer: undefined }) toolbar.vm.$emit('search') await flushPromises() expect(listPlatformUnifiedOrders).toHaveBeenLastCalledWith(expect.objectContaining({ fullContainer: undefined })) }) it('exports the selected business order numbers without loading other pages', async () => { const wrapper = mountView() await flushPromises() wrapper.getComponent({ name: 'DataTable' }).vm.$emit('selection-change', [ { orderId: '1', orderNo: 'ORDER-1', tenantId: '8' }, { orderId: '2', orderNo: 'ORDER-2', tenantId: '8' }, ]) await wrapper.vm.$nextTick() await wrapper.findAll('button').find((button) => button.text().includes('导出'))!.trigger('click') await flushPromises() expect(createPlatformExportTask).toHaveBeenCalledWith({ exportType: 'DOMESTIC_WAREHOUSE_ORDER_LIST', format: 'XLSX', taskName: '国内仓订单-已选2票', parameters: { selectedOrderNos: ['ORDER-1', 'ORDER-2'] }, }) expect(listPlatformUnifiedOrders).toHaveBeenCalledTimes(1) }) it('exports every result matching the applied filters instead of the current page', async () => { const wrapper = mountView() await flushPromises() const toolbar = wrapper.getComponent({ name: 'QueryToolbar' }) toolbar.vm.$emit('update:modelValue', { ...toolbar.props('modelValue'), inboundCode: ' IN-8 ', loaded: 'false', createdRange: ['2026-08-01', '2026-08-31'], }) toolbar.vm.$emit('search') await flushPromises() await wrapper.findAll('button').find((button) => button.text() === '导出')!.trigger('click') await flushPromises() expect(createPlatformExportTask).toHaveBeenCalledWith(expect.objectContaining({ exportType: 'DOMESTIC_WAREHOUSE_ORDER_LIST', taskName: '国内仓订单-当前筛选', parameters: expect.objectContaining({ inboundCode: 'IN-8', loaded: false, createdFrom: '2026-08-01T00:00:00', createdTo: '2026-08-31T23:59:59', }), })) expect(createPlatformExportTask).not.toHaveBeenCalledWith(expect.objectContaining({ parameters: expect.objectContaining({ page: expect.anything(), size: expect.anything() }), })) }) it('edits arrival and estimated volume and reports compact failure evidence through a message', async () => { const errorMessage = vi.spyOn(ElMessage, 'error').mockImplementation(() => undefined as never) vi.mocked(listPlatformUnifiedOrders).mockResolvedValue({ records: [{ orderId: '2089223263733518337', orderVersion: 7, tenantId: '2089176869176389634', internalOrderStatus: 'SUBMITTED', inboundCode: 'NYEXX2609030001', expectedArrivalAt: '2026-09-10T08:30:00', estimatedVolumeCbm: 0.25, }], page: 1, size: 20, total: 1, pages: 1, } as never) vi.mocked(updatePlatformUnifiedOrderPlanning) .mockResolvedValueOnce({ version: 8 } as never) .mockRejectedValueOnce(new ApiResponseError('订单版本已变化', 'A0409', 200, undefined, 'req-planning', 'trace-planning')) const wrapper = mountView() await flushPromises() const volume = wrapper.getComponent({ name: 'ElInputNumber' }) volume.vm.$emit('update:modelValue', 0.5) volume.vm.$emit('change', 0.5) await flushPromises() expect(updatePlatformUnifiedOrderPlanning).toHaveBeenNthCalledWith(1, '2089176869176389634', '2089223263733518337', { expectedVersion: 7, expectedArrivalAt: '2026-09-10T08:30:00', estimatedVolumeCbm: 0.5, }) const arrival = wrapper.getComponent({ name: 'ElDatePicker' }) arrival.vm.$emit('update:modelValue', '2026-09-12T16:00:00') arrival.vm.$emit('change', '2026-09-12T16:00:00') await flushPromises() expect(updatePlatformUnifiedOrderPlanning).toHaveBeenNthCalledWith(2, '2089176869176389634', '2089223263733518337', { expectedVersion: 8, expectedArrivalAt: '2026-09-12T16:00:00', estimatedVolumeCbm: 0.5, }) expect(wrapper.getComponent({ name: 'ElDatePicker' }).props('modelValue')).toBe('2026-09-12T16:00:00') expect(wrapper.text()).not.toContain('订单版本已变化') expect(wrapper.find('.planning-error').exists()).toBe(false) expect(errorMessage).toHaveBeenCalledWith(expect.objectContaining({ message: '订单版本已变化(错误码 A0409;Request ID req-planning;Trace ID trace-planning)', showClose: true, })) }) it('shows planning values without editors after the order enters irreversible fulfillment', async () => { vi.mocked(listPlatformUnifiedOrders).mockResolvedValue({ records: [{ orderId: '2099751993783402497', orderVersion: 7, tenantId: '2097708157074210818', internalOrderStatus: 'PROCESSING', mainStatus: 'LOADED', inboundCode: 'AT0E9B5BF2839C6944CFA3', expectedArrivalAt: '2026-10-15T00:00:00', estimatedVolumeCbm: 220, }], page: 1, size: 20, total: 1, pages: 1, } as never) const wrapper = mountView() await flushPromises() expect(wrapper.findComponent({ name: 'ElInputNumber' }).exists()).toBe(false) expect(wrapper.findComponent({ name: 'ElDatePicker' }).exists()).toBe(false) expect(wrapper.text()).toContain('220') expect(wrapper.text()).toContain('2026-10-15 00:00') expect(updatePlatformUnifiedOrderPlanning).not.toHaveBeenCalled() }) })