From ae5214195244921184270338ef6696fe1814e5a2 Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Sun, 30 Aug 2026 17:43:03 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 1 + .../components/ContainerCustomsPanel.vue | 2 +- .../pages/ContainerDetailView.vue | 8 +- .../pages/GlobalSmartLoadingView.vue | 4 +- .../pages/SmartLoadingWorkbenchView.vue | 2 +- src/modules/finance/index.ts | 8 +- .../finance/pages/FinanceCustomerArapView.vue | 12 ++- .../inbounds/components/InboundTaskList.vue | 23 ++++- .../components/InboundWorkflowWorkspace.vue | 2 +- .../InboundMeasurementDialog.test.ts | 3 +- .../measurement/InboundMeasurementDialog.vue | 2 +- .../measurement/InboundMeasurementPanel.vue | 2 +- .../inbounds/pages/InboundOrdersView.vue | 13 ++- .../review/MeasurementReviewPanel.vue | 4 +- .../components/ContainerFeesPanel.test.ts | 10 +- .../components/ContainerFeesPanel.vue | 17 +++- .../components/LoadingListDetailDrawer.vue | 8 +- src/modules/outbound-dispatch/index.ts | 1 + .../overseas-warehouse/api/inventory.test.ts | 7 ++ .../overseas-warehouse/api/inventory.ts | 2 + .../components/SmartDispatchDialog.vue | 2 +- .../pages/DeliveryOrdersView.vue | 13 ++- .../pages/DispatchesView.vue | 2 +- .../pages/ExceptionOrdersView.vue | 2 +- .../pages/InventoryTransactionsView.vue | 21 ++-- .../pages/LastMileOrderDetailView.vue | 2 +- .../pages/OutboundsView.vue | 2 +- .../pages/PalletsView.test.ts | 8 +- .../pages/RelabelOrderDetailView.vue | 2 +- .../pages/prototypeContract.test.ts | 4 +- .../pages/realApiMount.test.ts | 4 +- src/modules/overseas-warehouse/types.ts | 1 + .../suppliers/api/platformSuppliers.ts | 2 +- .../components/OrderFulfillmentTreePanel.vue | 2 +- .../pages/UnifiedOrdersView.coLoad.test.ts | 6 +- .../pages/UnifiedOrdersView.test.ts | 13 ++- .../pages/UnifiedOrdersView.vue | 30 ++++-- .../components/SmartLoadingWorkbench.vue | 2 +- .../UnifiedOrderWorkflowPanel.test.ts | 9 ++ .../components/UnifiedOrderWorkflowPanel.vue | 5 +- .../pages/UnifiedOrderWorkflowView.vue | 1 + src/shared/api/inboundCodeRelations.test.ts | 27 ++++++ src/shared/api/inboundCodeRelations.ts | 67 +++++++++++++ .../InboundCodeRelationshipDrawer.test.ts | 33 +++++++ .../InboundCodeRelationshipDrawer.vue | 95 +++++++++++++++++++ 45 files changed, 404 insertions(+), 82 deletions(-) create mode 100644 src/shared/api/inboundCodeRelations.test.ts create mode 100644 src/shared/api/inboundCodeRelations.ts create mode 100644 src/shared/components/InboundCodeRelationshipDrawer.test.ts create mode 100644 src/shared/components/InboundCodeRelationshipDrawer.vue diff --git a/AGENTS.md b/AGENTS.md index 520a332c..48e8b1b9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -48,6 +48,7 @@ | 入库、测量与计价主数据 | `modules/inbounds/`、`modules/fba-pricing/`、`modules/dimension-config/`、`modules/fee-items/` | `firstleg/`、`order/`、`file/`、`integration-service/` | | 财务 | `modules/finance/`,继续读该目录 `AGENTS.md` | `finance/`、`firstleg/`、`file/`、`docs/财务管理/` | | 货柜、排柜、装柜清单与整柜报关 | `modules/container-loading/`、`modules/loading-lists/`;详情 `pages/ContainerDetailView.vue`,资料/PDA 证据 `components/ContainerOverviewPanel.vue`,报关 `components/ContainerCustomsPanel.vue`、`api/platformContainerCustoms.ts` | 装柜完成后按 `loadingPlanId` 展示整柜退税/买单覆盖;平台开始报关即冻结客户资料版本与入仓码范围,逐资料上传一对一完成文件,最后一份要求二次确认;全买单柜使用 `complete-all-buy`。Backend `firstleg/customs/container/`、`customs-v1.yaml`,文件归 `file/` | +| 同柜不可拆组 | `modules/unified-orders/api/coLoadGroups.ts`、`pages/UnifiedOrdersView.vue`;装柜消费 `modules/container-loading/pages/SmartLoadingWorkbenchView.vue` | 前端只提交订单 ID;Backend `firstleg/loading/coload/` 展开并冻结每票全部可用入仓片段。成员数是入仓片段数,不等于订单票数;只要求同国内仓、同渠道,允许跨客户及不同目的仓港;分配、移除、取消均整组处理 | | 智能装柜按入仓码候选与箱数分配 | `modules/container-loading/pages/SmartLoadingWorkbenchView.vue`、`api/platformContainerLoading.ts` | Backend `firstleg/` 的 `PlatformSmartLoadingController`、`SmartLoadingService`;`GlobalSmartLoadingView.vue` 当前未注册路由,不作为正式入口 | | 三期订舱、关联候选与出仓前船期 | `modules/ocean-bookings/`、`modules/container-loading/`、`modules/port-schedules/` | `platformOceanBookings.ts` 的 `/link-candidates` 只消费服务端分页;列表 `containerCount` 是柜型需求 `quantity` 总和,不是已关联柜数;Backend `firstleg/oceanbooking/`、`firstleg/portschedule/`,出仓后节点归四期 | | 拖车、历史报关协同、国内出仓 | `modules/trucking/`、`modules/customs/`、`modules/outbound-dispatch/` | `modules/customs/` 是报关案件/清单/代理提交与回执入口;装柜完成后的整柜退税/买单资料改从货柜详情进入,不得在旧模块复制一套;Backend `firstleg/outbound/dispatch/`、`firstleg/customs/`、`file/` | diff --git a/src/modules/container-loading/components/ContainerCustomsPanel.vue b/src/modules/container-loading/components/ContainerCustomsPanel.vue index 1da973a0..40847c08 100644 --- a/src/modules/container-loading/components/ContainerCustomsPanel.vue +++ b/src/modules/container-loading/components/ContainerCustomsPanel.vue @@ -138,7 +138,7 @@ async function exportAllBuy() { actionLoading.value = true; actionError.value = null try { const task = await createPlatformExportTask({ - exportType: 'LOADING_LIST_BUY_DECLARATION_ITEMS', format: 'XLSX', taskName: `买单报关-${customs.value?.planNo || props.loadingPlanId}`, + exportType: 'LOADING_LIST_BUY_DECLARATION_ITEMS', format: 'XLSX', taskName: `买单报关-${customs.value?.planNo || '柜级明细'}`, parameters: { loadingPlanId: String(props.loadingPlanId) }, }) ElMessage.success(`买单报关导出任务已创建:${task.taskNo}`) diff --git a/src/modules/container-loading/pages/ContainerDetailView.vue b/src/modules/container-loading/pages/ContainerDetailView.vue index 1dcd262c..b35a549a 100644 --- a/src/modules/container-loading/pages/ContainerDetailView.vue +++ b/src/modules/container-loading/pages/ContainerDetailView.vue @@ -11,8 +11,12 @@ import AppDialog from '@/shared/components/AppDialog.vue' import LoadErrorState from '@/shared/components/LoadErrorState.vue' import StatusBadge from '@/shared/components/StatusBadge.vue' import { CarrierScheduleQueryPanel } from '@/modules/port-schedules' -import { confirmContainerOutboundDispatch, getContainerOutboundDispatch } from '@/modules/outbound-dispatch/api/platformOutboundDispatch' -import type { ContainerOutboundDispatchDetail, ContainerOutboundGateItem } from '@/modules/outbound-dispatch/types' +import { + confirmContainerOutboundDispatch, + getContainerOutboundDispatch, + type ContainerOutboundDispatchDetail, + type ContainerOutboundGateItem, +} from '@/modules/outbound-dispatch' import ContainerCustomsPanel from '../components/ContainerCustomsPanel.vue' import ContainerOverviewPanel from '../components/ContainerOverviewPanel.vue' import { diff --git a/src/modules/container-loading/pages/GlobalSmartLoadingView.vue b/src/modules/container-loading/pages/GlobalSmartLoadingView.vue index a48412ed..9ac4c355 100644 --- a/src/modules/container-loading/pages/GlobalSmartLoadingView.vue +++ b/src/modules/container-loading/pages/GlobalSmartLoadingView.vue @@ -88,7 +88,7 @@ type SmartLoadingCandidateRow = SmartLoadingCandidate & { sourceCandidates: Smar 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 current = orders.get(member.orderId) || { label: member.orderNo || '订单编码待补充', inboundCodes: [] } const inboundCode = member.inboundCode || '入仓码暂缺' if (!current.inboundCodes.includes(inboundCode)) current.inboundCodes.push(inboundCode) orders.set(member.orderId, current) @@ -626,7 +626,7 @@ onMounted(() => { void load(); void loadCustomers() }) 柜次号:{{ displayCabinetNo(item) }} 集装箱号:{{ displayActualContainerNo(item) }} 渠道:{{ item.channelName || item.channelCode || '渠道待补充' }} - 仓库:{{ item.warehouseName || item.warehouseId || '仓库待补充' }} · 柜型:{{ item.containerType || '柜型待补充' }} + 仓库:{{ item.warehouseName || '仓库待补充' }} · 柜型:{{ item.containerType || '柜型待补充' }} diff --git a/src/modules/container-loading/pages/SmartLoadingWorkbenchView.vue b/src/modules/container-loading/pages/SmartLoadingWorkbenchView.vue index 514e436d..2c45c06b 100644 --- a/src/modules/container-loading/pages/SmartLoadingWorkbenchView.vue +++ b/src/modules/container-loading/pages/SmartLoadingWorkbenchView.vue @@ -49,7 +49,7 @@ type SmartLoadingCandidateRow = SmartLoadingCandidate & { sourceCandidates: Smar 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 current = orders.get(member.orderId) || { label: member.orderNo || '订单编码待补充', inboundCodes: [] } const inboundCode = member.inboundCode || '入仓码暂缺' if (!current.inboundCodes.includes(inboundCode)) current.inboundCodes.push(inboundCode) orders.set(member.orderId, current) diff --git a/src/modules/finance/index.ts b/src/modules/finance/index.ts index 6cc4b774..7c88f2d5 100644 --- a/src/modules/finance/index.ts +++ b/src/modules/finance/index.ts @@ -1,3 +1,9 @@ export { financeRoutes } from './routes' export { default as CustomerStatementsPanel } from './components/CustomerStatementsPanel.vue' -export { getFinanceCustomerAccountSummary, type FinanceCustomerAccountSummaryResource } from './api/finance' +export { + getFinanceCustomerAccountSummary, + getFinanceSettlement, + listFinancePayableSettlements, + listFinanceReceivableSettlements, + type FinanceCustomerAccountSummaryResource, +} from './api/finance' diff --git a/src/modules/finance/pages/FinanceCustomerArapView.vue b/src/modules/finance/pages/FinanceCustomerArapView.vue index 8dac9f43..14a181c9 100644 --- a/src/modules/finance/pages/FinanceCustomerArapView.vue +++ b/src/modules/finance/pages/FinanceCustomerArapView.vue @@ -1,5 +1,5 @@ diff --git a/src/modules/overseas-warehouse/pages/DispatchesView.vue b/src/modules/overseas-warehouse/pages/DispatchesView.vue index 1573ee4a..405a2606 100644 --- a/src/modules/overseas-warehouse/pages/DispatchesView.vue +++ b/src/modules/overseas-warehouse/pages/DispatchesView.vue @@ -117,7 +117,7 @@ function toListItem(dto: DispatchSummaryDto): DispatchListItem { carrierName: text(dto.carrierName), orderNos: text(dto.orderNos), route: text(dto.route), - overseasWarehouse: text(dto.overseasWarehouseName || dto.overseasWarehouseId), + overseasWarehouse: text(dto.overseasWarehouseName || '仓库名称待补充'), departAt: text(dto.departAt), fbaAppointment: appointmentLabel(dto.fbaAppointmentNo, dto.fbaAppointmentStatus), vehicleAppointment: text(dto.vehicleAppointmentStatus), diff --git a/src/modules/overseas-warehouse/pages/ExceptionOrdersView.vue b/src/modules/overseas-warehouse/pages/ExceptionOrdersView.vue index 284a59fb..5771eee4 100644 --- a/src/modules/overseas-warehouse/pages/ExceptionOrdersView.vue +++ b/src/modules/overseas-warehouse/pages/ExceptionOrdersView.vue @@ -88,7 +88,7 @@ onMounted(load) - + diff --git a/src/modules/overseas-warehouse/pages/InventoryTransactionsView.vue b/src/modules/overseas-warehouse/pages/InventoryTransactionsView.vue index c8927269..de7625a1 100644 --- a/src/modules/overseas-warehouse/pages/InventoryTransactionsView.vue +++ b/src/modules/overseas-warehouse/pages/InventoryTransactionsView.vue @@ -3,32 +3,39 @@ import { computed, onMounted, ref } from 'vue' import { ArrowLeft } from '@lucide/vue' import { useRoute, useRouter } from 'vue-router' import { getApiErrorDetails, type ApiErrorDetails } from '@/shared/api/http' +import { useAccessPreviewStore } from '@/core/access/accessPreview' import AdminPage from '@/shared/components/AdminPage.vue'; import ApiErrorMeta from '@/shared/components/ApiErrorMeta.vue'; import DataTable, { type DataTableColumn } from '@/shared/components/DataTable.vue'; import ListPageLayout from '@/shared/components/ListPageLayout.vue'; import LoadErrorState from '@/shared/components/LoadErrorState.vue'; import QueryToolbar, { type QueryField, type QueryModel } from '@/shared/components/QueryToolbar.vue' +import InboundCodeRelationshipDrawer from '@/shared/components/InboundCodeRelationshipDrawer.vue' import { listInventoryLedger } from '../api/inventory'; import { formatOverseasDateTime } from '../lastMilePresentation'; import type { InventoryTransactionListItem } from '../types' -const route = useRoute(); const router = useRouter(); const rows = ref([]); const page = ref(1); const pageSize = ref(10); const total = ref(0); const loading = ref(false); const loadError = ref(null) -const filters = ref({ occurredDate: [], warehouse: '', sku: '', businessType: '', businessOrderNo: '', inboundOrderNo: '', outboundOrderNo: '' }) +const route = useRoute(); const router = useRouter(); const access = useAccessPreviewStore(); const rows = ref([]); const page = ref(1); const pageSize = ref(10); const total = ref(0); const loading = ref(false); const loadError = ref(null) +const relationshipOpen = ref(false); const relationshipInboundCode = ref('') +const filters = ref({ occurredDate: [], customerName: '', warehouse: '', sku: '', businessType: '', businessOrderNo: '', inboundCode: '', inboundOrderNo: '', outboundOrderNo: '' }) const fields = computed(() => [ { key: 'occurredDate', type: 'daterange', label: '发生日期', width: 260 }, + { key: 'customerName', type: 'input', label: '客户名称', placeholder: '请输入客户名称', width: 190 }, { key: 'warehouse', type: 'input', label: '目的仓代码', placeholder: '请输入目的仓代码', width: 190 }, { key: 'sku', type: 'input', label: 'SKU编码', placeholder: '请输入 SKU 编码', width: 170 }, { key: 'businessType', type: 'input', label: '业务类型', placeholder: '请输入业务类型代码', width: 170 }, { key: 'businessOrderNo', type: 'input', label: '业务订单号', placeholder: '请输入业务订单号', width: 180 }, - { key: 'inboundOrderNo', type: 'input', label: '入库单号', placeholder: '请输入入库单号', width: 180 }, { key: 'outboundOrderNo', type: 'input', label: '出库单号', placeholder: '只读关联筛选', width: 180 }, + { key: 'inboundCode', type: 'input', label: '入仓码', placeholder: '请输入入仓码', width: 190 }, { key: 'inboundOrderNo', type: 'input', label: '入库单号', placeholder: '请输入入库单号', width: 180 }, { key: 'outboundOrderNo', type: 'input', label: '出库单号', placeholder: '只读关联筛选', width: 180 }, ]) const columns: DataTableColumn[] = [ - { key: 'occurredAt', prop: 'occurredAt', label: '发生时间', minWidth: 160 }, { key: 'owner', prop: 'owner', label: '客户', minWidth: 160 }, { key: 'warehouse', prop: 'warehouse', label: '海外仓', minWidth: 180 }, + { key: 'actions', label: '操作', width: 108, fixed: 'left', slotName: 'actions' }, + { key: 'occurredAt', prop: 'occurredAt', label: '发生时间', minWidth: 160 }, { key: 'owner', prop: 'owner', label: '客户', minWidth: 160 }, { key: 'warehouse', prop: 'warehouse', label: '目的仓编码', minWidth: 180 }, { key: 'sku', prop: 'sku', label: 'SKU编码', minWidth: 150 }, { key: 'productName', prop: 'productName', label: '中文品名', minWidth: 180 }, { key: 'businessType', prop: 'businessType', label: '业务类型', width: 120 }, - { key: 'businessOrderNo', prop: 'businessOrderNo', label: '业务订单号', minWidth: 170 }, { key: 'inboundOrderNo', prop: 'inboundOrderNo', label: '入库单号', minWidth: 160 }, { key: 'outboundOrderNo', prop: 'outboundOrderNo', label: '出库单号', minWidth: 160 }, + { key: 'businessOrderNo', prop: 'businessOrderNo', label: '业务订单号', minWidth: 170 }, { key: 'inboundCode', prop: 'inboundCode', label: '入仓码', minWidth: 170 }, { key: 'inboundOrderNo', prop: 'inboundOrderNo', label: '入库单号', minWidth: 160 }, { key: 'outboundOrderNo', prop: 'outboundOrderNo', label: '出库单号', minWidth: 160 }, { key: 'inboundQuantity', prop: 'inboundQuantity', label: '入库数量', width: 110, align: 'right' }, { key: 'outboundQuantity', prop: 'outboundQuantity', label: '出库数量', width: 110, align: 'right' }, { key: 'balance', prop: 'balance', label: '结存', width: 100, align: 'right' }, { key: 'operator', prop: 'operator', label: '操作人', minWidth: 120 }, { key: 'remark', prop: 'remark', label: '备注', minWidth: 180 }, ] function text(value: unknown) { return String(value ?? '').trim() || '—' } -async function load(model: QueryModel = filters.value) { Object.assign(filters.value, model); const range = Array.isArray(filters.value.occurredDate) ? filters.value.occurredDate.map(String) : []; loading.value = true; loadError.value = null; try { const result = await listInventoryLedger({ page: page.value, size: pageSize.value, destinationWarehouseCode: String(filters.value.warehouse || '').trim() || undefined, skuCode: String(filters.value.sku || '').trim() || undefined, businessType: String(filters.value.businessType || '').trim() || undefined, businessOrderNo: String(filters.value.businessOrderNo || '').trim() || undefined, inboundOrderNo: String(filters.value.inboundOrderNo || '').trim() || undefined, outboundOrderNo: String(filters.value.outboundOrderNo || '').trim() || undefined, occurredFrom: range[0] ? `${range[0]}T00:00:00` : undefined, occurredTo: range[1] ? `${range[1]}T23:59:59` : undefined }); rows.value = result.records.map((item) => ({ id: String(item.id), occurredAt: formatOverseasDateTime(item.occurredAt), owner: text(item.customerId), warehouse: text(item.destinationWarehouseCode), sku: text(item.skuCode), productName: '—', businessType: text(item.businessType), businessOrderNo: text(item.businessOrderNo), inboundOrderNo: text(item.inboundOrderNo), outboundOrderNo: text(item.outboundOrderNo), inboundQuantity: item.deltaPackages > 0 ? item.deltaPackages : 0, outboundQuantity: item.deltaPackages < 0 ? Math.abs(item.deltaPackages) : 0, balance: Number(item.afterPackages || 0), operator: text(item.operatorId), remark: text(item.remark) })); total.value = result.total } catch (error) { rows.value = []; total.value = 0; loadError.value = getApiErrorDetails(error, '进销存记录加载失败,请重试') } finally { loading.value = false } } +function openRelationship(row: InventoryTransactionListItem) { if (row.inboundCode === '—') return; relationshipInboundCode.value = row.inboundCode; relationshipOpen.value = true } +async function load(model: QueryModel = filters.value) { Object.assign(filters.value, model); const range = Array.isArray(filters.value.occurredDate) ? filters.value.occurredDate.map(String) : []; loading.value = true; loadError.value = null; try { const result = await listInventoryLedger({ page: page.value, size: pageSize.value, customerName: String(filters.value.customerName || '').trim() || undefined, destinationWarehouseCode: String(filters.value.warehouse || '').trim() || undefined, skuCode: String(filters.value.sku || '').trim() || undefined, businessType: String(filters.value.businessType || '').trim() || undefined, businessOrderNo: String(filters.value.businessOrderNo || '').trim() || undefined, inboundCode: String(filters.value.inboundCode || '').trim() || undefined, inboundOrderNo: String(filters.value.inboundOrderNo || '').trim() || undefined, outboundOrderNo: String(filters.value.outboundOrderNo || '').trim() || undefined, occurredFrom: range[0] ? `${range[0]}T00:00:00` : undefined, occurredTo: range[1] ? `${range[1]}T23:59:59` : undefined }); rows.value = result.records.map((item) => ({ id: String(item.id), occurredAt: formatOverseasDateTime(item.occurredAt), owner: text(item.customerName || '客户信息待补充'), warehouse: text(item.destinationWarehouseCode), sku: text(item.skuCode), productName: '—', businessType: text(item.businessType), businessOrderNo: text(item.businessOrderNo), inboundCode: text(item.inboundCode), inboundOrderNo: text(item.inboundOrderNo), outboundOrderNo: text(item.outboundOrderNo), inboundQuantity: item.deltaPackages > 0 ? item.deltaPackages : 0, outboundQuantity: item.deltaPackages < 0 ? Math.abs(item.deltaPackages) : 0, balance: Number(item.afterPackages || 0), operator: item.operatorId ? '操作人名称待补充' : '—', remark: text(item.remark) })); total.value = result.total } catch (error) { rows.value = []; total.value = 0; loadError.value = getApiErrorDetails(error, '进销存记录加载失败,请重试') } finally { loading.value = false } } function search(model: QueryModel) { page.value = 1; void load(model) }; function reset(model: QueryModel) { page.value = 1; void load(model) }; function back() { void router.push({ name: 'overseas-inventory' }) }; onMounted(load) diff --git a/src/modules/overseas-warehouse/pages/LastMileOrderDetailView.vue b/src/modules/overseas-warehouse/pages/LastMileOrderDetailView.vue index eca3d512..4dbdca51 100644 --- a/src/modules/overseas-warehouse/pages/LastMileOrderDetailView.vue +++ b/src/modules/overseas-warehouse/pages/LastMileOrderDetailView.vue @@ -39,7 +39,7 @@ function text(value: unknown) { return String(value ?? '').trim() || '—' } function yesNo(value: boolean | null | undefined) { return value == null ? '—' : value ? '是' : '否' } function feeStatusLabel(value: string) { return ({ PENDING_REVIEW: '待审核', APPROVED: '审核通过', REJECTED: '已驳回' } as Record)[value] || value } -const referenceValue = computed(() => text(detail.value?.order.orderNo || orderId.value)) +const referenceValue = computed(() => text(detail.value?.order.orderNo || '订单编码待补充')) const orderFields = computed(() => { const order = detail.value?.order return [ diff --git a/src/modules/overseas-warehouse/pages/OutboundsView.vue b/src/modules/overseas-warehouse/pages/OutboundsView.vue index 6cb44834..976e5725 100644 --- a/src/modules/overseas-warehouse/pages/OutboundsView.vue +++ b/src/modules/overseas-warehouse/pages/OutboundsView.vue @@ -36,5 +36,5 @@ onMounted(load) - + diff --git a/src/modules/overseas-warehouse/pages/PalletsView.test.ts b/src/modules/overseas-warehouse/pages/PalletsView.test.ts index 1968a7ca..ea0a8631 100644 --- a/src/modules/overseas-warehouse/pages/PalletsView.test.ts +++ b/src/modules/overseas-warehouse/pages/PalletsView.test.ts @@ -57,12 +57,8 @@ describe('PalletsView', () => { await wrapper.findAll('button').find((button) => button.text().includes('新增托盘'))!.trigger('click') await flushPromises() - const warehouse = wrapper.findAllComponents({ name: 'ElSelect' }).find((item) => item.props('placeholder') === '请选择海外仓')! - const fbaWarehouse = wrapper.findAllComponents({ name: 'ElSelect' }).find((item) => item.props('placeholder') === '请选择所属 FBA 仓')! - const palletCode = wrapper.findAllComponents({ name: 'ElInput' }).find((item) => item.props('placeholder') === '请输入托盘编码')! - warehouse.vm.$emit('update:modelValue', '200') - fbaWarehouse.vm.$emit('update:modelValue', 'ABE3') - palletCode.vm.$emit('update:modelValue', 'PLT-LAX-ABE3') + const setupState = wrapper.vm.$.setupState as { form: { warehouseId: string; fbaWarehouseCode: string; code: string } } + Object.assign(setupState.form, { warehouseId: '200', fbaWarehouseCode: 'ABE3', code: 'PLT-LAX-ABE3' }) await flushPromises() wrapper.findComponent(AppDialog).vm.$emit('confirm') await flushPromises() diff --git a/src/modules/overseas-warehouse/pages/RelabelOrderDetailView.vue b/src/modules/overseas-warehouse/pages/RelabelOrderDetailView.vue index b9e05f93..da38e0f1 100644 --- a/src/modules/overseas-warehouse/pages/RelabelOrderDetailView.vue +++ b/src/modules/overseas-warehouse/pages/RelabelOrderDetailView.vue @@ -25,7 +25,7 @@ function status(value: unknown) { const labels: Record = { PENDING_PROCESSING: '待处理', PROCESSING: '处理中', COMPLETED: '已完成', REJECTED: '已驳回', CANCELLED: '已取消' } return labels[String(value || '')] || text(value) } -const referenceValue = computed(() => text(detail.value?.application.relabelNo || orderId.value)) +const referenceValue = computed(() => text(detail.value?.application.relabelNo || '换标单号待补充')) const fieldValues = computed>(() => { const value = detail.value const items = value?.items || [] diff --git a/src/modules/overseas-warehouse/pages/prototypeContract.test.ts b/src/modules/overseas-warehouse/pages/prototypeContract.test.ts index 684e896c..904b6d87 100644 --- a/src/modules/overseas-warehouse/pages/prototypeContract.test.ts +++ b/src/modules/overseas-warehouse/pages/prototypeContract.test.ts @@ -354,8 +354,8 @@ describe('overseas warehouse first-batch prototype contract', () => { 'actions', 'sku', 'productName', 'englishName', 'category', 'length', 'width', 'height', 'weight', ]) expect(columnKeys('InventoryTransactionsView.vue')).toEqual([ - 'occurredAt', 'owner', 'warehouse', 'sku', 'productName', 'businessType', 'businessOrderNo', - 'inboundOrderNo', 'outboundOrderNo', 'inboundQuantity', 'outboundQuantity', 'balance', 'operator', 'remark', + 'actions', 'occurredAt', 'owner', 'warehouse', 'sku', 'productName', 'businessType', 'businessOrderNo', + 'inboundCode', 'inboundOrderNo', 'outboundOrderNo', 'inboundQuantity', 'outboundQuantity', 'balance', 'operator', 'remark', ]) }) }) diff --git a/src/modules/overseas-warehouse/pages/realApiMount.test.ts b/src/modules/overseas-warehouse/pages/realApiMount.test.ts index e194e034..f1cb0aa4 100644 --- a/src/modules/overseas-warehouse/pages/realApiMount.test.ts +++ b/src/modules/overseas-warehouse/pages/realApiMount.test.ts @@ -25,10 +25,10 @@ describe('overseas warehouse mounted real API pages', () => { vi.stubGlobal('ResizeObserver', ResizeObserverStub); vi.stubGlobal('localStorage', memoryStorage()); routing.push.mockReset(); routing.route.params = {}; routing.route.query = {} vi.mocked(listPlatformTenants).mockReset().mockResolvedValue({ records: [{ id: '10001', tenantName: '客户甲', status: 'ACTIVE' } as never], page: 1, size: 200, total: 1, pages: 1 }) vi.mocked(getInboundPlan).mockReset().mockResolvedValue({ id: '9007199254740993', planNo: 'IP-1', customerId: '10001', sourceOrderId: '9007199254740995', status: 'PENDING_ASSIGNMENT', lineCount: 1, palletCount: 1, totalVolumeCbm: 2, lines: [{ id: '1', orderId: '9007199254740995', inboundCode: 'IN-1', fbaWarehouseCode: 'ONT8', destinationWarehouseName: '洛杉矶仓', volumeCbm: 2, requiredPackageCount: 10, plannedPalletCount: 1 }], pallets: [], allocations: [] }) - vi.mocked(listInventoryLedger).mockReset().mockResolvedValue({ records: [{ id: '9007199254740997', customerId: '10001', destinationWarehouseCode: 'LAX', inboundOrderNo: 'IP-1', businessType: 'STOCK_IN', beforePackages: 0, deltaPackages: 10, afterPackages: 10, occurredAt: '2026-08-20T10:00:00' }], page: 1, size: 20, total: 1, pages: 1 }) + vi.mocked(listInventoryLedger).mockReset().mockResolvedValue({ records: [{ id: '9007199254740997', customerId: '10001', customerName: '客户甲', destinationWarehouseCode: 'LAX', inboundCode: 'NYEXX2608300001', inboundOrderNo: 'IP-1', businessType: 'STOCK_IN', beforePackages: 0, deltaPackages: 10, afterPackages: 10, occurredAt: '2026-08-20T10:00:00' }], page: 1, size: 20, total: 1, pages: 1 }) vi.mocked(getLastMileOrder).mockReset().mockResolvedValue({ order: { id: '9007199254740999', customerId: '10001', customerName: '华远贸易', orderNo: 'LMO-260827-0001', currentStatus: 'PENDING_ASSIGNMENT' }, cargo: [], statusHistory: [], fees: [], operationHistory: [] }) }) it('mounts inbound detail with a string-safe source order id', async () => { routing.route.params = { orderId: 'IP-1' }; routing.route.query = { customerId: '10001' }; const wrapper = mount(InboundOrderDetailView, { global: { plugins: [ElementPlus] } }); await flushPromises(); expect(getInboundPlan).toHaveBeenCalledWith('IP-1', '10001'); expect(wrapper.text()).toContain('9007199254740995'); expect(wrapper.text()).toContain('洛杉矶仓') }) - it('mounts inventory ledger as read-only facts', async () => { const wrapper = mount(InventoryTransactionsView, { global: { plugins: [ElementPlus] } }); await flushPromises(); expect(listInventoryLedger).toHaveBeenCalledWith(expect.objectContaining({ page: 1, size: 10 })); expect(listInventoryLedger).not.toHaveBeenCalledWith(expect.objectContaining({ customerId: expect.anything() })); expect(wrapper.text()).toContain('IP-1'); expect(wrapper.text()).toContain('10'); expect(wrapper.text()).toContain('不提供出库单跳转') }) + it('mounts inventory ledger as read-only business facts without exposing customer ids', async () => { const wrapper = mount(InventoryTransactionsView, { global: { plugins: [createPinia(), ElementPlus] } }); await flushPromises(); expect(listInventoryLedger).toHaveBeenCalledWith(expect.objectContaining({ page: 1, size: 10 })); expect(listInventoryLedger).not.toHaveBeenCalledWith(expect.objectContaining({ customerId: expect.anything() })); expect(wrapper.text()).toContain('客户甲'); expect(wrapper.text()).toContain('NYEXX2608300001'); expect(wrapper.text()).not.toContain('10001'); expect(wrapper.text()).toContain('IP-1'); expect(wrapper.text()).toContain('10'); expect(wrapper.text()).toContain('不提供出库单跳转') }) it('renders the backend owner and complete short order number in last-mile detail', async () => { routing.route.params = { orderId: '9007199254740999' }; const wrapper = mount(LastMileOrderDetailView, { global: { plugins: [createPinia(), ElementPlus] } }); await flushPromises(); expect(getLastMileOrder).toHaveBeenCalledWith('9007199254740999'); expect(wrapper.text()).toContain('华远贸易'); expect(wrapper.text()).toContain('LMO-260827-0001'); expect(wrapper.get('.order-reference-text').attributes('title')).toBe('LMO-260827-0001') }) }) diff --git a/src/modules/overseas-warehouse/types.ts b/src/modules/overseas-warehouse/types.ts index 3f69f128..919b2935 100644 --- a/src/modules/overseas-warehouse/types.ts +++ b/src/modules/overseas-warehouse/types.ts @@ -176,6 +176,7 @@ export interface InventoryTransactionListItem { productName: string businessType: string businessOrderNo: string + inboundCode: string inboundOrderNo: string /** Read-only inventory fact; it is not an outbound-workflow link. */ outboundOrderNo: string diff --git a/src/modules/suppliers/api/platformSuppliers.ts b/src/modules/suppliers/api/platformSuppliers.ts index 53190823..dedb058d 100644 --- a/src/modules/suppliers/api/platformSuppliers.ts +++ b/src/modules/suppliers/api/platformSuppliers.ts @@ -9,7 +9,7 @@ function normalizeSupplier(raw: Supplier): Supplier { return { ...raw, id: String(raw.id), tenantId: String(raw.tenantId), - carrierCodes: [...raw.carrierCodes], + carrierCodes: [...(raw.carrierCodes || [])], licenseFileId: id(raw.licenseFileId), createdBy: id(raw.createdBy), updatedBy: id(raw.updatedBy), version: Number(raw.version), deleted: Boolean(raw.deleted), } diff --git a/src/modules/unified-orders/components/OrderFulfillmentTreePanel.vue b/src/modules/unified-orders/components/OrderFulfillmentTreePanel.vue index d49f8360..e66c1cbe 100644 --- a/src/modules/unified-orders/components/OrderFulfillmentTreePanel.vue +++ b/src/modules/unified-orders/components/OrderFulfillmentTreePanel.vue @@ -44,7 +44,7 @@ onMounted(() => { void load() }) - + diff --git a/src/modules/unified-orders/pages/UnifiedOrdersView.coLoad.test.ts b/src/modules/unified-orders/pages/UnifiedOrdersView.coLoad.test.ts index 2945df33..95bb9409 100644 --- a/src/modules/unified-orders/pages/UnifiedOrdersView.coLoad.test.ts +++ b/src/modules/unified-orders/pages/UnifiedOrdersView.coLoad.test.ts @@ -92,9 +92,9 @@ describe('UnifiedOrdersView co-load group behavior', () => { 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) + expect(wrapper.text()).toContain('入仓码待补充') + expect(wrapper.text()).not.toContain('7003') + expect(wrapper.text()).not.toContain('5001') 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 9458f6c2..f40ce574 100644 --- a/src/modules/unified-orders/pages/UnifiedOrdersView.test.ts +++ b/src/modules/unified-orders/pages/UnifiedOrdersView.test.ts @@ -183,10 +183,12 @@ describe('UnifiedOrdersView contract', () => { expect(source).toContain('coLoadGroupOrderCount(coLoadGroup(row as UnifiedOrderResource)!)') expect(source).not.toContain('—\n @@ -1354,6 +1364,7 @@ onBeforeUnmount(() => { - + - + @@ -1384,7 +1395,7 @@ onBeforeUnmount(() => { -
{{ row.orderNo || row.inboundCode || row.orderId }}
+
{{ row.orderNo || row.inboundCode || '业务编码待补充' }}
@@ -1392,8 +1403,8 @@ onBeforeUnmount(() => {
{{ coLoadGroupOrderCount(coLoadDetailGroup) }} 票 · {{ coLoadDetailGroup.members.length }} 个入仓片段 · {{ coLoadGroupStatusLabel(coLoadDetailGroup) }}共 {{ coLoadDetailGroup.fullPackageCount }} 箱
- - + +
入仓码 / 入仓 ID订单编码 / ID箱数
{{ visibleInboundCode(member.inboundId) || member.inboundId }}ID {{ member.inboundId }}{{ visibleOrderNo(member.orderId) || member.orderId }}ID {{ member.orderId }}{{ member.fullPackageCount }} 箱
入仓码订单编码箱数
{{ visibleInboundCode(member.inboundId) || '入仓码待补充' }}{{ visibleOrderNo(member.orderId) || '订单编码待补充' }}{{ member.fullPackageCount }} 箱
@@ -1408,6 +1419,7 @@ onBeforeUnmount(() => { :inbound-code="relationOrder.inboundCode" /> + diff --git a/src/modules/unified-orders/workflows/components/SmartLoadingWorkbench.vue b/src/modules/unified-orders/workflows/components/SmartLoadingWorkbench.vue index c14f2a31..fd1a6d94 100644 --- a/src/modules/unified-orders/workflows/components/SmartLoadingWorkbench.vue +++ b/src/modules/unified-orders/workflows/components/SmartLoadingWorkbench.vue @@ -275,7 +275,7 @@ onMounted(() => { void load() })