diff --git a/src/modules/container-loading/pages/GlobalSmartLoadingView.test.ts b/src/modules/container-loading/pages/GlobalSmartLoadingView.test.ts index c8a91b68..931719b7 100644 --- a/src/modules/container-loading/pages/GlobalSmartLoadingView.test.ts +++ b/src/modules/container-loading/pages/GlobalSmartLoadingView.test.ts @@ -95,7 +95,6 @@ function containerPage(records: ContainerBookingResource[] = [container]) { function mockContainerSearch(records: ContainerBookingResource[]) { vi.mocked(listSmartLoadingWorkbenchContainers).mockImplementation(async (query) => containerPage(records.filter((item) => ( item.planStatus !== 'COMPLETED' - && (!query.warehouseId || item.warehouseId === query.warehouseId) && query.channelGroupCodes.includes(resolveSmartLoadingChannelGroup(item.channelCode, item.channelName)?.code || '') )))) } @@ -259,7 +258,7 @@ describe('GlobalSmartLoadingView', () => { expect(wrapper.text()).not.toContain('美森正班海派') }) - it('moves the shipping warehouse into the query filters', async () => { + it('keeps the shipping warehouse as a candidate-only query filter', async () => { const dongguanContainer = { ...container, id: '9007199254740998', @@ -277,16 +276,18 @@ describe('GlobalSmartLoadingView', () => { expect(wrapper.text()).toContain('MSCU-1') expect(wrapper.text()).toContain('MSCU-DG') + const containerRequestCount = vi.mocked(listSmartLoadingWorkbenchContainers).mock.calls.length wrapper.findComponent(QueryToolbar).vm.$emit('search', { warehouseId: '18', customerId: '', inboundCode: '', expectedArrivalRange: [], orderStatus: '' }) await flushPromises() expect(wrapper.text()).toContain('MSCU-DG') - expect(wrapper.text()).not.toContain('MSCU-1') + expect(wrapper.text()).toContain('MSCU-1') expect(listSmartLoadingCandidates).toHaveBeenLastCalledWith({ channelGroupCodes: ['MATSON_CLX'], warehouseId: dongguanContainer.warehouseId, page: 1, size: 20 }) + expect(listSmartLoadingWorkbenchContainers).toHaveBeenCalledTimes(containerRequestCount) }) - it('keeps the selected channel when the warehouse changes even if that warehouse has no matching container', async () => { + it('keeps channel cabinets unchanged when another search field has no matching cabinet', async () => { const dongguanOvertimeContainer = { ...container, id: '9007199254740998', @@ -302,15 +303,49 @@ describe('GlobalSmartLoadingView', () => { mockContainerSearch([container, dongguanOvertimeContainer]) const wrapper = mount(GlobalSmartLoadingView, { global: { plugins: [ElementPlus] } }) await flushPromises() + const containerRequestCount = vi.mocked(listSmartLoadingWorkbenchContainers).mock.calls.length wrapper.findComponent(QueryToolbar).vm.$emit('search', { warehouseId: '18', customerId: '', inboundCode: '', expectedArrivalRange: [], orderStatus: '' }) await flushPromises() expect(wrapper.find('.channel-tags .el-checkbox-button.is-checked').text()).toBe('美森正班') - expect(wrapper.findAll('.container-option')).toHaveLength(0) + expect(wrapper.findAll('.container-option')).toHaveLength(1) + expect(wrapper.text()).toContain('MSCU-1') expect(wrapper.text()).not.toContain('MSCU-DG-MAX') expect(listSmartLoadingCandidates).toHaveBeenCalledTimes(2) expect(listSmartLoadingCandidates).toHaveBeenLastCalledWith({ channelGroupCodes: ['MATSON_CLX'], warehouseId: '18', page: 1, size: 20 }) + expect(listSmartLoadingWorkbenchContainers).toHaveBeenCalledTimes(containerRequestCount) + }) + + it('filters candidate details by inbound code without filtering channel cabinets', async () => { + const secondContainer = { + ...container, + id: '9007199254740998', + planId: '9007199254740999', + cabinetNo: 'CB-2', + containerNo: 'MSCU-2', + } + vi.mocked(listContainers).mockResolvedValue({ records: [container, secondContainer], page: 1, size: 100, total: 2, pages: 1 }) + mockContainerSearch([container, secondContainer]) + vi.mocked(getSmartLoadingPlanByBooking).mockImplementation(async (bookingId) => ({ ...plan, bookingId })) + vi.mocked(listSmartLoadingCandidates) + .mockResolvedValueOnce(candidatePage([candidate])) + .mockResolvedValueOnce(candidatePage([{ ...candidate, inboundCode: 'NYEXX2609030002' }])) + const wrapper = mount(GlobalSmartLoadingView, { global: { plugins: [ElementPlus] } }) + await flushPromises() + const containerRequestCount = vi.mocked(listSmartLoadingWorkbenchContainers).mock.calls.length + + wrapper.findComponent(QueryToolbar).vm.$emit('search', { + warehouseId: '', customerId: '', inboundCode: 'NYEXX2609030002', expectedArrivalRange: [], orderStatus: '', + }) + await flushPromises() + + expect(wrapper.get('.candidate-pane').text()).toContain('NYEXX2609030002') + expect(wrapper.findAll('.container-option')).toHaveLength(2) + expect(wrapper.text()).toContain('MSCU-1') + expect(wrapper.text()).toContain('MSCU-2') + expect(listSmartLoadingCandidates).toHaveBeenLastCalledWith({ channelGroupCodes: ['MATSON_CLX'], inboundCode: 'NYEXX2609030002', page: 1, size: 20 }) + expect(listSmartLoadingWorkbenchContainers).toHaveBeenCalledTimes(containerRequestCount) }) it('sends the active name-only customer selector to the server candidate query', () => { @@ -336,6 +371,7 @@ describe('GlobalSmartLoadingView', () => { .mockResolvedValueOnce(candidatePage([{ ...candidate, inboundId: '51', inboundCode: 'IN-MATCH', orderStatusSnapshot: 'WAIT_SPLIT', expectedArrivalAt: '2026-09-11T08:00:00' }])) const wrapper = mount(GlobalSmartLoadingView, { global: { plugins: [ElementPlus] } }) await flushPromises() + const containerRequestCount = vi.mocked(listSmartLoadingWorkbenchContainers).mock.calls.length wrapper.getComponent(QueryToolbar).vm.$emit('search', { customerId: '', inboundCode: '', orderStatus: 'WAIT_SPLIT', expectedArrivalRange: ['2026-09-10', '2026-09-12'], @@ -347,7 +383,8 @@ describe('GlobalSmartLoadingView', () => { expect(candidates).not.toContain('IN-WRONG-STATUS') expect(candidates).not.toContain('IN-WRONG-DATE') expect(listSmartLoadingCandidates).toHaveBeenLastCalledWith({ channelGroupCodes: ['MATSON_CLX'], expectedArrivalStart: '2026-09-10', expectedArrivalEnd: '2026-09-12', orderStatus: 'WAIT_SPLIT', page: 1, size: 20 }) - expect(listSmartLoadingWorkbenchContainers).toHaveBeenLastCalledWith({ channelGroupCodes: ['MATSON_CLX'], expectedArrivalStart: '2026-09-10', expectedArrivalEnd: '2026-09-12', orderStatus: 'WAIT_SPLIT', page: 1, size: 200 }) + expect(listSmartLoadingWorkbenchContainers).toHaveBeenCalledTimes(containerRequestCount) + expect(listSmartLoadingWorkbenchContainers).toHaveBeenLastCalledWith({ channelGroupCodes: ['MATSON_CLX'], page: 1, size: 200 }) }) it('paginates large inbound candidate results with twenty rows by default', async () => { diff --git a/src/modules/container-loading/pages/GlobalSmartLoadingView.vue b/src/modules/container-loading/pages/GlobalSmartLoadingView.vue index 008affe8..9a83fb45 100644 --- a/src/modules/container-loading/pages/GlobalSmartLoadingView.vue +++ b/src/modules/container-loading/pages/GlobalSmartLoadingView.vue @@ -219,19 +219,19 @@ const candidateColumns: DataTableColumn[] = [ { key: 'customerName', prop: 'customerName', label: '客户', minWidth: 70 }, { key: 'availablePackageCount', prop: 'availablePackageCount', label: '可用箱数', width: 80 }, // { key: 'cartonIdentityStatus', label: '箱号归属', width: 105, slotName: 'cartonIdentityStatus' }, - { key: 'unitGrossWeightKg', label: '平均单箱毛重', width: 100, slotName: 'unitGrossWeight' }, - { key: 'totalGrossWeightKg', label: '总重量', width: 90, slotName: 'candidateTotalWeight' }, - { key: 'averageUnitVolumeCbm', label: '平均单箱体积', width: 100, slotName: 'averageUnitVolume' }, - { key: 'totalVolumeCbm', label: '总体积', width: 90, slotName: 'candidateTotalVolume' }, + { key: 'unitGrossWeightKg', label: '单箱毛重', width: 80, slotName: 'unitGrossWeight' }, + { key: 'totalGrossWeightKg', label: '重量', width: 90, slotName: 'candidateTotalWeight' }, + { key: 'averageUnitVolumeCbm', label: '单箱体积', width: 80, slotName: 'averageUnitVolume' }, + { key: 'totalVolumeCbm', label: '体积', width: 90, slotName: 'candidateTotalVolume' }, ] const itemColumns: DataTableColumn[] = [ - { key: 'actions', label: '操作', width: 90, fixed: 'left', slotName: 'actions' }, - { key: 'inboundCode', prop: 'inboundCode', label: '入仓码', minWidth: 190, fixed: 'left', showOverflowTooltip: true }, - { key: 'coLoadGroup', label: '同柜不可拆', minWidth: 210, slotName: 'coLoadGroup' }, - { key: 'customerName', prop: 'customerName', label: '客户', minWidth: 140 }, - { key: 'packageCount', label: '分配箱数', width: 140, slotName: 'packageCount' }, - { key: 'grossWeightKg', label: '毛重', width: 110, slotName: 'weight' }, - { key: 'volumeCbm', label: '体积', width: 105, slotName: 'volume' }, + { key: 'actions', label: '操作', width: 80, fixed: 'left', slotName: 'actions' }, + { key: 'inboundCode', prop: 'inboundCode', label: '入仓码', minWidth: 140, fixed: 'left', showOverflowTooltip: true }, + { key: 'coLoadGroup', label: '同柜', minWidth: 80, slotName: 'coLoadGroup' }, + { key: 'customerName', prop: 'customerName', label: '客户', minWidth: 70 }, + { key: 'packageCount', label: '分配箱数', width: 100, slotName: 'packageCount' }, + { key: 'grossWeightKg', label: '毛重', width: 80, slotName: 'weight' }, + { key: 'volumeCbm', label: '体积', width: 80, slotName: 'volume' }, ] function formatNumber(value: number, digits = 3) { @@ -306,6 +306,14 @@ function candidateQueryParams(): Parameters[0 return params } +function containerQueryParams(page = 1): Parameters[0] { + return { + channelGroupCodes: [...selectedChannelCodes.value], + page, + size: 200, + } +} + async function loadCandidateData() { if (!selectedChannelCodes.value.length) return const request = ++candidateRequest @@ -357,9 +365,9 @@ async function loadContainerData(preserved: Record = {}) { if (!selectedChannelCodes.value.length) return const request = ++containerRequest try { - const firstPage = await listSmartLoadingWorkbenchContainers({ ...candidateQueryParams(), page: 1, size: 200 }) + const firstPage = await listSmartLoadingWorkbenchContainers(containerQueryParams()) const remainingPages = firstPage.pages > 1 - ? await Promise.all(Array.from({ length: firstPage.pages - 1 }, (_, index) => listSmartLoadingWorkbenchContainers({ ...candidateQueryParams(), page: index + 2, size: 200 }))) + ? await Promise.all(Array.from({ length: firstPage.pages - 1 }, (_, index) => listSmartLoadingWorkbenchContainers(containerQueryParams(index + 2)))) : [] if (request !== containerRequest) return containers.value = [firstPage, ...remainingPages].flatMap((page) => page.records) @@ -431,7 +439,7 @@ async function selectChannels(codes: string[]) { async function applyFilters(model: QueryModel) { appliedQuery.value = { ...model } candidatePage.value = 1 - await loadWorkbenchData() + await loadCandidateData() } async function changeCandidatePage(page: number) {