From de1b956d43bc7c1a1f442d8da57073d0c191dfdd Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Sun, 30 Aug 2026 15:49:01 +0800 Subject: [PATCH] fix(admin): align coload groups with inbound fragments --- .../pages/GlobalSmartLoadingView.test.ts | 24 ++++--- .../pages/GlobalSmartLoadingView.vue | 39 ++++++++--- .../pages/SmartLoadingWorkbenchView.test.ts | 24 ++++--- .../pages/SmartLoadingWorkbenchView.vue | 37 +++++++--- .../unified-orders/api/coLoadGroups.test.ts | 24 +++++-- .../unified-orders/api/coLoadGroups.ts | 12 +--- .../pages/UnifiedOrdersView.coLoad.test.ts | 23 +++++-- .../pages/UnifiedOrdersView.test.ts | 31 ++++++++- .../pages/UnifiedOrdersView.vue | 67 ++++++++++++++++--- 9 files changed, 214 insertions(+), 67 deletions(-) diff --git a/src/modules/container-loading/pages/GlobalSmartLoadingView.test.ts b/src/modules/container-loading/pages/GlobalSmartLoadingView.test.ts index ee368c49..91ab3e26 100644 --- a/src/modules/container-loading/pages/GlobalSmartLoadingView.test.ts +++ b/src/modules/container-loading/pages/GlobalSmartLoadingView.test.ts @@ -205,15 +205,17 @@ describe('GlobalSmartLoadingView', () => { it('aggregates a two-order candidate group and calls only whole-group assignment', async () => { vi.mocked(listSmartLoadingCandidates).mockResolvedValue([ - { accountId: '1', inboundId: '201', orderId: '301', orderNo: 'ORD-201', inboundCode: 'NYEXX2608290051', warehouseId: '8', channelCode: 'ZBYHK', channelName: '美森正班海卡包税', destinationWarehouseId: '9', destinationWarehouseCode: 'LAX9', destinationWarehouseName: 'LAX9', customerName: '客户一', goodsSummary: '货物一', availablePackageCount: 4, unitGrossWeightKg: 10, unitNetWeightKg: 9, averageUnitVolumeCbm: 0.2, availableVolumeCbm: 0.8, coLoadGroupId: '2093539779795795969', groupNo: 'CLG-2093539779795795969', memberCount: 2, groupTotalBoxes: 10, locked: false, blockReason: null }, - { accountId: '2', inboundId: '202', orderId: '302', orderNo: 'ORD-202', inboundCode: 'NYEXX2608290052', warehouseId: '8', channelCode: 'ZBYHK', channelName: '美森正班海卡包税', destinationWarehouseId: '9', destinationWarehouseCode: 'LAX9', destinationWarehouseName: 'LAX9', customerName: '客户二', goodsSummary: '货物二', availablePackageCount: 6, unitGrossWeightKg: 10, unitNetWeightKg: 9, averageUnitVolumeCbm: 0.2, availableVolumeCbm: 1.2, coLoadGroupId: '2093539779795795969', groupNo: 'CLG-2093539779795795969', memberCount: 2, groupTotalBoxes: 10, locked: false, blockReason: null }, + { accountId: '1', inboundId: '201', orderId: '301', orderNo: 'ORD-201', inboundCode: 'NYEXX2608290051', warehouseId: '8', channelCode: 'ZBYHK', channelName: '美森正班海卡包税', destinationWarehouseId: '9', destinationWarehouseCode: 'LAX9', destinationWarehouseName: 'LAX9', customerName: '客户一', goodsSummary: '货物一', availablePackageCount: 4, unitGrossWeightKg: 10, unitNetWeightKg: 9, averageUnitVolumeCbm: 0.2, availableVolumeCbm: 0.8, coLoadGroupId: '2093539779795795969', groupNo: 'CLG-2093539779795795969', memberCount: 3, groupTotalBoxes: 12, locked: false, blockReason: null }, + { accountId: '1', inboundId: '203', orderId: '301', orderNo: 'ORD-201', inboundCode: 'NYEXX2608290051-B', warehouseId: '8', channelCode: 'ZBYHK', channelName: '美森正班海卡包税', destinationWarehouseId: '10', destinationWarehouseCode: 'JFK8', destinationWarehouseName: 'JFK8', customerName: '客户一', goodsSummary: '货物一', availablePackageCount: 2, unitGrossWeightKg: 10, unitNetWeightKg: 9, averageUnitVolumeCbm: 0.2, availableVolumeCbm: 0.4, coLoadGroupId: '2093539779795795969', groupNo: 'CLG-2093539779795795969', memberCount: 3, groupTotalBoxes: 12, locked: false, blockReason: null }, + { accountId: '2', inboundId: '202', orderId: '302', orderNo: 'ORD-202', inboundCode: 'NYEXX2608290052', warehouseId: '8', channelCode: 'ZBYHK', channelName: '美森正班海卡包税', destinationWarehouseId: '9', destinationWarehouseCode: 'LAX9', destinationWarehouseName: 'LAX9', customerName: '客户二', goodsSummary: '货物二', availablePackageCount: 6, unitGrossWeightKg: 10, unitNetWeightKg: 9, averageUnitVolumeCbm: 0.2, availableVolumeCbm: 1.2, coLoadGroupId: '2093539779795795969', groupNo: 'CLG-2093539779795795969', memberCount: 3, groupTotalBoxes: 12, locked: false, blockReason: null }, ]) vi.mocked(assignSmartLoadingGroup).mockResolvedValue(plan) const wrapper = mount(GlobalSmartLoadingView, { attachTo: document.body, global: { plugins: [ElementPlus] } }) await flushPromises() expect(wrapper.text()).toContain('CLG-2093539779795795969 · 2 票') - expect(wrapper.text()).toContain('NYEXX2608290051、NYEXX2608290052') + expect(wrapper.text()).toContain('3 个入仓片段') + expect(wrapper.text()).toContain('ORD-201:NYEXX2608290051、NYEXX2608290051-B') const buttons = wrapper.findAll('button').filter((button) => button.text().includes('整组分配')) expect(buttons).toHaveLength(1) await buttons[0].trigger('click') @@ -243,25 +245,27 @@ describe('GlobalSmartLoadingView', () => { }) it('aggregates assigned members into one locked group row and removes the group once', async () => { - const items = ['201', '202'].map((inboundId, index) => ({ + const baseItems = ['201', '202'].map((inboundId, index) => ({ ...plan.items[0], id: `70${index}`, accountId: `${index + 1}`, inboundId, inboundDetailId: `80${index}`, orderId: `30${index}`, orderNo: `ORD-${index}`, inboundCode: `NYEXX260829005${index + 1}`, customerName: `客户${index}`, packageCount: 5, grossWeightKg: 50, volumeCbm: 1, loadSequence: index + 1, })) + const items = [baseItems[0], { ...baseItems[0], id: '703', inboundId: '203', inboundDetailId: '803', inboundCode: 'NYEXX2608290051-B', packageCount: 3, grossWeightKg: 30, volumeCbm: 0.6 }, baseItems[1]] const groupedPlan = { ...plan, items } const group = { - groupId: '2093539779795795969', groupNo: 'CLG-2093539779795795969', status: 'LOCKED' as const, warehouseId: '8', channelCode: 'ZBYHK', destinationType: 'WAREHOUSE', destinationWarehouseId: '9', destinationCode: 'LAX9', lockedPlanId: '99', version: 1, - members: items.map((item) => ({ tenantId: item.accountId, orderId: item.orderId, inboundId: item.inboundId, fullPackageCount: 5 })), fullPackageCount: 10, createdAt: '2026-08-29T10:00:00', updatedAt: '2026-08-29T10:30:00', + groupId: '2093539779795795969', groupNo: 'CLG-2093539779795795969', status: 'LOCKED' as const, warehouseId: '8', channelCode: 'ZBYHK', lockedPlanId: '99', version: 1, + members: items.map((item) => ({ tenantId: item.accountId, orderId: item.orderId, inboundId: item.inboundId, fullPackageCount: item.packageCount })), fullPackageCount: 13, createdAt: '2026-08-29T10:00:00', updatedAt: '2026-08-29T10:30:00', } vi.mocked(getSmartLoadingPlanByBooking).mockResolvedValueOnce(groupedPlan).mockResolvedValue({ ...groupedPlan, version: 3, items: [] }) - vi.mocked(batchQueryPlatformCoLoadGroups).mockResolvedValue(items.map((item) => ({ orderId: item.orderId, inboundId: item.inboundId, group }))) + vi.mocked(batchQueryPlatformCoLoadGroups).mockResolvedValue([...new Set(items.map((item) => item.orderId))].map((orderId) => ({ orderId, group }))) vi.mocked(removeSmartLoadingGroup).mockResolvedValue({ ...groupedPlan, version: 3, items: [] }) vi.spyOn(ElMessageBox, 'confirm').mockResolvedValue('confirm' as never) const wrapper = mount(GlobalSmartLoadingView, { global: { plugins: [ElementPlus] } }) await flushPromises() - expect(wrapper.text()).toContain('CLG-2093539779795795969 · 2 票') - expect(wrapper.text()).toContain('固定 10 箱 · 已锁定') + expect(wrapper.text()).toContain('CLG-2093539779795795969 · 2 票 · 3 个入仓片段') + expect(wrapper.text()).toContain('ORD-0:NYEXX2608290051、NYEXX2608290051-B') + expect(wrapper.text()).toContain('固定 13 箱 · 已锁定') expect(wrapper.text()).toContain('整组固定全箱数,不允许单票调整') - expect(wrapper.findAllComponents(ElInputNumber).some((input) => input.props('modelValue') === 10)).toBe(false) + expect(wrapper.findAllComponents(ElInputNumber).some((input) => input.props('modelValue') === 13)).toBe(false) const removeButton = wrapper.findAll('button').find((button) => button.text().includes('移除整组'))! await removeButton.trigger('click') await flushPromises() diff --git a/src/modules/container-loading/pages/GlobalSmartLoadingView.vue b/src/modules/container-loading/pages/GlobalSmartLoadingView.vue index 829c5826..a48412ed 100644 --- a/src/modules/container-loading/pages/GlobalSmartLoadingView.vue +++ b/src/modules/container-loading/pages/GlobalSmartLoadingView.vue @@ -84,13 +84,28 @@ const editable = computed(() => plan.value?.canEdit === true) const assignable = computed(() => plan.value?.canAssign === true) const selectedPlanStatus = computed(() => selectedContainer.value?.planStatus || null) const assignedInboundIds = computed(() => new Set((plan.value?.items || []).map((item) => item.inboundId))) -type SmartLoadingCandidateRow = SmartLoadingCandidate & { sourceCandidates: SmartLoadingCandidate[]; memberInboundCodes: string[] } +type SmartLoadingCandidateRow = SmartLoadingCandidate & { sourceCandidates: SmartLoadingCandidate[]; memberInboundCodes: string[]; memberOrderSummaries: string[]; memberOrderCount: number; inboundFragmentCount: number } +function memberOrderSummaries(members: T[]) { + const orders = new Map() + for (const member of members) { + const current = orders.get(member.orderId) || { label: member.orderNo || member.orderId, inboundCodes: [] } + const inboundCode = member.inboundCode || '入仓码暂缺' + if (!current.inboundCodes.includes(inboundCode)) current.inboundCodes.push(inboundCode) + orders.set(member.orderId, current) + } + return [...orders.values()].map((order) => `${order.label}:${order.inboundCodes.join('、')}`) +} +function groupOrderCount(group: CoLoadGroup) { return new Set(group.members.map((member) => member.orderId)).size } +function groupMemberLabel(group: CoLoadGroup) { + const orderCount = groupOrderCount(group) + return group.members.length === orderCount ? `${orderCount} 票` : `${orderCount} 票 · ${group.members.length} 个入仓片段` +} const displayedCandidates = computed(() => { const rows: SmartLoadingCandidateRow[] = [] const groups = new Map() for (const candidate of candidates.value) { if (!candidate.coLoadGroupId) { - rows.push({ ...candidate, sourceCandidates: [candidate], memberInboundCodes: [candidate.inboundCode || '入仓码暂缺'] }) + rows.push({ ...candidate, sourceCandidates: [candidate], memberInboundCodes: [candidate.inboundCode || '入仓码暂缺'], memberOrderSummaries: memberOrderSummaries([candidate]), memberOrderCount: 1, inboundFragmentCount: 1 }) continue } groups.set(candidate.coLoadGroupId, [...(groups.get(candidate.coLoadGroupId) || []), candidate]) @@ -106,11 +121,14 @@ const displayedCandidates = computed(() => { availableVolumeCbm: members.reduce((sum, member) => sum + Number(member.availableVolumeCbm || 0), 0), sourceCandidates: members, memberInboundCodes: members.map((member) => member.inboundCode || '入仓码暂缺'), + memberOrderSummaries: memberOrderSummaries(members), + memberOrderCount: new Set(members.map((member) => member.orderId)).size, + inboundFragmentCount: Number(first.memberCount || members.length), }) } return rows }) -type SmartLoadingPlanRow = SmartLoadingInboundItem & { coLoadGroup: CoLoadGroup | null; memberInboundCodes: string[] } +type SmartLoadingPlanRow = SmartLoadingInboundItem & { coLoadGroup: CoLoadGroup | null; memberInboundCodes: string[]; memberOrderSummaries: string[] } const displayedPlanItems = computed(() => { const inboundRows = groupSmartLoadingItems(plan.value?.items || []) const result: SmartLoadingPlanRow[] = [] @@ -118,7 +136,7 @@ const displayedPlanItems = computed(() => { for (const row of inboundRows) { const group = planGroupsByInboundId.value.get(row.inboundId) if (!group) { - result.push({ ...row, coLoadGroup: null, memberInboundCodes: [row.inboundCode || '入仓码暂缺'] }) + result.push({ ...row, coLoadGroup: null, memberInboundCodes: [row.inboundCode || '入仓码暂缺'], memberOrderSummaries: memberOrderSummaries([row]) }) continue } grouped.set(group.groupId, [...(grouped.get(group.groupId) || []), row]) @@ -143,6 +161,7 @@ const displayedPlanItems = computed(() => { detailCount: rows.reduce((sum, row) => sum + row.detailCount, 0), coLoadGroup: group, memberInboundCodes: rows.map((row) => row.inboundCode || '入仓码暂缺'), + memberOrderSummaries: memberOrderSummaries(rows), }) } return result @@ -430,7 +449,7 @@ async function remove(source: object) { if (!plan.value || !item.canRemove) return const row = source as SmartLoadingPlanRow const confirmed = await ElMessageBox.confirm(row.coLoadGroup - ? `确认整组移除 ${row.coLoadGroup.groupNo}(${row.coLoadGroup.members.length} 票、${row.packageCount} 箱)吗?组内成员不能单独移除。` + ? `确认整组移除 ${row.coLoadGroup.groupNo}(${groupMemberLabel(row.coLoadGroup)}、${row.packageCount} 箱)吗?组内所有入仓片段将联动移除。` : `确认从当前柜次移除入仓码“${item.inboundCode || '—'}”的 ${item.packageCount} 箱吗?`, row.coLoadGroup ? '移除同柜不可拆组' : '移除装柜明细', { type: 'warning', confirmButtonText: row.coLoadGroup ? '移除整组' : '移除', cancelButtonText: '取消', }).catch(() => false) @@ -579,7 +598,7 @@ onMounted(() => { void load(); void loadCustomers() }) @click="openAssign(row)" >{{ row.coLoadGroupId ? '整组分配' : '加入' }} - + @@ -658,7 +677,7 @@ onMounted(() => { void load(); void loadCustomers() }) - + @@ -671,9 +690,9 @@ onMounted(() => { void load(); void loadCustomers() })

{{ selectedCandidate.groupNo || selectedCandidate.customerName || '客户待补充' }}

-

{{ selectedCandidate.groupNo || selectedCandidate.inboundCode || '入仓码暂缺' }}

+

{{ selectedCandidate.groupNo || selectedCandidate.inboundCode || '入仓码暂缺' }}

diff --git a/src/modules/unified-orders/api/coLoadGroups.test.ts b/src/modules/unified-orders/api/coLoadGroups.test.ts index ffa41ec8..3e0a1039 100644 --- a/src/modules/unified-orders/api/coLoadGroups.test.ts +++ b/src/modules/unified-orders/api/coLoadGroups.test.ts @@ -4,8 +4,7 @@ import { batchQueryPlatformCoLoadGroups, cancelPlatformCoLoadGroup, createPlatfo const group = { groupId: 2093000000000000001n, groupNo: 'CLG-2093000000000000001', status: 'ACTIVE', - warehouseId: 2093000000000000002n, channelCode: 'NY-EXX', destinationType: 'WAREHOUSE', - destinationWarehouseId: 2093000000000000003n, destinationCode: 'ONT8', lockedPlanId: null, + warehouseId: 2093000000000000002n, channelCode: 'NY-EXX', lockedPlanId: null, version: 0, fullPackageCount: 18, createdAt: '2026-08-29T10:00:00', updatedAt: '2026-08-29T10:00:00', members: [ { tenantId: 2093000000000000004n, orderId: 2093000000000000005n, inboundId: 2093000000000000006n, fullPackageCount: 8 }, @@ -39,11 +38,28 @@ describe('co-load group API', () => { it('batch-enriches three list rows and normalizes every long id without numeric coercion', async () => { const third = { ...group.members[0], orderId: 2093000000000000010n, inboundId: 2093000000000000011n } const threeMemberGroup = { ...group, fullPackageCount: 23, members: [...group.members, third] } - const post = vi.spyOn(http, 'post').mockResolvedValue({ code: '200', message: 'ok', data: threeMemberGroup.members.map((member) => ({ orderId: member.orderId, inboundId: member.inboundId, group: threeMemberGroup })) }) + const post = vi.spyOn(http, 'post').mockResolvedValue({ code: '200', message: 'ok', data: threeMemberGroup.members.map((member) => ({ orderId: member.orderId, group: threeMemberGroup })) }) const orderIds = ['2093000000000000005', '2093000000000000008', '2093000000000000010'] const result = await batchQueryPlatformCoLoadGroups(orderIds) expect(post).toHaveBeenCalledWith('/platform/v1/first-leg/coload-groups/batch-query', { orderIds }) expect(result).toHaveLength(3) - expect(result[2]).toMatchObject({ orderId: '2093000000000000010', inboundId: '2093000000000000011', group: { groupId: '2093000000000000001', fullPackageCount: 23 } }) + expect(result[2]).toMatchObject({ orderId: '2093000000000000010', group: { groupId: '2093000000000000001', fullPackageCount: 23 } }) + expect(result[2]).not.toHaveProperty('inboundId') + }) + + it('keeps every inbound fragment when one order has multiple current inbounds', async () => { + const fragment = { ...group.members[0], inboundId: 2093000000000000012n, fullPackageCount: 3 } + const multiInboundGroup = { ...group, fullPackageCount: 21, members: [group.members[0], fragment, group.members[1]] } + const post = vi.spyOn(http, 'post').mockResolvedValue({ code: '200', message: 'ok', data: [ + { orderId: group.members[0].orderId, group: multiInboundGroup }, + { orderId: group.members[1].orderId, group: multiInboundGroup }, + ] }) + const result = await batchQueryPlatformCoLoadGroups(['2093000000000000005', '2093000000000000008']) + expect(post).toHaveBeenCalledTimes(1) + expect(result[0].group.members).toEqual([ + expect.objectContaining({ orderId: '2093000000000000005', inboundId: '2093000000000000006', fullPackageCount: 8 }), + expect.objectContaining({ orderId: '2093000000000000005', inboundId: '2093000000000000012', fullPackageCount: 3 }), + expect.objectContaining({ orderId: '2093000000000000008', inboundId: '2093000000000000009', fullPackageCount: 10 }), + ]) }) }) diff --git a/src/modules/unified-orders/api/coLoadGroups.ts b/src/modules/unified-orders/api/coLoadGroups.ts index 2fdde984..86039995 100644 --- a/src/modules/unified-orders/api/coLoadGroups.ts +++ b/src/modules/unified-orders/api/coLoadGroups.ts @@ -15,9 +15,6 @@ export interface CoLoadGroup { status: CoLoadGroupStatus warehouseId: string channelCode: string - destinationType: string - destinationWarehouseId: string | null - destinationCode: string | null lockedPlanId: string | null version: number members: CoLoadGroupMember[] @@ -28,21 +25,18 @@ export interface CoLoadGroup { export interface OrderCoLoadGroupInfo { orderId: string - inboundId: string group: CoLoadGroup } type RawId = string | number -type RawCoLoadGroup = Omit & { +type RawCoLoadGroup = Omit & { groupId: RawId warehouseId: RawId - destinationWarehouseId: RawId | null lockedPlanId: RawId | null members: Array & { tenantId: RawId; orderId: RawId; inboundId: RawId }> } -type RawOrderCoLoadGroupInfo = Omit & { +type RawOrderCoLoadGroupInfo = Omit & { orderId: RawId - inboundId: RawId group: RawCoLoadGroup } @@ -55,7 +49,6 @@ function normalizeGroup(raw: RawCoLoadGroup): CoLoadGroup { ...raw, groupId: String(raw.groupId), warehouseId: String(raw.warehouseId), - destinationWarehouseId: optionalId(raw.destinationWarehouseId), lockedPlanId: optionalId(raw.lockedPlanId), version: Number(raw.version), fullPackageCount: Number(raw.fullPackageCount), @@ -94,7 +87,6 @@ export async function batchQueryPlatformCoLoadGroups(orderIds: string[]) { ) return unwrapApiResponse(response).map((item) => ({ orderId: String(item.orderId), - inboundId: String(item.inboundId), group: normalizeGroup(item.group), })) } diff --git a/src/modules/unified-orders/pages/UnifiedOrdersView.coLoad.test.ts b/src/modules/unified-orders/pages/UnifiedOrdersView.coLoad.test.ts index 30ba44e1..2945df33 100644 --- a/src/modules/unified-orders/pages/UnifiedOrdersView.coLoad.test.ts +++ b/src/modules/unified-orders/pages/UnifiedOrdersView.coLoad.test.ts @@ -32,8 +32,7 @@ const rows = ['5001', '5002'].map((orderId, index) => ({ expressCompany: null, expressTrackingNo: null, skuNos: null, totalQuantity: null, shipmentId: null, referenceId: null, })) const group = { - groupId: '9001', groupNo: 'CLG-9001', status: 'ACTIVE' as const, warehouseId: '8', channelCode: 'NY-EXX', destinationType: 'WAREHOUSE', - destinationWarehouseId: '9', destinationCode: 'ONT8', lockedPlanId: null, version: 0, fullPackageCount: 10, + groupId: '9001', groupNo: 'CLG-9001', status: 'ACTIVE' as const, warehouseId: '8', channelCode: 'NY-EXX', lockedPlanId: null, version: 0, fullPackageCount: 10, members: rows.map((row) => ({ tenantId: String(row.tenantId), orderId: String(row.orderId), inboundId: String(row.inboundId), fullPackageCount: 5 })), createdAt: '2026-08-29T10:00:00', updatedAt: '2026-08-29T10:00:00', } @@ -44,9 +43,10 @@ function mountView() { AdminPage: { template: '
' }, ListPageLayout: { template: '
' }, QueryToolbar: { template: '
' }, - DataTable: { name: 'DataTable', props: ['data'], emits: ['selection-change'], template: '
' }, + DataTable: { name: 'DataTable', props: ['data'], emits: ['selection-change'], template: '
' }, AppDialog: { name: 'AppDialog', props: ['modelValue', 'confirmText'], emits: ['confirm', 'update:modelValue'], template: '
' }, ApiErrorMeta: { props: ['details'], template: '
{{ details.code }} {{ details.requestId }} {{ details.traceId }}
' }, + CopyButton: { props: ['value', 'label'], template: '' }, 'el-button': { template: '' }, 'el-tag': { template: '' }, 'el-alert': { props: ['title'], template: '
{{ title }}
' }, @@ -77,13 +77,24 @@ describe('UnifiedOrdersView co-load group behavior', () => { }) it('shows group number and member count and cancels only the whole ACTIVE group', async () => { - vi.mocked(batchQueryPlatformCoLoadGroups).mockResolvedValue(rows.map((row) => ({ orderId: row.orderId, inboundId: String(row.inboundId), group }))) - vi.mocked(cancelPlatformCoLoadGroup).mockResolvedValue({ ...group, status: 'CANCELLED', version: 1 }) + const multiInboundGroup = { + ...group, + fullPackageCount: 13, + members: [group.members[0], { ...group.members[0], inboundId: '7003', fullPackageCount: 3 }, group.members[1]], + } + vi.mocked(batchQueryPlatformCoLoadGroups).mockResolvedValue(rows.map((row) => ({ orderId: row.orderId, group: multiInboundGroup }))) + vi.mocked(cancelPlatformCoLoadGroup).mockResolvedValue({ ...multiInboundGroup, status: 'CANCELLED', version: 1 }) vi.spyOn(ElMessageBox, 'prompt').mockResolvedValue({ value: '计划调整', action: 'confirm' } as never) const wrapper = mountView() await flushPromises() expect(wrapper.text()).toContain('CLG-9001') - expect(wrapper.text()).toContain('2 票 · 未锁定') + expect(wrapper.text()).toContain('CLG-9001 · 2 票 · 未锁定') + await wrapper.get('[aria-label="查看同柜组 CLG-9001 详情"]').trigger('click') + expect(wrapper.text()).toContain('2 票 · 3 个入仓片段 · 未锁定') + expect(wrapper.text()).toContain('13 箱') + expect(wrapper.text()).toContain('7003') + expect(wrapper.findAll('[data-copy="7003"]')).toHaveLength(1) + expect(wrapper.findAll('[data-copy="5001"]')).toHaveLength(2) await wrapper.findAll('button').find((button) => button.text().includes('取消同柜组'))!.trigger('click') await flushPromises() expect(cancelPlatformCoLoadGroup).toHaveBeenCalledWith('9001', 0, '计划调整') diff --git a/src/modules/unified-orders/pages/UnifiedOrdersView.test.ts b/src/modules/unified-orders/pages/UnifiedOrdersView.test.ts index c1b71c2b..9458f6c2 100644 --- a/src/modules/unified-orders/pages/UnifiedOrdersView.test.ts +++ b/src/modules/unified-orders/pages/UnifiedOrdersView.test.ts @@ -95,7 +95,7 @@ describe('UnifiedOrdersView contract', () => { expect(source).toContain('v-model:page-size="pageSize"') const columnSource = source.slice(source.indexOf('const columns ='), source.indexOf('function emptyFilters')) const expectedColumnKeys = [ - 'actions', 'orderNo', 'inboundCode', 'coLoadGroup', 'mainStatus', 'customerName', 'receivingWarehouseName', + 'actions', 'orderNo', 'inboundCode', 'mainStatus', 'customerName', 'receivingWarehouseName', 'destinationWarehouseName', 'channelName', 'totalPackageCount', 'totalPalletCount', 'requestedPickupAt', 'locationCodes', 'measurementReviewStatus', 'actualGrossWeightKg', 'actualVolumeCbm', 'totalNetWeightKg', 'chargeableWeightKg', 'chargeableVolumeCbm', 'expectedShipDate', 'expectedArrivalAt', 'feeReviewStatus', 'skuNos', 'totalQuantity', 'expressCompany', 'expressTrackingNo', @@ -108,6 +108,8 @@ describe('UnifiedOrdersView contract', () => { ]) expect(columnSource).not.toContain(`key: '${uncontractedKey}'`) expect(columnSource.indexOf("key: 'actions'")).toBeLessThan(columnSource.indexOf("key: 'orderNo'")) expect(columnSource.indexOf("key: 'orderNo'")).toBeLessThan(columnSource.indexOf("key: 'inboundCode'")) + expect(columnSource).not.toContain("key: 'coLoadGroup'") + expect(columnSource).toContain("key: 'orderNo', prop: 'orderNo', label: '订单号', minWidth: 230, slotName: 'orderNo'") expect(columnSource).toContain("{ key: 'mainStatus', prop: 'mainStatus', label: '订单主状态'") expect(columnSource).not.toContain("key: 'loadingStatus'") expect(columnSource).not.toContain("key: 'businessDate'") @@ -160,6 +162,33 @@ describe('UnifiedOrdersView contract', () => { expect(source).toContain('') }) + it('states only the domestic warehouse and channel qualification without local destination or split blocking', () => { + const createSource = source.slice(source.indexOf('function openCoLoadCreate'), source.indexOf('async function batchApproveOrders')) + const dialogSource = source.slice(source.indexOf(' String(row.orderId)))') + }) + + it('embeds a clickable compact group tag under the order code without an empty placeholder column', () => { + expect(source).not.toContain("{ key: 'coLoadGroup', label: '同柜组'") + expect(source).toContain('\n - + @@ -1355,10 +1383,21 @@ onBeforeUnmount(() => { - +
{{ row.orderNo || row.inboundCode || row.orderId }}
+ + + @@ -1426,10 +1465,22 @@ onBeforeUnmount(() => { .table-card { overflow: hidden; } .coload-error { margin: 8px 12px; padding: 8px 10px; border: 1px solid var(--el-color-danger-light-5); border-radius: 4px; background: var(--el-color-danger-light-9); color: var(--el-color-danger); font-size: 12px; } .coload-error :deep(.api-error-meta) { margin-top: 5px; } -.coload-cell { display: grid; width: fit-content; gap: 3px; } -.coload-cell span { color: var(--cc-muted); font-size: 11px; } +.order-code-cell { display: grid; justify-items: start; gap: 4px; } +.coload-tag-trigger { margin: 0; padding: 0; border: 0; background: transparent; cursor: pointer; } +.coload-tag-trigger:focus-visible { outline: 2px solid var(--el-color-primary); outline-offset: 2px; border-radius: 3px; } .coload-selection { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 12px; } .coload-selection > span { padding: 4px 7px; border: 1px solid var(--cc-border); border-radius: 4px; background: var(--cc-surface-muted); font-size: 12px; } +.coload-detail-summary { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 10px; } +.coload-detail-scroll { overflow-x: auto; border: 1px solid var(--cc-border); border-radius: 4px; } +.coload-detail-table { width: 100%; border-collapse: collapse; font-size: 12px; } +.coload-detail-table th,.coload-detail-table td { padding: 8px 10px; border-bottom: 1px solid var(--cc-border); text-align: left; vertical-align: top; } +.coload-detail-table th { background: var(--cc-surface-muted); color: var(--cc-muted); font-weight: 600; } +.coload-detail-table tbody tr:last-child td { border-bottom: 0; } +.coload-detail-table td { min-width: 210px; } +.coload-detail-table td:last-child { min-width: 80px; white-space: nowrap; } +.coload-detail-table td strong,.coload-detail-table td span { display: block; overflow-wrap: anywhere; } +.coload-detail-table td span { color: var(--cc-muted); } +.coload-detail-table :deep(.el-button) { min-height: 24px; padding: 2px 0; } .table-card :deep(.el-table) { --el-table-border-color: #e5e7eb; --el-table-header-bg-color: #f9fafb; --el-table-row-hover-bg-color: #f3f7fb; --el-table-tr-bg-color: #fff; color: #374151; font-size: 12px; } .table-card :deep(.el-table th.el-table__cell) { height: 42px; padding: 0; background: #f9fafb; color: #374151; font-size: 12px; font-weight: 600; } .table-card :deep(.el-table td.el-table__cell) { min-height: 42px; padding: 9px 0; }