diff --git a/src/modules/container-loading/api/platformContainerLoading.test.ts b/src/modules/container-loading/api/platformContainerLoading.test.ts index d3262f5a..d45a60b5 100644 --- a/src/modules/container-loading/api/platformContainerLoading.test.ts +++ b/src/modules/container-loading/api/platformContainerLoading.test.ts @@ -14,7 +14,7 @@ vi.mock('@/shared/api/http', () => ({ unwrapApiResponse: (response: { data: T }) => response.data, })) -const container = { id: 9007199254740993, planId: 9007199254740994, warehouseId: 9007199254740995, shippingCompanyId: 9007199254740996, version: '2', capacityCbm: '68', maxWeightKg: '28000' } +const container = { id: 9007199254740993, planId: 9007199254740994, warehouseId: 9007199254740995, shippingCompanyId: 9007199254740996, channelCode: 'NY_EXX', channelName: '纽约EXX海铁', version: '2', capacityCbm: '68', maxWeightKg: '28000' } const plan = { id: 9007199254740993, bookingId: 9007199254740994, version: '3', status: 'EDITING', items: [{ id: 9007199254740995, loadingPlanId: 9007199254740993, accountId: 1, inboundId: 2, inboundDetailId: 3, orderId: 4, destinationWarehouseId: 5, packageCount: '6', version: '1' }] } describe('platform container loading API', () => { @@ -32,7 +32,7 @@ describe('platform container loading API', () => { it('normalizes paged container ids and numeric metadata', async () => { vi.mocked(http.get).mockResolvedValueOnce({ code: '200', data: { records: [container], page: '1', size: '20', total: '1', pages: '1' } }) const result = await listContainers({ page: 1, size: 20 }) - expect(result.records[0]).toMatchObject({ id: String(container.id), planId: String(container.planId), warehouseId: String(container.warehouseId), version: 2, capacityCbm: 68 }) + expect(result.records[0]).toMatchObject({ id: String(container.id), planId: String(container.planId), warehouseId: String(container.warehouseId), channelCode: 'NY_EXX', channelName: '纽约EXX海铁', version: 2, capacityCbm: 68 }) expect(http.get).toHaveBeenCalledWith('/platform/v1/containers', { params: { page: 1, size: 20 } }) }) @@ -49,9 +49,9 @@ describe('platform container loading API', () => { it('normalizes every long id in candidates and plans', async () => { vi.mocked(http.get) .mockResolvedValueOnce({ code: '200', data: container }) - .mockResolvedValueOnce({ code: '200', data: [{ accountId: 1, inboundId: 2, inboundDetailId: 3, orderId: 4, warehouseId: 5, destinationWarehouseId: 6, availablePackageCount: '7' }] }) + .mockResolvedValueOnce({ code: '200', data: [{ accountId: 1, inboundId: 2, inboundDetailId: 3, orderId: 4, warehouseId: 5, destinationWarehouseId: 6, channelCode: 'NY_EXX', channelName: '纽约EXX海铁', availablePackageCount: '7' }] }) expect((await getContainer('9')).id).toBe(String(container.id)) - expect(await listSmartLoadingCandidates('7', '5')).toMatchObject([{ accountId: '1', inboundDetailId: '3', destinationWarehouseId: '6', availablePackageCount: 7 }]) + expect(await listSmartLoadingCandidates('7', '5')).toMatchObject([{ accountId: '1', inboundDetailId: '3', destinationWarehouseId: '6', channelCode: 'NY_EXX', channelName: '纽约EXX海铁', availablePackageCount: 7 }]) expect(http.get).toHaveBeenLastCalledWith('/platform/v1/first-leg/smart-loading/candidates', { params: { bookingId: '7', warehouseId: '5' } }) }) diff --git a/src/modules/container-loading/pages/GlobalSmartLoadingView.test.ts b/src/modules/container-loading/pages/GlobalSmartLoadingView.test.ts index c7354bcf..004e8ed8 100644 --- a/src/modules/container-loading/pages/GlobalSmartLoadingView.test.ts +++ b/src/modules/container-loading/pages/GlobalSmartLoadingView.test.ts @@ -143,7 +143,6 @@ describe('GlobalSmartLoadingView', () => { expect(wrapper.text()).not.toContain('税率') expect(wrapper.text()).not.toContain('是否退税') - expect(wrapper.text()).not.toContain('税率') }) it('prefers an editable container on first load so pushed plans do not show a false editing error', async () => { diff --git a/src/modules/container-loading/pages/GlobalSmartLoadingView.vue b/src/modules/container-loading/pages/GlobalSmartLoadingView.vue index 3082135c..19d45c60 100644 --- a/src/modules/container-loading/pages/GlobalSmartLoadingView.vue +++ b/src/modules/container-loading/pages/GlobalSmartLoadingView.vue @@ -25,7 +25,6 @@ import { } from '../api/platformContainerLoading' import type { ContainerBookingResource, - ContainerPlanStatus, SmartLoadingCandidate, SmartLoadingItem, SmartLoadingPlan, @@ -65,7 +64,7 @@ const selectedContainer = computed(() => containers.value.find((item) => item.id const channelTabs = computed(() => { const channels = new Map() for (const item of containers.value) { - if (item.planStatus === 'COMPLETED' || !item.channelCode) continue + if (!['WAIT_LOADING', 'EDITING'].includes(item.planStatus) || !item.channelCode) continue const current = channels.get(item.channelCode) if (current) current.containerCount += 1 else channels.set(item.channelCode, { @@ -77,7 +76,7 @@ const channelTabs = computed(() => { return [...channels.values()] }) const activeChannel = computed(() => channelTabs.value.find((item) => item.code === activeChannelCode.value) || null) -const editable = computed(() => plan.value?.status === 'EDITING' || plan.value?.status === 'LOADING') +const editable = computed(() => plan.value?.status === 'EDITING') const selectedPlanStatus = computed(() => selectedContainer.value?.planStatus || null) const assignedDetailIds = computed(() => new Set((plan.value?.items || []).map((item) => item.inboundDetailId))) const totalPackages = computed(() => (plan.value?.items || []).reduce((sum, item) => sum + Number(item.packageCount || 0), 0)) @@ -93,11 +92,10 @@ const displayedWeightPercent = computed(() => percentage(weightPercent.value)) const queryFields = computed(() => [ { key: 'customerId', type: 'select', label: '客户', placeholder: '全部客户', width: 220, options: customers.value.filter((customer) => customer.status === 'ACTIVE').map((customer) => ({ label: customer.tenantName, value: String(customer.id) })), props: { filterable: true, loading: customersLoading.value } }, { key: 'orderKeyword', type: 'input', label: '订单或货物', placeholder: '订单号、入库单、货物', width: 250 }, - { key: 'containerKeyword', type: 'input', label: '柜次', placeholder: '柜次号、集装箱号、目的仓', width: 230 }, + { key: 'containerKeyword', type: 'input', label: '柜号', placeholder: '请输入柜号', width: 230 }, { key: 'status', type: 'select', label: '柜次状态', width: 160, options: [ { label: '待装柜', value: 'WAIT_LOADING' }, { label: '预装柜中', value: 'EDITING' }, - { label: '拣货中', value: 'LOADING' }, ] }, ]) @@ -105,12 +103,11 @@ const filteredContainers = computed(() => { const keyword = String(appliedQuery.value.containerKeyword || '').trim().toLowerCase() const status = String(appliedQuery.value.status || '') return containers.value.filter((item) => { - if (item.planStatus === 'COMPLETED') return false + if (!['WAIT_LOADING', 'EDITING'].includes(item.planStatus)) return false if (!activeChannelCode.value || item.channelCode !== activeChannelCode.value) return false if (status && item.planStatus !== status) return false if (!keyword) return true - return [item.cabinetNo, item.containerNo, item.bookingNo, item.destinationWarehouseCode, item.destinationWarehouseName] - .some((value) => String(value || '').toLowerCase().includes(keyword)) + return displayContainerNo(item).toLowerCase().includes(keyword) }) }) const filteredCandidates = computed(() => { @@ -139,7 +136,6 @@ const candidateColumns: DataTableColumn[] = [ { key: 'availablePackageCount', prop: 'availablePackageCount', label: '可用箱数', width: 100 }, { key: 'unitVolumeCbm', label: '单箱体积', width: 105, slotName: 'unitVolume' }, { key: 'unitWeightKg', label: '单箱重量', width: 105, slotName: 'unitWeight' }, - { key: 'destination', label: '目的仓', minWidth: 130, slotName: 'destination' }, ] const itemColumns: DataTableColumn[] = [ { key: 'actions', label: '操作', width: 170, fixed: 'left', slotName: 'actions' }, @@ -148,7 +144,6 @@ const itemColumns: DataTableColumn[] = [ { key: 'packageCount', label: '分配箱数', width: 140, slotName: 'packageCount' }, { key: 'volumeCbm', label: '体积', width: 105, slotName: 'volume' }, { key: 'grossWeightKg', label: '毛重', width: 110, slotName: 'weight' }, - { key: 'destination', label: '目的仓', minWidth: 130, slotName: 'destination' }, ] function formatNumber(value: number, digits = 3) { @@ -157,16 +152,11 @@ function formatNumber(value: number, digits = 3) { function percentage(value: number) { return Number.isFinite(value) ? Number(Math.max(value, 0).toFixed(2)) : 0 } function candidateRow(source: object) { return source as SmartLoadingCandidate } function itemRow(source: object) { return source as SmartLoadingItem } -function statusLabel(status: ContainerPlanStatus) { - return ({ WAIT_LOADING: '待装柜', EDITING: '预装柜中', LOADING: '拣货中', COMPLETED: '装柜完成' } as const)[status] -} -function statusType(status: ContainerPlanStatus) { - return status === 'WAIT_LOADING' ? 'info' : status === 'EDITING' ? 'primary' : status === 'LOADING' ? 'warning' : 'success' -} -function routeMatches(source: object) { - const candidate = candidateRow(source) - const target = selectedContainer.value?.destinationWarehouseCode - return !target || target === candidate.destinationWarehouseCode +function displayContainerNo(container: ContainerBookingResource | null | undefined) { + if (!container) return '柜号待生成' + const actual = String(container.containerNo || '').trim() + const internalReference = /^(?:MAN|SPLIT)-/i.test(actual) + return actual && !internalReference ? actual : String(container.cabinetNo || '').trim() || '柜号待生成' } function syncDrafts(next: SmartLoadingPlan, preserved: Record = {}) { for (const key of Object.keys(quantityDrafts)) delete quantityDrafts[key] @@ -412,7 +402,7 @@ onMounted(() => { void load(); void loadCustomers() })
候选货物{{ filteredCandidates.length }}
-
可见柜次{{ filteredContainers.length }}
+
可见空柜{{ filteredContainers.length }}
当前已装箱数{{ totalPackages }}
当前总体积{{ formatNumber(totalVolume) }} m³
当前总重量{{ formatNumber(totalWeight, 1) }} kg
@@ -436,22 +426,18 @@ onMounted(() => { void load(); void loadCustomers() }) size="small" type="primary" plain - :disabled="!routeMatches(row) || assignedDetailIds.has(row.inboundDetailId) || row.availablePackageCount < 1" + :disabled="assignedDetailIds.has(row.inboundDetailId) || row.availablePackageCount < 1" @click="openAssign(row)" >加入 -