From e41e9e6d3fbd6a1f6948e415dc3e3cfc262c0a3e Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Wed, 28 Oct 2026 10:32:33 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=9F=E4=B8=80=E5=90=8E=E5=8F=B0=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E5=88=97=E9=A1=BA=E5=BA=8F=E3=80=81=E8=AF=A6=E6=83=85?= =?UTF-8?q?=E5=B8=83=E5=B1=80=E4=B8=8E=E6=B5=8B=E9=87=8F=E8=AE=A1=E8=B4=B9?= =?UTF-8?q?=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/business-index.md | 2 +- src/core/navigation/adminNavigation.test.ts | 4 +- .../AppointmentCreatePanel.vue | 2 +- .../WorkExistingDispatchForm.vue | 2 +- .../appointment-calendar/WorkReceiptPlans.vue | 2 +- .../components/ContainerCustomsPanel.test.ts | 2 +- .../components/ContainerCustomsPanel.vue | 8 +-- .../components/ContainerLastLegFeesPanel.vue | 2 +- .../components/ContainerOverviewPanel.test.ts | 4 +- .../components/ContainerOverviewPanel.vue | 6 +- .../pages/ContainersView.test.ts | 4 +- .../pages/ContainersView.vue | 10 ++-- .../pages/GlobalSmartLoadingView.vue | 2 +- .../pages/SmartLoadingWorkbenchView.vue | 2 +- .../CustomerBillingRulesPanel.test.ts | 12 ++++ .../components/CustomerBillingRulesPanel.vue | 6 +- .../customers/pages/CustomerDetailView.vue | 4 +- src/modules/customers/pages/CustomersView.vue | 2 +- .../customs/pages/CustomsCasesView.vue | 2 +- .../pages/DomesticContainersView.vue | 4 +- .../downloads/pages/DownloadCenterView.vue | 2 +- .../carrier-rates/pages/CarrierRatesView.vue | 2 +- src/modules/fee-items/pages/FeeItemsView.vue | 2 +- .../components/CustomerStatementsPanel.vue | 2 +- .../pages/FinanceCustomerAccountsView.test.ts | 4 +- .../pages/FinanceCustomerAccountsView.vue | 12 ++-- .../pages/FinanceCustomerArapView.test.ts | 4 +- .../finance/pages/FinanceCustomerArapView.vue | 12 ++-- .../FinanceCustomerStatementsView.test.ts | 2 +- .../pages/FinanceCustomerStatementsView.vue | 8 +-- .../pages/FinanceFormalSettlementPage.vue | 10 ++-- .../pages/FinanceSettlementDialogs.test.ts | 15 +++++ .../FinanceSettlementRouteLayout.test.ts | 1 + src/modules/iam/pages/InternalUsersView.vue | 2 +- src/modules/iam/pages/PermissionsView.vue | 2 +- src/modules/iam/pages/RolesView.vue | 2 +- .../inbounds/components/InboundTaskList.vue | 4 +- .../InboundMeasurementPanel.test.ts | 2 +- .../measurement/InboundMeasurementPanel.vue | 2 +- .../inbounds/pages/InboundOrdersView.vue | 14 ++--- .../components/ContainerFeesPanel.vue | 4 +- .../components/LoadingListsView.vue | 4 +- .../pages/ConfirmedLoadingListDetailView.vue | 4 +- .../counterparties/pages/PartnersView.vue | 2 +- .../pages/ExpressCompaniesView.vue | 2 +- .../pages/MessageManagementView.vue | 4 +- .../pages/MessageTemplatesView.vue | 2 +- .../pages/PlatformNotificationsView.vue | 2 +- .../pages/OceanBookingsView.vue | 2 +- .../pages/OutboundDispatchesView.vue | 8 +-- .../components/BusinessOrderFees.vue | 4 +- .../components/OverseasFeeEditor.vue | 4 +- .../components/ProcurementWorkflowPanel.vue | 6 +- .../overseasBusinessDensity.css | 1 - .../pages/OverseasBusinessViews.test.ts | 16 +++-- .../pages/OverseasFeesView.vue | 4 +- .../pages/PurchaseOrdersView.vue | 2 +- .../pages/TransportOrdersView.vue | 8 +-- .../pages/TruckOrderDetailView.vue | 12 ++-- .../components/CapacityInquiryTable.vue | 6 +- .../components/TripBookingPanel.vue | 2 +- .../components/TripPickupPanel.vue | 2 +- .../components/VehicleCareTable.vue | 5 +- .../components/VehicleFulfillmentTable.vue | 2 +- .../pages/CapacityOffersView.vue | 4 +- .../pages/DispatchCapacityDemandsView.vue | 4 +- .../pages/DriverDetailView.vue | 2 +- .../pages/DriversView.vue | 4 +- .../pages/ExchangeRatesView.vue | 4 +- .../pages/FleetQuotesView.vue | 4 +- .../pages/OutsourcedBusinessView.vue | 2 +- .../pages/OverseasQuotesView.vue | 4 +- .../pages/OwnedBusinessView.vue | 2 +- .../pages/TripDetailView.vue | 8 +-- .../pages/TripOperationsView.vue | 2 +- .../overseas-master-data/pages/TripsView.vue | 4 +- .../pages/VehiclesView.vue | 2 +- .../components/DeliveryDocumentsDialog.vue | 4 +- .../DispatchVehicleMaintenanceFees.vue | 2 +- .../components/TransportExecutionPanel.vue | 4 +- .../WarehouseTransferFulfillmentPanel.vue | 2 +- .../WarehouseTransferReceiptPanel.vue | 2 +- .../pages/CargoDiscardsView.vue | 2 +- .../overseas-warehouse/pages/CartonsView.vue | 4 +- .../pages/CustomerVisitsView.vue | 2 +- .../pages/DeliveryOrderDetailView.vue | 4 +- .../pages/DeliveryOrdersView.vue | 2 +- .../pages/DispatchesView.vue | 6 +- .../pages/ExceptionOrdersView.test.ts | 27 +++++++++ .../pages/ExceptionOrdersView.vue | 2 +- .../pages/InboundOrderDetailView.vue | 2 +- .../pages/InboundOrdersView.vue | 8 +-- .../pages/InventoryDetailView.vue | 2 +- .../pages/LastMileOrderDetailView.vue | 4 +- .../pages/LastMileOrdersView.vue | 14 ++--- .../pages/LocationsView.vue | 2 +- .../pages/MojiaSkuSyncView.vue | 4 +- .../pages/OutboundDetailView.vue | 2 +- .../pages/OutboundsView.vue | 2 +- .../overseas-warehouse/pages/PalletsView.vue | 6 +- .../pages/RelabelOrdersView.vue | 4 +- .../pages/ReturnInventoryManagementView.vue | 4 +- .../pda-releases/pages/PdaReleasesView.vue | 2 +- .../pages/PortSchedulesView.vue | 4 +- .../pages/SailingCalendarView.vue | 4 +- .../components/AnalyticsView.vue | 2 +- .../components/DashboardTrend.vue | 2 +- .../self-operated/components/SkuTrend.vue | 4 +- src/modules/suppliers/pages/SuppliersView.vue | 2 +- .../trucking/pages/TruckAppointmentsView.vue | 2 +- .../components/InboundFeeDetailManager.vue | 4 +- .../components/OrderFulfillmentTreePanel.vue | 2 +- .../UnifiedOrdersView.fulfillment.test.ts | 58 ++++++++++++++++++- .../pages/UnifiedOrdersView.test.ts | 4 +- .../pages/UnifiedOrdersView.vue | 12 ++-- .../locations/pages/LocationsView.vue | 2 +- .../warehouses/pages/WarehousesView.vue | 2 +- src/shared/components/DataTable.test.ts | 50 ++++++++++++++++ src/shared/components/DataTable.vue | 26 ++++++++- src/styles/adminDensity.test.ts | 35 ++++++----- 120 files changed, 436 insertions(+), 238 deletions(-) diff --git a/docs/business-index.md b/docs/business-index.md index ad53b656..0153d172 100644 --- a/docs/business-index.md +++ b/docs/business-index.md @@ -29,7 +29,7 @@ | 订单列表、批量审核预览、详情、商品图片与入库费用 | `modules/unified-orders/`、`modules/first-leg-orders/`;平台商品图片由 `modules/unified-orders/api/unifiedOrders.ts` 按客户与 fileId 下载 | `order/`、`firstleg/`、Finance;旧系统导入固定归属“IT测试公司”,前端不提交或选择客户,逐行结果展示入仓码与失败原因。图片读取归 `file/` 的 `PlatformProductImageController`。Order 状态为 `SUBMITTED` 时统一列表必须保持 `PENDING_REVIEW/APPROVE_ORDER`,不得被已存在的 Firstleg 准备中入库投影覆盖。批量审核弹框通过 `unifiedOrders.ts` 的 `/orders/first-leg/batch-review-preview` 一次取回全部预览,按服务端 `canApprove/blockReason` 展示,不逐票请求详情或用内部 ID 作标题 | | 客户原始申报资料编辑与渠道变更 | `modules/unified-orders/components/OrderDeclarationEditorDialog.vue`、`api/unifiedOrders.ts`、`pages/UnifiedOrdersView.vue` | 使用订单版本和货物 `itemNo` 整体提交,不允许增删货物行;只编辑客户申报快照,不覆盖仓库/PDA 实测、测量审核或计费事实。渠道变化需二次确认并由 Backend `order/` 联查 `firstleg/` 的分柜、同柜、装柜与重新计价门禁;国内收货仓或中转仓变化必须重新下单 | | 入库扣货、解除与凭证历史 | `modules/unified-orders/pages/UnifiedOrdersView.vue`、`modules/inbounds/core/api.ts`、`types.ts` | 页面只消费服务端 `holdFlag/currentHoldReason/canHold/canRelease/actionBlockReason/history`;扣货与解除提交当前入库版本、幂等键、必填原因和可选凭证,凭证下载只走 Firstleg 授权入口。Backend `firstleg/` 的 `InboundHoldService`、`InboundHoldGuard`、`InboundHoldEvidenceService`,文件归 `file/` | -| 入库、测量、称重库位状态、客户箱规策略与计价主数据 | 测量弹框/工作区 `modules/inbounds/measurement/InboundMeasurementDialog.vue`、`InboundMeasurementPanel.vue`,称重 `modules/inbounds/weight/components/WeightOcrPanel.vue`;主数据 `modules/fba-pricing/`、`modules/dimension-config/`、`modules/customers/components/CustomerBillingRulesPanel.vue`、`modules/fee-items/` | 客户固定四档箱规,默认计算方式为 `ADD/MULTIPLY`;测量行可选实际尺寸、箱规尺寸策略或客户默认尺寸系数,保存后以 Backend `firstleg/` 返回的外箱尺寸、体积重和计费基数为准,不在页面独立重算。费用确认的实际箱数、毛重和体积优先汇总当前 `actualMeasurementRows`,不得用可能滞后的工作区顶层合计覆盖明细。称重页在每张图片卡片只展示服务端 `locationCode/slotStatus/sharedOverflow`,不渲染或自行推导整票预留引导;删除图片须提示槽位仍归本票保留,不得按图片数组下标推导托盘或库位 | +| 入库、测量、称重库位状态、客户箱规策略与计价主数据 | 测量弹框/工作区 `modules/inbounds/measurement/InboundMeasurementDialog.vue`、`InboundMeasurementPanel.vue`,称重 `modules/inbounds/weight/components/WeightOcrPanel.vue`;主数据 `modules/fba-pricing/`、`modules/dimension-config/`、`modules/customers/components/CustomerBillingRulesPanel.vue`、`modules/fee-items/` | 客户固定四档箱规,默认计算方式为 `ACTUAL/ADD/MULTIPLY`;未启用默认调整规则时按实测原尺寸计算,仓库修正值替换对应实测边。测量行可选实际尺寸、箱规尺寸策略或客户默认尺寸系数,保存后以 Backend `firstleg/` 返回的外箱尺寸、体积重和计费基数为准,不在页面独立重算。费用确认的箱数、毛重汇总当前 `actualMeasurementRows`,计费体积默认取已保存的 `outerMeasurementRows` 汇总,最终按用户确认值提交,体积来源标记为 `OUTER`;不得用可能滞后的工作区顶层合计覆盖明细。称重页在每张图片卡片只展示服务端 `locationCode/slotStatus/sharedOverflow`,不渲染或自行推导整票预留引导;删除图片须提示槽位仍归本票保留,不得按图片数组下标推导托盘或库位 | | 一件代发托盘打印 | `modules/unified-orders/pages/UnifiedOrdersView.vue` 的“托盘打印”页签、`modules/inbounds/core/DropshipPalletPrintPanel.vue`、`core/api.ts` | 仅一件代发订单展示;测量审核预生成托盘码后由 Admin 电脑端打印,支持本票全部托盘合并打印和单托补打。PDA 只扫码组托,不承担 PDF 下载或打印机连接 | | 入库单扣货、解除、凭证与历史 | `modules/inbounds/pages/InboundOrdersView.vue`、详情工作区、`modules/inbounds/core/api.ts` | 列表展示服务端 `holdFlag/currentHoldReason` 并支持筛选;详情只按 `canHold/canRelease/actionBlockReason` 开放操作。扣货与解除提交当前 `version`、原因和独立幂等键,可选凭证先按明确客户与入库单上传;历史文件只调用专用下载入口,不把上传成功但未提交命令的文件冒充扣货证据 | | 客户专属价格簿、柜级阶梯运费与受控调价 | 列表/上传 `modules/fba-pricing/carrier-rates/pages/CarrierRatesView.vue`,版本详情/仓库映射/确认 `modules/fba-pricing/carrier-rates/pages/CarrierRateDetailView.vue`,路由 `modules/fba-pricing/routes.ts`;`modules/inbounds/fee/`、`modules/unified-orders/components/InboundFeeDetailManager.vue` | 列表与上传后按 `versionId` 进入 `fba-carrier-rate-detail` 独立详情页,价格行只读;回归入口 `modules/fba-pricing/carrier-rates/pages/CarrierRatesView.test.ts`。价格簿页面显式选择公共报价或具体客户;列表、上传、确认只作用于该 `customerId`,不得混合版本。计价优先消费客户专属 ACTIVE 版本,无专属版本才回退公共 ACTIVE;柜级阶梯金额和各客户分摊只展示服务端结果。调整计费量只能在服务端返回的上下 10% 范围内并提交当前费用版本、原因与二次确认;私人仓尾程派送费使用稳定 `PRIVATE_LAST_MILE_DELIVERY` 代码且只对 `PRIVATE` 目的地开放;一件代发须在预装确认前完成试算,计价后保持服务端 `WAIT_PALLETIZE` 状态 | diff --git a/src/core/navigation/adminNavigation.test.ts b/src/core/navigation/adminNavigation.test.ts index 76612995..cc74cc95 100644 --- a/src/core/navigation/adminNavigation.test.ts +++ b/src/core/navigation/adminNavigation.test.ts @@ -49,14 +49,14 @@ describe('adminNavigation', () => { { title: '预约管理', children: ['预约列表'] }, { title: '车辆管理', children: ['排车列表', '车辆列表', '司机管理', '自有业务', '外配任务', '出车记录'] }, { title: '海外业务', children: ['采购订单', '本土卡车', '本土散板', '散板需求'] }, - { title: '客户服务', children: ['异常订单'] }, + { title: '客户服务', children: ['异常订单', '客户回访'] }, { title: '财务管理', children: ['海外费用明细', '客户账户', '客户应收应付', '应收款统计表', '客户对账单', '结算单应收', '结算单应付'] }, { title: '海外仓', children: ['经营总览', '销量分析', '库存分析', '数据采集'] }, { title: '通用', children: ['费用项目', '汇率管理', '仓库管理', '海外仓报价', '车队报价', '船期价格表', '下载中心', '供应商列表'] }, { title: '系统管理', children: ['用户管理', '角色权限', '菜单权限'] }, ]) expect(visible).toHaveLength(13) - expect(visible.flatMap(node => node.children ?? [])).toHaveLength(59) + expect(visible.flatMap(node => node.children ?? [])).toHaveLength(60) expect(adminNavigation.find((item) => item.key === 'notifications')).toBeUndefined() }) diff --git a/src/modules/appointment-calendar/AppointmentCreatePanel.vue b/src/modules/appointment-calendar/AppointmentCreatePanel.vue index 8da2ea1d..d23e1427 100644 --- a/src/modules/appointment-calendar/AppointmentCreatePanel.vue +++ b/src/modules/appointment-calendar/AppointmentCreatePanel.vue @@ -37,12 +37,12 @@ let generation = 0, selectionGeneration = 0, controller: AbortController | null const columns: DataTableColumn[] = [ { key: 'select', label: '选择', width: 60, slotName: 'select' }, { prop: 'inboundCode', label: '入仓码', minWidth: 180 }, + { key: 'eligibility', label: '预约资格', minWidth: 260, singleLine: false, slotName: 'eligibility', afterColumn: 'inboundCode' }, { prop: 'customerName', label: '客户', minWidth: 140, slotName: 'customer' }, { prop: 'containerNo', label: '柜号', minWidth: 145 }, { prop: 'originWarehouseName', label: '起运仓', minWidth: 140, slotName: 'origin' }, { prop: 'destinationWarehouseCode', label: '目的FBA仓', width: 110 }, { prop: 'packages', label: '箱数', width: 80 }, - { key: 'eligibility', label: '预约资格', minWidth: 260, singleLine: false, slotName: 'eligibility' }, ] function key(row: PlanningCargo) { return JSON.stringify([row.customerId, row.sourceKey]) } function reason(row: PlanningCargo) { diff --git a/src/modules/appointment-calendar/WorkExistingDispatchForm.vue b/src/modules/appointment-calendar/WorkExistingDispatchForm.vue index a038bf2d..a2f98863 100644 --- a/src/modules/appointment-calendar/WorkExistingDispatchForm.vue +++ b/src/modules/appointment-calendar/WorkExistingDispatchForm.vue @@ -55,7 +55,7 @@ onBeforeUnmount(() => { generation++ })

沿用原整车排车

在原排车单维护车辆、司机和货物;一张排车单就是一趟车。

查看原排车单及批次关联需要相应列表权限。

正在核对原排车单与批次…

-
操作排车单号车牌号箱数阶段
继续维护本车为本车添加同仓货物查看原排车单{{ row.dispatchNo }}{{ row.vehicleInfo.vehicle?.plate || '—' }}{{ row.counts.packageCount }}{{ stageLabel(row.stage) }}
+
操作排车单号阶段车牌号箱数
继续维护本车为本车添加同仓货物查看原排车单{{ row.dispatchNo }}{{ stageLabel(row.stage) }}{{ row.vehicleInfo.vehicle?.plate || '—' }}{{ row.counts.packageCount }}
diff --git a/src/modules/appointment-calendar/WorkReceiptPlans.vue b/src/modules/appointment-calendar/WorkReceiptPlans.vue index a404e91b..23b8d2b1 100644 --- a/src/modules/appointment-calendar/WorkReceiptPlans.vue +++ b/src/modules/appointment-calendar/WorkReceiptPlans.vue @@ -48,7 +48,7 @@ onMounted(load) 核对本柜入库计划预览 -

核对本柜计划与托盘

{{ preview.containerNo || '柜号待核实' }} · {{ preview.overseasWarehouseName || '接仓仓库待核实' }} · {{ preview.pendingOrderCount }} 票待生成 · {{ preview.blockedOrderCount }} 票待处理

头程已有 {{ preview.sourcePalletCount }} 托;海外补充 {{ preview.supplementPalletCount }} 托;已保存计划 {{ preview.totalPlannedPalletCount }} 托。

客户原票目的地箱数目标托数计划状态
{{ row.customerName || '客户名称待核实' }}{{ row.inboundCode || row.businessOrderNo || '—' }}{{ row.destinationWarehouseName || row.fbaWarehouseCode || (row.destinationType === 'PRIVATE_ADDRESS' ? '客户地址' : '目的地待核实') }}{{ row.packageCount }}{{ row.targetPalletCount }}{{ row.existingPlanNo || ({ READY: '可生成', RESOURCE_PENDING: '资源待确认', PLANNED: '已有计划', BLOCKED: '来源条件未就绪' }[row.readiness]) }}

本柜尚不具备生成条件,请在原尾程订单核对阻塞来源。

{{ preview.resourceRetry ? '重试原资源确认' : '确认生成本柜计划' }}
+

核对本柜计划与托盘

{{ preview.containerNo || '柜号待核实' }} · {{ preview.overseasWarehouseName || '接仓仓库待核实' }} · {{ preview.pendingOrderCount }} 票待生成 · {{ preview.blockedOrderCount }} 票待处理

头程已有 {{ preview.sourcePalletCount }} 托;海外补充 {{ preview.supplementPalletCount }} 托;已保存计划 {{ preview.totalPlannedPalletCount }} 托。

客户原票计划状态目的地箱数目标托数
{{ row.customerName || '客户名称待核实' }}{{ row.inboundCode || row.businessOrderNo || '—' }}{{ row.existingPlanNo || ({ READY: '可生成', RESOURCE_PENDING: '资源待确认', PLANNED: '已有计划', BLOCKED: '来源条件未就绪' }[row.readiness]) }}{{ row.destinationWarehouseName || row.fbaWarehouseCode || (row.destinationType === 'PRIVATE_ADDRESS' ? '客户地址' : '目的地待核实') }}{{ row.packageCount }}{{ row.targetPalletCount }}

本柜尚不具备生成条件,请在原尾程订单核对阻塞来源。

{{ preview.resourceRetry ? '重试原资源确认' : '确认生成本柜计划' }}
diff --git a/src/modules/container-loading/components/ContainerCustomsPanel.test.ts b/src/modules/container-loading/components/ContainerCustomsPanel.test.ts index 23b30c49..2fe4daf0 100644 --- a/src/modules/container-loading/components/ContainerCustomsPanel.test.ts +++ b/src/modules/container-loading/components/ContainerCustomsPanel.test.ts @@ -248,7 +248,7 @@ describe('ContainerCustomsPanel', () => { global: { plugins: [ElementPlus] }, }) await flushPromises() - expect(wrapper.findAll('th').map(header => header.text())).toEqual(['操作', '客户退税资料', '预录单', '真实报关完税文件', '关联入仓码', '报关行状态', '客户', '预录单情况']) + expect(wrapper.findAll('th').map(header => header.text())).toEqual(['操作', '关联入仓码', '报关行状态', '客户退税资料', '预录单', '真实报关完税文件', '客户', '预录单情况']) expect(wrapper.text()).not.toMatch(/版本|退税资料 v|回执 v|完税 v/) expect(wrapper.text()).toContain('预录单历史:2026/8/29 10:50:00 · 报关员') expect(wrapper.text()).toContain('确认 · 2026/8/29 11:15:00 · 客户一') diff --git a/src/modules/container-loading/components/ContainerCustomsPanel.vue b/src/modules/container-loading/components/ContainerCustomsPanel.vue index 58905c02..d3540e54 100644 --- a/src/modules/container-loading/components/ContainerCustomsPanel.vue +++ b/src/modules/container-loading/components/ContainerCustomsPanel.vue @@ -321,13 +321,13 @@ onMounted(load)

已冻结退税资料、预录单与完税文件

-
+
操作客户退税资料预录单真实报关完税文件关联入仓码报关行状态客户预录单情况
+ + - - - +
操作关联入仓码报关行状态客户退税资料预录单真实报关完税文件客户预录单情况
{{ !brokerSubmission ? '首次发送后上传预录单' : document.receipt ? '覆盖预录单' : '上传预录单' }}提交{{ document.finalDocument ? '覆盖完税文件' : '上传完税文件' }}提交
只读
{{ document.inboundCodes.join('、') || '—' }}{{ brokerSubmitted ? '已发送报关行执行' : brokerSubmission ? '已首次发送,等待预录单确认' : '待首次发送报关行' }}
{{ document.source.fileName }}下载
{{ document.receipt.fileName }}下载
未上传
{{ document.finalDocument.fileName }}下载
未上传
{{ document.inboundCodes.join('、') || '—' }}{{ brokerSubmitted ? '已发送报关行执行' : brokerSubmission ? '已首次发送,等待预录单确认' : '待首次发送报关行' }}{{ documentCustomers(document) }}{{ confirmationActionLabel(history.action) }} · {{ formatDateTime(history.createdAt) }} · {{ history.operatorName || '—' }} · {{ history.reason }}预录单历史:{{ formatDateTime(history.createdAt) }} · {{ history.operatorName || '—' }}{{ document.receipt ? (document.receiptStatus === 'CONFIRMED' ? '客户已确认预录单' : document.receiptStatus === 'REJECTED' ? '客户已打回预录单' : '待客户确认预录单') : '待上传预录单' }}
{{ document.receipt.fileName }}下载
未上传
{{ document.finalDocument.fileName }}下载
未上传
{{ documentCustomers(document) }}{{ confirmationActionLabel(history.action) }} · {{ formatDateTime(history.createdAt) }} · {{ history.operatorName || '—' }} · {{ history.reason }}预录单历史:{{ formatDateTime(history.createdAt) }} · {{ history.operatorName || '—' }}{{ document.receipt ? (document.receiptStatus === 'CONFIRMED' ? '客户已确认预录单' : document.receiptStatus === 'REJECTED' ? '客户已打回预录单' : '待客户确认预录单') : '待上传预录单' }}
diff --git a/src/modules/container-loading/components/ContainerLastLegFeesPanel.vue b/src/modules/container-loading/components/ContainerLastLegFeesPanel.vue index afa7446e..9f85b49b 100644 --- a/src/modules/container-loading/components/ContainerLastLegFeesPanel.vue +++ b/src/modules/container-loading/components/ContainerLastLegFeesPanel.vue @@ -176,12 +176,12 @@ onMounted(load) + - diff --git a/src/modules/container-loading/components/ContainerOverviewPanel.test.ts b/src/modules/container-loading/components/ContainerOverviewPanel.test.ts index 1d795b0d..282fff59 100644 --- a/src/modules/container-loading/components/ContainerOverviewPanel.test.ts +++ b/src/modules/container-loading/components/ContainerOverviewPanel.test.ts @@ -58,8 +58,10 @@ describe('ContainerOverviewPanel', () => { it('renders two file rows and one inline PU code row without a nested heading', async () => { const wrapper = mount(ContainerOverviewPanel, { props: { overview, canManageDocuments: true, canManageCustoms: true, canManageException: true }, global: { plugins: [ElementPlus] } }) const headers = wrapper.findAll('.document-table thead th').map((cell) => cell.text()) - expect(headers).toEqual(['操作', '资料类型', '资料内容', '更新人', '更新时间', '状态']) + expect(headers).toEqual(['操作', '资料类型', '状态', '资料内容', '更新人', '更新时间']) expect(wrapper.findAll('.document-table tbody tr')).toHaveLength(3) + expect(wrapper.findAll('.document-table tbody tr').every(row => row.findAll('td')[2]!.find('.el-tag').exists())).toBe(true) + expect(wrapper.find('.pickup-code-row').findAll('td')[3]!.find('input[aria-label="PU码"]').exists()).toBe(true) expect(wrapper.findAll('.document-item')).toHaveLength(0) expect(wrapper.find('.document-table-wrap').exists()).toBe(true) expect(wrapper.text()).not.toContain('业务资料') diff --git a/src/modules/container-loading/components/ContainerOverviewPanel.vue b/src/modules/container-loading/components/ContainerOverviewPanel.vue index 4c2c024e..14339177 100644 --- a/src/modules/container-loading/components/ContainerOverviewPanel.vue +++ b/src/modules/container-loading/components/ContainerOverviewPanel.vue @@ -130,21 +130,21 @@ onBeforeUnmount(() => { photoGeneration += 1; revokePhotoUrls() })
- + + - + -
操作资料类型资料内容更新人更新时间状态
操作资料类型状态资料内容更新人更新时间
下载{{ documentsByType.get(item.type) ? '更新附件' : '上传附件' }}—
{{ item.label }}{{ documentStatusLabel(item.type) }} {{ documentsByType.get(item.type)?.fileName || '—' }}{{ deliveryOrderBlockedReason }} {{ documentsByType.get(item.type)?.uploadedByName || '—' }} {{ documentsByType.get(item.type) ? formatTime(documentsByType.get(item.type)?.uploadedAt) : '—' }}{{ documentStatusLabel(item.type) }}
保存— PU 资料{{ overview.container.pickupCode ? '已填写' : '未填写' }} {{ overview.container.pickupCodeUpdatedByName || '—' }} {{ formatTime(overview.container.pickupCodeUpdatedAt) }}{{ overview.container.pickupCode ? '已填写' : '未填写' }}
diff --git a/src/modules/container-loading/pages/ContainersView.test.ts b/src/modules/container-loading/pages/ContainersView.test.ts index d5e27176..2095e917 100644 --- a/src/modules/container-loading/pages/ContainersView.test.ts +++ b/src/modules/container-loading/pages/ContainersView.test.ts @@ -849,8 +849,8 @@ describe('ContainersView', () => { describe('ContainersView trucking supplier contract', () => { it('shows the frozen plan, exception and document aggregates without deriving them client-side', () => { const source = readFileSync(resolve(process.cwd(), 'src/modules/container-loading/pages/ContainersView.vue'), 'utf8') - const fields = ['containerNo', 'planNo', 'planStatus', 'exceptionActive', 'cabinetNo', 'destinationClearanceDocumentStatus', 'deliveryOrderDocumentStatus', 'pickupCode'] - let previous = source.indexOf("key: 'actions'") + const fields = ['containerNo', 'planStatus', 'destinationClearanceDocumentStatus', 'deliveryOrderDocumentStatus', 'exceptionActive', 'planNo', 'cabinetNo', 'pickupCode'] + let previous = source.indexOf('const allColumns') for (const field of fields) { const current = source.indexOf(`key: '${field}'`, previous + 1) expect(current, field).toBeGreaterThan(previous) diff --git a/src/modules/container-loading/pages/ContainersView.vue b/src/modules/container-loading/pages/ContainersView.vue index c0b7b686..971a1f7b 100644 --- a/src/modules/container-loading/pages/ContainersView.vue +++ b/src/modules/container-loading/pages/ContainersView.vue @@ -109,9 +109,9 @@ let emailSequence = 0 const emailColumns: DataTableColumn[] = [ { key: 'actions', label: '操作', width: 90, fixed: 'left', slotName: 'emailActions' }, { key: 'kind', label: '类型', width: 100, formatter: (row: TerminalDrayageEmail) => row.kind === 'CANCEL_PICKUP' ? '取消提柜' : '请求提柜' }, + { key: 'status', label: '发送状态', width: 100, formatter: (row: TerminalDrayageEmail) => ({ PENDING: '待发送', SENDING: '发送中', SENT: '已发送', FAILED: '失败待核对' })[row.status], afterColumn: 'kind' }, { key: 'fleetName', label: '车队', minWidth: 130 }, { key: 'recipient', label: '收件邮箱', minWidth: 210 }, - { key: 'status', label: '发送状态', width: 100, formatter: (row: TerminalDrayageEmail) => ({ PENDING: '待发送', SENDING: '发送中', SENT: '已发送', FAILED: '失败待核对' })[row.status] }, { key: 'content', label: '邮件内容', minWidth: 270, slotName: 'emailContent' }, { key: 'failureReason', label: '失败说明', minWidth: 200 }, { key: 'createdAt', label: '创建时间', width: 165, formatter: (row: TerminalDrayageEmail) => formatTime(row.createdAt) }, @@ -239,12 +239,12 @@ const allColumns: DataTableColumn[] = [ { key: 'actions', label: '操作', width: 240, fixed: 'left', slotName: 'actions' }, { key: 'emptyReturnWarning', label: '报空预警', width: 120, slotName: 'emptyReturnWarning' }, { key: 'containerNo', label: '集装箱号', minWidth: 150, slotName: 'containerNo' }, + { key: 'planStatus', label: '状态', width: 110, slotName: 'status', afterColumn: 'containerNo' }, + { key: 'destinationClearanceDocumentStatus', label: '清关资料', width: 110, formatter: (row: ContainerBookingResource) => documentStatusText(row.destinationClearanceDocumentStatus), afterColumn: 'containerNo' }, + { key: 'deliveryOrderDocumentStatus', label: 'DO资料', width: 100, formatter: (row: ContainerBookingResource) => documentStatusText(row.deliveryOrderDocumentStatus), afterColumn: 'containerNo' }, + { key: 'exceptionActive', label: '异常', width: 90, formatter: (row: ContainerBookingResource) => row.exceptionActive ? '异常' : '正常', afterColumn: 'containerNo' }, { key: 'planNo', prop: 'planNo', label: '装柜清单号', minWidth: 165, formatter: (row: ContainerBookingResource) => row.planNo || '—' }, - { key: 'planStatus', label: '状态', width: 110, slotName: 'status' }, - { key: 'exceptionActive', label: '异常', width: 90, formatter: (row: ContainerBookingResource) => row.exceptionActive ? '异常' : '正常' }, { key: 'cabinetNo', label: '柜次号', minWidth: 180, slotName: 'cabinet' }, - { key: 'destinationClearanceDocumentStatus', label: '清关资料', width: 110, formatter: (row: ContainerBookingResource) => documentStatusText(row.destinationClearanceDocumentStatus) }, - { key: 'deliveryOrderDocumentStatus', label: 'DO资料', width: 100, formatter: (row: ContainerBookingResource) => documentStatusText(row.deliveryOrderDocumentStatus) }, { key: 'pickupCode', label: 'PU码', minWidth: 140, formatter: (row: ContainerBookingResource) => row.pickupCode || '—' }, { key: 'oceanBooking', label: '真实订舱', minWidth: 170, slotName: 'oceanBooking' }, { key: 'warehouseName', prop: 'warehouseName', label: '装柜仓库', minWidth: 160 }, diff --git a/src/modules/container-loading/pages/GlobalSmartLoadingView.vue b/src/modules/container-loading/pages/GlobalSmartLoadingView.vue index 766e5447..028d48ab 100644 --- a/src/modules/container-loading/pages/GlobalSmartLoadingView.vue +++ b/src/modules/container-loading/pages/GlobalSmartLoadingView.vue @@ -240,8 +240,8 @@ function preferredContainerId() { const candidateColumns: DataTableColumn[] = [ { key: 'actions', label: '操作', width: 90, fixed: 'left', slotName: 'actions' }, { key: 'inboundCode', label: '入仓码', minWidth: 180, fixed: 'left', slotName: 'candidateInbounds' }, + { key: 'orderStatusSnapshot', prop: 'orderStatusSnapshot', label: '订单状态', width: 100, slotName: 'orderStatusSnapshot', afterColumn: 'inboundCode' }, { key: 'customerName', prop: 'customerName', label: '客户', minWidth: 120, slotName: 'customerName' }, - { key: 'orderStatusSnapshot', prop: 'orderStatusSnapshot', label: '订单状态', width: 100, slotName: 'orderStatusSnapshot' }, { key: 'availablePackageCount', prop: 'availablePackageCount', label: '箱数', width: 80 }, { key: 'totalGrossWeightKg', label: '重量', width: 100, slotName: 'candidateTotalWeight' }, { key: 'totalVolumeCbm', label: '体积', width: 100, slotName: 'candidateTotalVolume' }, diff --git a/src/modules/container-loading/pages/SmartLoadingWorkbenchView.vue b/src/modules/container-loading/pages/SmartLoadingWorkbenchView.vue index ca85ed8f..218f2b7d 100644 --- a/src/modules/container-loading/pages/SmartLoadingWorkbenchView.vue +++ b/src/modules/container-loading/pages/SmartLoadingWorkbenchView.vue @@ -157,12 +157,12 @@ const itemColumns: DataTableColumn[] = [ const candidateColumns: DataTableColumn[] = [ { key: 'actions', label: '操作', width: 110, fixed: 'left', slotName: 'actions' }, { key: 'inboundCode', label: '入仓码', minWidth: 220, fixed: 'left', slotName: 'candidateInbounds' }, + { key: 'cartonIdentityStatus', label: '箱号归属', width: 110, slotName: 'cartonIdentityStatus', afterColumn: 'inboundCode' }, { key: 'palletNo', prop: 'palletNo', label: '托盘号', minWidth: 150, showOverflowTooltip: true }, { key: 'coLoadGroup', label: '装柜规则', minWidth: 170, slotName: 'coLoadGroup' }, { key: 'customerName', prop: 'customerName', label: '客户', minWidth: 150, slotName: 'customerName' }, { key: 'goodsSummary', prop: 'goodsSummary', label: '货物摘要', minWidth: 180, slotName: 'goodsSummary' }, { key: 'availablePackageCount', prop: 'availablePackageCount', label: '箱数', width: 90 }, - { key: 'cartonIdentityStatus', label: '箱号归属', width: 110, slotName: 'cartonIdentityStatus' }, { key: 'averageUnitVolumeCbm', label: '单位体积', width: 120, slotName: 'averageUnitVolume' }, { key: 'unitGrossWeightKg', label: '单位毛重', width: 120, slotName: 'unitGrossWeight' }, ] diff --git a/src/modules/customers/components/CustomerBillingRulesPanel.test.ts b/src/modules/customers/components/CustomerBillingRulesPanel.test.ts index 2017aef6..63b8a043 100644 --- a/src/modules/customers/components/CustomerBillingRulesPanel.test.ts +++ b/src/modules/customers/components/CustomerBillingRulesPanel.test.ts @@ -89,6 +89,18 @@ describe('CustomerBillingRulesPanel', () => { expect(document.body.textContent).toContain('标准中箱') }) + it('shows weighted volume and volume weight with the coefficient applied to all three dimensions', async () => { + vi.mocked(listCustomerChargeRules).mockResolvedValue([{ ...chargeRule, defaultRule: true, lengthFactor: 1.25 }]) + const wrapper = mount(CustomerBillingRulesPanel, { attachTo: document.body, props: { customerId: '8', customerName: '柏悦物流' }, global: { plugins: [ElementPlus] } }) + await flushPromises() + + const chargeTable = wrapper.findAll('.el-table')[1]! + const largeRow = chargeTable.findAll('.el-table__body tbody tr')[3]! + const values = largeRow.findAll('td').map(cell => cell.text()) + expect(values[4]).toBe('0.293') + expect(values[6]).toBe('48.93') + }) + it('hides permission codes in the no-access state', async () => { const auth = useAuthStore() auth.permissions = [] diff --git a/src/modules/customers/components/CustomerBillingRulesPanel.vue b/src/modules/customers/components/CustomerBillingRulesPanel.vue index fdd8297b..8c8f3a04 100644 --- a/src/modules/customers/components/CustomerBillingRulesPanel.vue +++ b/src/modules/customers/components/CustomerBillingRulesPanel.vue @@ -399,15 +399,15 @@ onBeforeUnmount(stopInitializationPolling)
-

箱规按权重系数计算

勾选后客户下单数据才会按该规则调整;未勾选时默认使用实测原尺寸(不调整)。

设为默认规则
+

箱规按权重系数计算

勾选后才默认按该规则调整;两个默认规则均未勾选时使用实测原尺寸,仓库修正尺寸替换对应实测边。

系数分别作用于长、宽、高,加权体积 = 原体积 × 系数³。

设为默认规则
- + - +
diff --git a/src/modules/customers/pages/CustomerDetailView.vue b/src/modules/customers/pages/CustomerDetailView.vue index 244302f3..c09d3b8f 100644 --- a/src/modules/customers/pages/CustomerDetailView.vue +++ b/src/modules/customers/pages/CustomerDetailView.vue @@ -289,14 +289,14 @@ onBeforeUnmount(() => {

企业用户

登录手机号属于账号信息;请在成员/账号管理中单独维护,修改会影响登录

- +
账号成员类型登录手机号邮箱角色状态
+ -
账号状态成员类型登录手机号邮箱角色
{{ (member.displayName || member.username || '?').slice(0, 1) }}
{{ displayText(member.username) }}{{ displayText(member.displayName) }}
{{ member.status === 'ACTIVE' ? '启用' : '停用' }} {{ member.memberType === 'OWNER' ? '企业管理员' : '企业员工' }} {{ displayText(member.phone) }} {{ displayText(member.email) }}
{{ role }}
—
{{ member.status === 'ACTIVE' ? '启用' : '停用' }}
diff --git a/src/modules/customers/pages/CustomersView.vue b/src/modules/customers/pages/CustomersView.vue index a294a5f0..8cd6df8d 100644 --- a/src/modules/customers/pages/CustomersView.vue +++ b/src/modules/customers/pages/CustomersView.vue @@ -214,6 +214,7 @@ const unifiedSocialCreditCodePattern = /^[0-9A-Z]{18}$/ const customerColumns: DataTableColumn[] = [ { key: 'actions', label: '操作', width: 330, slotName: 'actions' }, { key: 'tenantCode', prop: 'tenantCode', label: '客户编号', width: 220, className: 'customer-code-column' }, + { key: 'status', label: '状态', width: 110, slotName: 'status', afterColumn: 'tenantCode' }, { key: 'customerNickname', prop: 'customerNickname', label: '客户昵称', width: 120, formatter: (row: PlatformTenantResource) => displayText(row.customerNickname) }, { key: 'company', label: '公司名称', width: 250, slotName: 'company' }, { key: 'unifiedSocialCreditCode', prop: 'unifiedSocialCreditCode', label: '统一社会信用代码', width: 220, formatter: (row: PlatformTenantResource) => displayText(row.unifiedSocialCreditCode) }, @@ -221,7 +222,6 @@ const customerColumns: DataTableColumn[] = [ { key: 'contactPhone', prop: 'contactPhone', label: '公司联系电话', minWidth: 150, formatter: (row: PlatformTenantResource) => displayText(row.contactPhone) }, { key: 'salesOwnerName', prop: 'salesOwnerName', label: '销售人员', minWidth: 120, formatter: (row: PlatformTenantResource) => displayText(row.salesOwnerName) }, { key: 'paymentTermDays', prop: 'paymentTermDays', label: '账期(天)', width: 105, formatter: (row: PlatformTenantResource) => row.paymentTermDays ?? 0 }, - { key: 'status', label: '状态', width: 110, slotName: 'status' }, { key: 'orderCount', label: '运单数', width: 100, slotName: 'orderCount' }, { key: 'createdAt', prop: 'createdAt', label: '入驻时间', minWidth: 165, formatter: (row: PlatformTenantResource) => formatDate(row.createdAt) }, ] diff --git a/src/modules/customs/pages/CustomsCasesView.vue b/src/modules/customs/pages/CustomsCasesView.vue index ffa4c2aa..de3f0b31 100644 --- a/src/modules/customs/pages/CustomsCasesView.vue +++ b/src/modules/customs/pages/CustomsCasesView.vue @@ -27,7 +27,7 @@ const filters = ref({ customerId: '', status: '' }); const fields = { 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: 'status', type: 'select', label: '状态', width: 160, options: customsCaseStatusOptions.map((option) => ({ ...option })) }, ]) -const columns = [{ key: 'actions', label: '操作', fixed: 'left', width: 175, slotName: 'actions', hideable: false, movable: false }, { prop: 'customsNo', label: '报关编号', minWidth: 145 }, { key: 'orderLink', label: '订单关联', minWidth: 120, formatter: (row: CustomsCaseResource) => associationLabel(row.orderId) }, { key: 'inboundLink', label: '入库单关联', minWidth: 120, formatter: (row: CustomsCaseResource) => associationLabel(row.inboundId) }, { prop: 'checklistCode', label: '资料清单', minWidth: 125 }, { prop: 'checklistVersionNo', label: '版本', width: 90 }, { prop: 'status', label: '状态', width: 110, slotName: 'status' }, { prop: 'updatedAt', label: '更新时间', minWidth: 160 }] +const columns = [{ key: 'actions', label: '操作', fixed: 'left', width: 175, slotName: 'actions', hideable: false, movable: false }, { prop: 'customsNo', label: '报关编号', minWidth: 145 }, { prop: 'status', label: '状态', width: 110, slotName: 'status', afterColumn: 'customsNo' }, { key: 'orderLink', label: '订单关联', minWidth: 120, formatter: (row: CustomsCaseResource) => associationLabel(row.orderId) }, { key: 'inboundLink', label: '入库单关联', minWidth: 120, formatter: (row: CustomsCaseResource) => associationLabel(row.inboundId) }, { prop: 'checklistCode', label: '资料清单', minWidth: 125 }, { prop: 'checklistVersionNo', label: '版本', width: 90 }, { prop: 'updatedAt', label: '更新时间', minWidth: 160 }] function associationLabel(value: string | null | undefined) { return value ? '已关联' : '未关联' } async function loadCustomers() { customersLoading.value = true; try { const result = await listPlatformTenants({ page: 1, size: 200, status: 'ACTIVE' }); customers.value = result.records.filter((customer) => customer.status === 'ACTIVE') } catch { customers.value = [] } finally { customersLoading.value = false } } async function loadCustomsAgents() { customsAgentsLoading.value = true; customsAgentsError.value = ''; try { const result = await listSuppliers({ page: 1, size: 200, category: 'CUSTOMS', status: 'ACTIVE' }); customsAgents.value = result.records.filter((supplier) => supplier.category === 'CUSTOMS' && supplier.status === 'ACTIVE') } catch (cause) { customsAgents.value = []; customsAgentsError.value = getApiErrorDetails(cause, '报关代理加载失败').message } finally { customsAgentsLoading.value = false } } diff --git a/src/modules/domestic-containers/pages/DomesticContainersView.vue b/src/modules/domestic-containers/pages/DomesticContainersView.vue index f75f3e21..e202f58d 100644 --- a/src/modules/domestic-containers/pages/DomesticContainersView.vue +++ b/src/modules/domestic-containers/pages/DomesticContainersView.vue @@ -44,9 +44,9 @@ const queryFields = computed(() => { return base }) const columns = computed(() => activeTab.value === 'inventory' ? [ - { key: 'actions', label: '操作', fixed: 'left', width: 112, slotName: 'actions', hideable: false, movable: false }, { prop: 'containerNo', label: '集装箱号', minWidth: 150 }, { prop: 'warehouseName', label: '国内仓', minWidth: 150 }, { prop: 'cargoName', label: '货物名称', minWidth: 150 }, { prop: 'inboundBoxes', label: '入库箱数', width: 105, align: 'right' }, { prop: 'outboundBoxes', label: '已出库箱数', width: 115, align: 'right' }, { prop: 'availableBoxes', label: '当前库存', width: 105, align: 'right' }, { prop: 'inboundVolumeCbm', label: '累计入库体积(m³)', width: 155, align: 'right' }, { prop: 'inventoryStatus', label: '状态', width: 110, slotName: 'status' }, + { key: 'actions', label: '操作', fixed: 'left', width: 112, slotName: 'actions', hideable: false, movable: false }, { prop: 'containerNo', label: '集装箱号', minWidth: 150 }, { prop: 'inventoryStatus', label: '状态', width: 110, slotName: 'status', afterColumn: 'containerNo' }, { prop: 'warehouseName', label: '国内仓', minWidth: 150 }, { prop: 'cargoName', label: '货物名称', minWidth: 150 }, { prop: 'inboundBoxes', label: '入库箱数', width: 105, align: 'right' }, { prop: 'outboundBoxes', label: '已出库箱数', width: 115, align: 'right' }, { prop: 'availableBoxes', label: '当前库存', width: 105, align: 'right' }, { prop: 'inboundVolumeCbm', label: '累计入库体积(m³)', width: 155, align: 'right' }, ] : [ - { key: 'actions', label: '操作', fixed: 'left', width: 112, slotName: 'actions', hideable: false, movable: false }, { prop: 'orderNo', label: activeTab.value === 'inbound' ? '入库单号' : '出库单号', minWidth: 155 }, { prop: 'warehouseName', label: '国内仓', minWidth: 150 }, { prop: 'containerNo', label: '集装箱号', minWidth: 150 }, { prop: 'cargoName', label: '货物名称', minWidth: 150 }, { prop: 'boxCount', label: activeTab.value === 'inbound' ? '箱子数量' : '出库箱数', width: 105, align: 'right' }, ...(activeTab.value === 'inbound' ? [{ prop: 'volumeCbm', label: '体积(m³)', width: 105, align: 'right' }] : []), { prop: 'operationTime', label: activeTab.value === 'inbound' ? '入库时间' : '出库时间', minWidth: 165, formatter: (row: ContainerOrder) => displayTime(row.operationTime) }, { prop: 'status', label: '状态', width: 105, slotName: 'status' }, + { key: 'actions', label: '操作', fixed: 'left', width: 112, slotName: 'actions', hideable: false, movable: false }, { prop: 'orderNo', label: activeTab.value === 'inbound' ? '入库单号' : '出库单号', minWidth: 155 }, { prop: 'status', label: '状态', width: 105, slotName: 'status', afterColumn: 'orderNo' }, { prop: 'warehouseName', label: '国内仓', minWidth: 150 }, { prop: 'containerNo', label: '集装箱号', minWidth: 150 }, { prop: 'cargoName', label: '货物名称', minWidth: 150 }, { prop: 'boxCount', label: activeTab.value === 'inbound' ? '箱子数量' : '出库箱数', width: 105, align: 'right' }, ...(activeTab.value === 'inbound' ? [{ prop: 'volumeCbm', label: '体积(m³)', width: 105, align: 'right' }] : []), { prop: 'operationTime', label: activeTab.value === 'inbound' ? '入库时间' : '出库时间', minWidth: 165, formatter: (row: ContainerOrder) => displayTime(row.operationTime) }, ]) function displayTime(value: string) { return value ? value.replace('T', ' ').slice(0, 16) : '—' } function statusLabel(row: ContainerOrder | InventoryRecord) { if ('inventoryStatus' in row) return ({ IN_STOCK: '在库', PARTIALLY_OUTBOUND: '部分出库', OUTBOUND: '已出库' } as const)[row.inventoryStatus]; return row.status === 'PENDING' ? (activeTab.value === 'inbound' ? '待入库' : '待出库') : (activeTab.value === 'inbound' ? '已入库' : '已出库') } diff --git a/src/modules/downloads/pages/DownloadCenterView.vue b/src/modules/downloads/pages/DownloadCenterView.vue index ba9f29e0..93160ffb 100644 --- a/src/modules/downloads/pages/DownloadCenterView.vue +++ b/src/modules/downloads/pages/DownloadCenterView.vue @@ -85,8 +85,8 @@ const queryFields: QueryField[] = [ const columns: DataTableColumn[] = [ { key: 'actions', label: '操作', width: 210, fixed: 'left', slotName: 'actions' }, { key: 'taskName', prop: 'taskName', label: '任务名称', minWidth: 180, showOverflowTooltip: true }, + { key: 'status', label: '状态', width: 110, slotName: 'status', afterColumn: 'taskName' }, { key: 'type', label: '任务类型', width: 120, slotName: 'type' }, - { key: 'status', label: '状态', width: 110, slotName: 'status' }, { key: 'progress', label: '数据量', width: 120, slotName: 'progress' }, { key: 'format', prop: 'format', label: '格式', width: 80 }, { key: 'requester', label: '申请人', minWidth: 130, slotName: 'requester' }, diff --git a/src/modules/fba-pricing/carrier-rates/pages/CarrierRatesView.vue b/src/modules/fba-pricing/carrier-rates/pages/CarrierRatesView.vue index 1621149d..7313ca78 100644 --- a/src/modules/fba-pricing/carrier-rates/pages/CarrierRatesView.vue +++ b/src/modules/fba-pricing/carrier-rates/pages/CarrierRatesView.vue @@ -85,8 +85,8 @@ const columns: DataTableColumn[] = [ { key: 'actions', label: '操作', width: 210, fixed: 'left', slotName: 'actions' }, { key: 'scope', label: '报价范围', minWidth: 160, slotName: 'scope' }, { key: 'version', label: '价格表版本', width: 110, slotName: 'version' }, + { key: 'status', label: '状态', width: 100, slotName: 'status', afterColumn: 'version' }, { key: 'originalFileName', prop: 'originalFileName', label: '原始文件', minWidth: 250, showOverflowTooltip: true, defaultVisible: false }, - { key: 'status', label: '状态', width: 100, slotName: 'status' }, { key: 'contents', label: '解析结果', minWidth: 170, slotName: 'contents' }, { key: 'issues', label: '校验结果', width: 140, slotName: 'issues' }, { key: 'createdAt', label: '上传时间', minWidth: 165, formatter: (row: PlatformPriceBookVersion) => formatDateTime(row.createdAt) }, diff --git a/src/modules/fee-items/pages/FeeItemsView.vue b/src/modules/fee-items/pages/FeeItemsView.vue index 689df25e..95a1fc84 100644 --- a/src/modules/fee-items/pages/FeeItemsView.vue +++ b/src/modules/fee-items/pages/FeeItemsView.vue @@ -119,6 +119,7 @@ const queryFields = computed(() => [ const columns: DataTableColumn[] = [ { key: 'actions', label: '操作', width: 250, fixed: 'left', slotName: 'actions' }, { key: 'feeTypeName', label: '费用项目', minWidth: 210, slotName: 'feeItem' }, + { key: 'status', label: '状态', width: 100, slotName: 'status', afterColumn: 'feeTypeName' }, { key: 'category', prop: 'category', label: '费用分类', minWidth: 150 }, { key: 'defaultPrice', label: '默认价格(CNY)', width: 150, formatter: (row: PlatformFeeItemResource) => row.defaultPrice == null ? '—' : Number(row.defaultPrice).toFixed(2) }, { key: 'tradeScope', label: '贸易范围', width: 110, slotName: 'tradeScope' }, @@ -131,7 +132,6 @@ const columns: DataTableColumn[] = [ { key: 'createdAt', label: '创建日期', minWidth: 155, formatter: (row: PlatformFeeItemResource) => formatTime(row.createdAt) }, { key: 'updatedByName', label: '更新人', minWidth: 120, formatter: (row: PlatformFeeItemResource) => row.updatedByName || '—' }, { key: 'updatedAt', label: '更新日期', minWidth: 155, formatter: (row: PlatformFeeItemResource) => formatTime(row.updatedAt) }, - { key: 'status', label: '状态', width: 100, slotName: 'status' }, ] function optionalText(value: string | null) { diff --git a/src/modules/finance/components/CustomerStatementsPanel.vue b/src/modules/finance/components/CustomerStatementsPanel.vue index f3d54354..1d052cd4 100644 --- a/src/modules/finance/components/CustomerStatementsPanel.vue +++ b/src/modules/finance/components/CustomerStatementsPanel.vue @@ -25,12 +25,12 @@ let requestSerial = 0 const columns: DataTableColumn[] = [ { key: 'actions', label: '操作', width: 92, fixed: 'left', slotName: 'actions' }, { key: 'billNo', prop: 'billNo', label: '对账单号', minWidth: 180 }, + { key: 'status', label: '状态', width: 110, slotName: 'status', afterColumn: 'billNo' }, { key: 'billDate', prop: 'billDate', label: '账单日期', width: 120 }, { key: 'dueDate', prop: 'dueDate', label: '到期日', width: 120 }, { key: 'totalAmount', label: '应收(CNY)', width: 130, align: 'right', slotName: 'totalAmount' }, { key: 'receivedAmount', label: '已收(CNY)', width: 130, align: 'right', slotName: 'receivedAmount' }, { key: 'outstandingAmount', label: '未收(CNY)', width: 130, align: 'right', slotName: 'outstandingAmount' }, - { key: 'status', label: '状态', width: 110, slotName: 'status' }, ] async function load() { diff --git a/src/modules/finance/pages/FinanceCustomerAccountsView.test.ts b/src/modules/finance/pages/FinanceCustomerAccountsView.test.ts index 123aa5d4..b174585e 100644 --- a/src/modules/finance/pages/FinanceCustomerAccountsView.test.ts +++ b/src/modules/finance/pages/FinanceCustomerAccountsView.test.ts @@ -97,8 +97,8 @@ describe('FinanceCustomerAccountsView delivery', () => { expect(page).not.toContain('text-overflow: ellipsis') }) - it('keeps account detail and term editing readable across the 1920 workspace', () => { - expect(page).toContain('width: min(100%, 1920px)') + it('keeps account detail and term editing readable across the full workspace width', () => { + expect(page).not.toContain('width: min(100%, 1920px)') expect(page).toContain('@media (min-width: 1920px)') expect(page).toContain('finance-density-detail') expect(page).toContain('../financeDensity.css') diff --git a/src/modules/finance/pages/FinanceCustomerAccountsView.vue b/src/modules/finance/pages/FinanceCustomerAccountsView.vue index 5216714b..a225a281 100644 --- a/src/modules/finance/pages/FinanceCustomerAccountsView.vue +++ b/src/modules/finance/pages/FinanceCustomerAccountsView.vue @@ -98,6 +98,7 @@ const columns: DataTableColumn[] = [ { key: 'actions', label: '操作', width: 290, fixed: 'left', slotName: 'actions' }, { key: 'customerName', label: '客户', minWidth: 180, slotName: 'customerName' }, { key: 'customerCode', label: '客户编号', width: 150, slotName: 'customerCode' }, + { key: 'status', label: '客户状态', width: 110, slotName: 'status', afterColumn: 'customerCode' }, { key: 'contactName', label: '联系人', width: 130, slotName: 'contactName' }, { key: 'contactPhone', label: '联系电话', width: 150, slotName: 'contactPhone' }, { key: 'actualBalance', label: '实际余额(CNY)', align: 'right', width: 155, slotName: 'actualBalance' }, @@ -107,7 +108,6 @@ const columns: DataTableColumn[] = [ { key: 'creditExpiresOn', label: '授信有效期', width: 140, slotName: 'creditExpiresOn' }, { key: 'creditUsed', label: '已用授信(CNY)', align: 'right', width: 150, slotName: 'creditUsed' }, { key: 'pendingRechargeCount', label: '待审核充值', align: 'right', width: 120, slotName: 'pendingRechargeCount' }, - { key: 'status', label: '客户状态', width: 110, slotName: 'status' }, { key: 'lastChangedAt', label: '最近变动', width: 170, slotName: 'lastChangedAt' }, ] const ledgerQueryFields: QueryField[] = [ @@ -133,10 +133,10 @@ const ledgerColumns: DataTableColumn[] = [ ] const rechargeColumns: DataTableColumn[] = [ { key: 'actions', label: '操作', width: 90, fixed: 'left', slotName: 'actions' }, - { key: 'requestNo', label: '申请单号', minWidth: 190 }, { key: 'createdAt', label: '提交时间', width: 170, slotName: 'createdAt' }, + { key: 'requestNo', label: '申请单号', minWidth: 190 }, + { key: 'status', label: '状态', width: 110, slotName: 'status', afterColumn: 'requestNo' }, { key: 'createdAt', label: '提交时间', width: 170, slotName: 'createdAt' }, { key: 'amount', label: '充值金额(CNY)', width: 150, align: 'right', slotName: 'amount' }, - { key: 'voucherFileName', label: '付款凭证', minWidth: 170, slotName: 'voucherFileName' }, { key: 'submittedByName', label: '提交人', width: 130 }, - { key: 'status', label: '状态', width: 110, slotName: 'status' }, { key: 'reviewedByName', label: '审核人', width: 130 }, + { key: 'voucherFileName', label: '付款凭证', minWidth: 170, slotName: 'voucherFileName' }, { key: 'submittedByName', label: '提交人', width: 130 }, { key: 'reviewedByName', label: '审核人', width: 130 }, { key: 'reviewRemark', label: '审核备注', minWidth: 180 }, { key: 'reviewedAt', label: '审核时间', width: 170, slotName: 'reviewedAt' }, ] const sortedRechargeRequests = computed(() => [...rechargeRequests.value].sort((left, right) => Number(right.status === 'PENDING') - Number(left.status === 'PENDING'))) @@ -560,7 +560,7 @@ onBeforeUnmount(clearVoucherPreview)
-
+
账期天数
天
@@ -646,7 +646,7 @@ onBeforeUnmount(clearVoucherPreview) } .finance-account-detail__surface { - width: min(100%, 1920px); + width: 100%; margin: 0 auto; overflow: hidden; } diff --git a/src/modules/finance/pages/FinanceCustomerArapView.test.ts b/src/modules/finance/pages/FinanceCustomerArapView.test.ts index 6bd103b7..6b812d12 100644 --- a/src/modules/finance/pages/FinanceCustomerArapView.test.ts +++ b/src/modules/finance/pages/FinanceCustomerArapView.test.ts @@ -281,9 +281,9 @@ describe('FinanceCustomerArapView G2 operations', () => { expect(wrapper.find('.arap-information').text()).not.toContain('T00:00:00') }) - it('keeps the fee-source detail readable across the 1920 workspace', () => { + it('keeps the fee-source detail readable across the full workspace width', () => { const page = readFileSync(resolve(process.cwd(), 'src/modules/finance/pages/FinanceCustomerArapView.vue'), 'utf8') - expect(page).toContain('width: min(100%, 1920px)') + expect(page).not.toContain('width: min(100%, 1920px)') expect(page).toContain('') expect(page).not.toContain('arap-detail-grid') expect(page).toContain('finance-density-detail') diff --git a/src/modules/finance/pages/FinanceCustomerArapView.vue b/src/modules/finance/pages/FinanceCustomerArapView.vue index b487960c..fb8d1179 100644 --- a/src/modules/finance/pages/FinanceCustomerArapView.vue +++ b/src/modules/finance/pages/FinanceCustomerArapView.vue @@ -149,11 +149,11 @@ const settlementStatusSpan = computed(() => { }) const columns: DataTableColumn[] = [ - { key: 'actions', label: '操作', width: 260, fixed: 'left', slotName: 'actions' }, { key: 'selection', type: 'selection', width: 48, fixed: 'left', selectable: isSelectableReceivable }, { key: 'direction', label: '收付方向', width: 100, slotName: 'direction' }, - { key: 'customerName', label: '客户', minWidth: 180, slotName: 'customerName' }, { key: 'businessReference', label: '关联业务', minWidth: 220, slotName: 'businessReference' }, { key: 'sourceType', label: '来源类型', width: 120, slotName: 'sourceType' }, - { key: 'feeName', label: '费用项目', minWidth: 150 }, { key: 'amount', label: '金额(CNY)', align: 'right', width: 130 }, { key: 'supplierName', label: '供应商', minWidth: 170, slotName: 'supplierName' }, - { key: 'status', label: '来源状态', width: 130, slotName: 'sourceStatus' }, - { key: 'reviewStatus', label: '审核状态', width: 120, slotName: 'status' }, { key: 'statementGenerated', label: '已生成对账单', width: 140, slotName: 'generated' }, { key: 'businessDate', label: '业务发生日', width: 170, slotName: 'businessDate' }, + { key: 'actions', label: '操作', width: 260, fixed: 'left', slotName: 'actions' }, { key: 'selection', type: 'selection', width: 48, fixed: 'left', selectable: isSelectableReceivable }, { key: 'businessReference', label: '关联业务', minWidth: 220, slotName: 'businessReference' }, + { key: 'status', label: '来源状态', width: 130, slotName: 'sourceStatus', afterColumn: 'businessReference' }, + { key: 'reviewStatus', label: '审核状态', width: 120, slotName: 'status', afterColumn: 'businessReference' }, { key: 'statementGenerated', label: '已生成对账单', width: 140, slotName: 'generated', afterColumn: 'businessReference' }, { key: 'direction', label: '收付方向', width: 100, slotName: 'direction' }, + { key: 'customerName', label: '客户', minWidth: 180, slotName: 'customerName' }, { key: 'sourceType', label: '来源类型', width: 120, slotName: 'sourceType' }, + { key: 'feeName', label: '费用项目', minWidth: 150 }, { key: 'amount', label: '金额(CNY)', align: 'right', width: 130 }, { key: 'supplierName', label: '供应商', minWidth: 170, slotName: 'supplierName' }, { key: 'businessDate', label: '业务发生日', width: 170, slotName: 'businessDate' }, ] function customerName(customerId: string, snapshot?: string) { const masterName = customers.value.find((item) => String(item.id) === String(customerId))?.tenantName?.trim() @@ -509,7 +509,7 @@ watch(() => [route.name, String(route.params.feeSourceId || '')], ([name, feeSou } .finance-arap-detail__surface { - width: min(100%, 1920px); + width: 100%; margin: 0 auto; overflow: hidden; } diff --git a/src/modules/finance/pages/FinanceCustomerStatementsView.test.ts b/src/modules/finance/pages/FinanceCustomerStatementsView.test.ts index 042d39dc..f1702f70 100644 --- a/src/modules/finance/pages/FinanceCustomerStatementsView.test.ts +++ b/src/modules/finance/pages/FinanceCustomerStatementsView.test.ts @@ -302,7 +302,7 @@ describe('FinanceCustomerStatementsView', () => { { label: '已结清', value: 'PAID' }, { label: '已作废', value: 'VOIDED' }, ]) - expect(statusColumn).toEqual({ key: 'status', label: '对账单状态', width: 120, slotName: 'status' }) + expect(statusColumn).toEqual({ key: 'status', label: '对账单状态', width: 120, slotName: 'status', afterColumn: 'billNo' }) }) it('creates a formal statement export task with only non-empty filters', async () => { diff --git a/src/modules/finance/pages/FinanceCustomerStatementsView.vue b/src/modules/finance/pages/FinanceCustomerStatementsView.vue index 6ce37b74..fdebe3d9 100644 --- a/src/modules/finance/pages/FinanceCustomerStatementsView.vue +++ b/src/modules/finance/pages/FinanceCustomerStatementsView.vue @@ -128,9 +128,10 @@ function resetFilters() { const columns: DataTableColumn[] = [ { key: 'actions', label: '操作', width: 280, fixed: 'left', slotName: 'actions' }, - { key: 'status', label: '对账单状态', width: 120, slotName: 'status' }, - { key: 'customerConfirmationStatus', label: '客户确认', width: 130, slotName: 'customerConfirmationStatus' }, { key: 'billNo', label: '对账单号', minWidth: 180 }, + { key: 'status', label: '对账单状态', width: 120, slotName: 'status', afterColumn: 'billNo' }, + { key: 'customerConfirmationStatus', label: '客户确认', width: 130, slotName: 'customerConfirmationStatus', afterColumn: 'billNo' }, + { key: 'settled', label: '是否结清', width: 100, slotName: 'settled', afterColumn: 'billNo' }, { key: 'customerName', label: '客户', minWidth: 180, slotName: 'customerName' }, { key: 'periodStart', label: '结算起始日', width: 130 }, { key: 'periodEnd', label: '结算结束日', width: 130 }, @@ -144,7 +145,6 @@ const columns: DataTableColumn[] = [ { key: 'receivedAmount', label: '已收金额(CNY)', align: 'right', width: 145 }, { key: 'outstandingAmount', label: '未收金额(CNY)', align: 'right', width: 145 }, { key: 'lastPaymentAt', label: '最后收款时间', minWidth: 170, slotName: 'lastPaymentAt' }, - { key: 'settled', label: '是否结清', width: 100, slotName: 'settled' }, { key: 'remark', label: '备注', minWidth: 180 }, { key: 'createdByName', label: '创建人', width: 130, slotName: 'createdByName' }, { key: 'createdAt', label: '创建时间', minWidth: 170, slotName: 'createdAt' }, @@ -765,7 +765,7 @@ watch(detailRouteId, (statementId) => { .statement-create-table td:not([colspan]) { width: 50%; } .statement-dialog-facts th { width: var(--admin-label-width); } .statement-adjust-editor { display: grid; gap: var(--admin-space-5); } -.statement-detail-page { min-width: 0; width: min(100%, 1920px); margin: 0 auto; overflow: hidden; } +.statement-detail-page { min-width: 0; width: 100%; margin: 0 auto; overflow: hidden; } .statement-summary-section { padding: var(--admin-space-4) var(--admin-space-6); border-bottom: 1px solid var(--workbench-border); } .statement-outstanding { color: var(--el-color-warning-dark-2); } .statement-detail-table { min-width: 0; } diff --git a/src/modules/finance/pages/FinanceFormalSettlementPage.vue b/src/modules/finance/pages/FinanceFormalSettlementPage.vue index 5efce432..a482ad5a 100644 --- a/src/modules/finance/pages/FinanceFormalSettlementPage.vue +++ b/src/modules/finance/pages/FinanceFormalSettlementPage.vue @@ -139,6 +139,8 @@ const columns = computed(() => props.kind === 'receivable' ? [ { key: 'actions', label: '操作', width: 250, fixed: 'left', slotName: 'actions' }, { key: 'settlementNo', label: '结算单号', minWidth: 180 }, + { key: 'paymentStatus', label: '收款状态', width: 120, slotName: 'paymentStatus', afterColumn: 'settlementNo' }, + { key: 'status', label: '结算状态', width: 120, slotName: 'settlementStatus', afterColumn: 'settlementNo' }, { key: 'customerId', label: '客户', minWidth: 180, slotName: 'customer' }, { key: 'feeItemSummary', label: '费用类型', minWidth: 150, slotName: 'feeItemSummary' }, { key: 'currency', label: '币种', width: 80 }, @@ -149,15 +151,15 @@ const columns = computed(() => props.kind === 'receivable' { key: 'businessPeriod', label: '结算周期', width: 120, slotName: 'businessPeriod' }, { key: 'generatedDate', label: '生成日期', width: 130, slotName: 'generatedDate' }, { key: 'dueDate', label: '到期日', width: 130, slotName: 'dueDate' }, - { key: 'paymentStatus', label: '收款状态', width: 120, slotName: 'paymentStatus' }, { key: 'createdByName', label: '制单人', minWidth: 130, slotName: 'createdByName' }, { key: 'createdAt', label: '创建时间', width: 170, slotName: 'createdAt' }, { key: 'lastPaymentAt', label: '最后收款时间', width: 170, slotName: 'lastPaymentAt' }, - { key: 'status', label: '结算状态', width: 120, slotName: 'settlementStatus' }, ] : [ { key: 'actions', label: '操作', width: 250, fixed: 'left', slotName: 'actions' }, { key: 'settlementNo', label: '结算单号', minWidth: 180 }, + { key: 'paymentStatus', label: '付款状态', width: 120, slotName: 'paymentStatus', afterColumn: 'settlementNo' }, + { key: 'status', label: '结算状态', width: 120, slotName: 'settlementStatus', afterColumn: 'settlementNo' }, { key: 'supplierName', label: '供应商', minWidth: 180, slotName: 'supplier' }, { key: 'customerId', label: '客户', minWidth: 180, slotName: 'customer' }, { key: 'feeItemSummary', label: '费用类型', minWidth: 150, slotName: 'feeItemSummary' }, @@ -168,11 +170,9 @@ const columns = computed(() => props.kind === 'receivable' { key: 'businessPeriod', label: '结算周期', width: 120, slotName: 'businessPeriod' }, { key: 'generatedDate', label: '生成日期', width: 130, slotName: 'generatedDate' }, { key: 'dueDate', label: '到期日', width: 130, slotName: 'dueDate' }, - { key: 'paymentStatus', label: '付款状态', width: 120, slotName: 'paymentStatus' }, { key: 'createdByName', label: '制单人', minWidth: 130, slotName: 'createdByName' }, { key: 'createdAt', label: '创建时间', width: 170, slotName: 'createdAt' }, { key: 'lastPaymentAt', label: '最后付款时间', width: 170, slotName: 'lastPaymentAt' }, - { key: 'status', label: '结算状态', width: 120, slotName: 'settlementStatus' }, ]) function money(value: number) { return `¥${Number(value || 0).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}` } @@ -738,7 +738,7 @@ onMounted(() => { .settlement-list-actions { display: flex; min-width: 0; align-items: center; flex-wrap: wrap; } .settlement-list-actions { flex: 0 0 auto; justify-content: flex-end; gap: var(--admin-space-3); } .settlement-list-actions :deep(.el-button + .el-button) { margin-left: 0; } -.settlement-detail-page { display: flex; width: min(100%, 1920px); margin: 0 auto; min-width: 0; gap: 0; overflow: hidden; padding: 0; } +.settlement-detail-page { display: flex; width: 100%; margin: 0 auto; min-width: 0; gap: 0; overflow: hidden; padding: 0; } .settlement-detail-actions { display: flex; align-items: center; gap: var(--admin-space-4); min-width: 0; flex-wrap: wrap; } .settlement-detail-state { margin: var(--admin-space-5) var(--admin-space-6); } .settlement-detail-content { min-width: 0; padding: 0 var(--admin-space-6) var(--admin-space-5); overflow: hidden; } diff --git a/src/modules/finance/pages/FinanceSettlementDialogs.test.ts b/src/modules/finance/pages/FinanceSettlementDialogs.test.ts index 22a3d156..a5f64dc4 100644 --- a/src/modules/finance/pages/FinanceSettlementDialogs.test.ts +++ b/src/modules/finance/pages/FinanceSettlementDialogs.test.ts @@ -5,6 +5,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { ElMessageBox } from 'element-plus' import { ApiResponseError } from '@/shared/api/http' import OperationErrorToast from '@/shared/components/OperationErrorToast.vue' +import { preferenceStorageKey } from '@/shared/utils/uiLayoutPreference' import { listPlatformTenants, type PlatformTenantResource } from '@/modules/iam' import { listSuppliers, type Supplier } from '@/modules/suppliers' import * as api from '../api/finance' @@ -110,6 +111,7 @@ function errorDetails(wrapper: VueWrapper) { } enableAutoUnmount(afterEach) beforeEach(() => { + localStorage.clear() vi.resetAllMocks() can.mockReturnValue(true) vi.stubGlobal('ResizeObserver', class { observe() {} unobserve() {} disconnect() {} }) @@ -129,6 +131,19 @@ beforeEach(() => { afterEach(() => { vi.restoreAllMocks(); vi.unstubAllGlobals(); document.body.innerHTML = '' }) describe('formal settlement native dialogs', () => { + it.each(['receivable', 'payable'] as const)('keeps %s statuses after the settlement number with saved old column order', async (kind) => { + localStorage.setItem(preferenceStorageKey('table', 'anonymous', `finance-settlement-${kind}-table`), JSON.stringify({ + version: 1, + order: ['actions', 'settlementNo', 'customerId', 'feeItemSummary', 'currency', 'paymentStatus', 'status'], + hidden: ['currency'], + widths: { settlementNo: 240 }, + })) + const wrapper = await mountPage(kind) + const headers = wrapper.findAll('.el-table__header-wrapper th .cell').map(header => header.text()) + expect(headers.slice(0, 4)).toEqual(['操作', '结算单号', kind === 'receivable' ? '收款状态' : '付款状态', '结算状态']) + expect(headers).not.toContain('币种') + }) + it('creates receivables from issued statements with original long IDs and CNY', async () => { const { dialog } = await openCreate('receivable') expect(dialog.props('width')).toContain('600px') diff --git a/src/modules/finance/pages/FinanceSettlementRouteLayout.test.ts b/src/modules/finance/pages/FinanceSettlementRouteLayout.test.ts index aef1d8b6..0ad1ad2e 100644 --- a/src/modules/finance/pages/FinanceSettlementRouteLayout.test.ts +++ b/src/modules/finance/pages/FinanceSettlementRouteLayout.test.ts @@ -19,5 +19,6 @@ describe('Finance settlement route 1920px layout', () => { const shared = readFileSync(resolve(process.cwd(), 'src/modules/finance/pages/FinanceFormalSettlementPage.vue'), 'utf8') expect(shared).toContain(':column="4" class="settlement-detail-info density-descriptions"') expect(shared).toContain('finance-density-detail') + expect(shared).not.toContain('width: min(100%, 1920px)') }) }) diff --git a/src/modules/iam/pages/InternalUsersView.vue b/src/modules/iam/pages/InternalUsersView.vue index ce1ff1b7..520b13fd 100644 --- a/src/modules/iam/pages/InternalUsersView.vue +++ b/src/modules/iam/pages/InternalUsersView.vue @@ -156,9 +156,9 @@ const formRules = computed(() => ({ const userColumns: DataTableColumn[] = [ { key: 'actions', label: '操作', width: 170, fixed: 'left', slotName: 'actions' }, { key: 'username', prop: 'username', label: '用户名', minWidth: 145 }, + { key: 'status', label: '状态', width: 100, slotName: 'status', afterColumn: 'username' }, { key: 'displayName', prop: 'displayName', label: '姓名', minWidth: 130 }, { key: 'roles', label: '角色', minWidth: 180, slotName: 'roles' }, - { key: 'status', label: '状态', width: 100, slotName: 'status' }, { key: 'lastLoginAt', label: '最后登录', minWidth: 170, slotName: 'lastLoginAt' }, ] diff --git a/src/modules/iam/pages/PermissionsView.vue b/src/modules/iam/pages/PermissionsView.vue index beb02c32..a8fd275b 100644 --- a/src/modules/iam/pages/PermissionsView.vue +++ b/src/modules/iam/pages/PermissionsView.vue @@ -92,12 +92,12 @@ const parentOptions = computed(() => permissions.value.filter((item) => ( const permissionColumns: DataTableColumn[] = [ { key: 'actions', label: '操作', width: 280, fixed: 'left', slotName: 'actions', className: 'permission-actions-column' }, { key: 'name', label: '权限名称', minWidth: 260, slotName: 'name' }, + { key: 'status', label: '状态', width: 88, slotName: 'status', afterColumn: 'name' }, { key: 'menuCount', label: '菜单数', width: 90, align: 'right', formatter: (row: PermissionTreeNode) => permissionCounts(row).menuCount }, { key: 'actionCount', label: '操作数', width: 90, align: 'right', formatter: (row: PermissionTreeNode) => permissionCounts(row).actionCount }, { key: 'type', prop: 'type', label: '类型', width: 80 }, { key: 'endpoint', label: '路由 / 接口', minWidth: 260, slotName: 'endpoint' }, { key: 'sort', prop: 'sort', label: '排序', width: 72, align: 'right' }, - { key: 'status', label: '状态', width: 88, slotName: 'status' }, ] async function loadPermissions() { diff --git a/src/modules/iam/pages/RolesView.vue b/src/modules/iam/pages/RolesView.vue index 75d25ba9..e6721bcd 100644 --- a/src/modules/iam/pages/RolesView.vue +++ b/src/modules/iam/pages/RolesView.vue @@ -85,11 +85,11 @@ const roleColumns: DataTableColumn[] = [ { key: 'actions', label: '操作', width: 250, fixed: 'left', slotName: 'actions' }, { key: 'role', label: '角色名称', minWidth: 180, slotName: 'role' }, { key: 'roleCode', prop: 'code', label: '角色编码', minWidth: 180, showOverflowTooltip: true }, + { key: 'status', label: '状态', width: 88, slotName: 'status', afterColumn: 'roleCode' }, { key: 'description', prop: 'description', label: '说明', minWidth: 240, showOverflowTooltip: true }, { key: 'permissionSummary', label: '授权范围', minWidth: 170, slotName: 'permissionSummary' }, { key: 'userCount', prop: 'userCount', label: '用户数', width: 86, align: 'right' }, { key: 'roleType', label: '类型', width: 95, slotName: 'roleType' }, - { key: 'status', label: '状态', width: 88, slotName: 'status' }, ] const availablePermissions = computed(() => serverPermissions.value) const permissionTree = computed(() => buildNavigationPermissionTree(availablePermissions.value, adminNavigation)) diff --git a/src/modules/inbounds/components/InboundTaskList.vue b/src/modules/inbounds/components/InboundTaskList.vue index 37d0eb11..3c88a6b6 100644 --- a/src/modules/inbounds/components/InboundTaskList.vue +++ b/src/modules/inbounds/components/InboundTaskList.vue @@ -102,8 +102,8 @@ const columns = computed(() => [ selectable: (row: InboundOrderSummary) => row.measureReviewStatus === 'PENDING' && !batchReviewLoading.value, } satisfies DataTableColumn] : []), { key: 'inboundCode', label: '入仓码', minWidth: 140, slotName: 'inboundCode' }, - { key: 'processStatus', label: '流程状态', minWidth: 132, slotName: 'processStatus' }, - { key: 'measureReviewStatus', label: '测量审核状态', minWidth: 120, slotName: 'measureReviewStatus' }, + { key: 'processStatus', label: '流程状态', minWidth: 132, slotName: 'processStatus', afterColumn: 'inboundCode' }, + { key: 'measureReviewStatus', label: '测量审核状态', minWidth: 120, slotName: 'measureReviewStatus', afterColumn: 'inboundCode' }, { key: 'expectedArrivalAt', label: '预计到达时间', minWidth: 140, slotName: 'expectedArrivalAt' }, { key: 'customerName', label: '客户', minWidth: 140, slotName: 'customerName' }, { key: 'warehouseName', label: '仓库', minWidth: 120, slotName: 'warehouseName' }, diff --git a/src/modules/inbounds/measurement/InboundMeasurementPanel.test.ts b/src/modules/inbounds/measurement/InboundMeasurementPanel.test.ts index c8af2b49..137c5943 100644 --- a/src/modules/inbounds/measurement/InboundMeasurementPanel.test.ts +++ b/src/modules/inbounds/measurement/InboundMeasurementPanel.test.ts @@ -404,7 +404,7 @@ describe('InboundMeasurementPanel', () => { const payload = vi.mocked(saveInboundMeasurementDraft).mock.calls[0]?.[2] expect(payload?.items[0]).toMatchObject({ - itemNo: '001', outerLengthCm: 55, outerWidthCm: null, outerHeightCm: null, + itemNo: '001', outerLengthCm: 55, outerWidthCm: null, outerHeightCm: null, coefficientMode: 'ACTUAL', }) }) diff --git a/src/modules/inbounds/measurement/InboundMeasurementPanel.vue b/src/modules/inbounds/measurement/InboundMeasurementPanel.vue index b0ab0542..35a038fa 100644 --- a/src/modules/inbounds/measurement/InboundMeasurementPanel.vue +++ b/src/modules/inbounds/measurement/InboundMeasurementPanel.vue @@ -492,7 +492,7 @@ async function saveDraft() { intelligentImageFileId: item.intelligentImageFileId, overLimitWarning: item.overLimitWarning, outerLengthCm: item.outerLengthCm, outerWidthCm: item.outerWidthCm, outerHeightCm: item.outerHeightCm, chargeRuleId: item.coefficientMode === 'MULTIPLY' ? item.chargeRuleId : null, - coefficientMode: item.coefficientMode === 'ACTUAL' ? null : item.coefficientMode, remark: item.remark, + coefficientMode: item.coefficientMode, remark: item.remark, })), }) version = saved.version diff --git a/src/modules/inbounds/pages/InboundOrdersView.vue b/src/modules/inbounds/pages/InboundOrdersView.vue index 61c7f346..9cb2be75 100644 --- a/src/modules/inbounds/pages/InboundOrdersView.vue +++ b/src/modules/inbounds/pages/InboundOrdersView.vue @@ -117,17 +117,17 @@ const queryFields = computed(() => showAdvancedFilters.value const columns: DataTableColumn[] = [ { key: 'actions', label: '操作', width: 228, fixed: 'left', slotName: 'actions' }, { key: 'inboundCode', label: '入仓码', minWidth: 166, slotName: 'inbound' }, - { key: 'processStatus', label: '入库流程状态', minWidth: 132, slotName: 'processStatus' }, - { key: 'orderStatus', label: '订单状态', minWidth: 120, slotName: 'orderStatus' }, - { key: 'measureReviewStatus', label: '测量数据审核状态', minWidth: 142, slotName: 'measureReviewStatus' }, + { key: 'processStatus', label: '入库流程状态', minWidth: 132, slotName: 'processStatus', afterColumn: 'inboundCode' }, + { key: 'orderStatus', label: '订单状态', minWidth: 120, slotName: 'orderStatus', afterColumn: 'inboundCode' }, + { key: 'measureReviewStatus', label: '测量数据审核状态', minWidth: 142, slotName: 'measureReviewStatus', afterColumn: 'inboundCode' }, + { key: 'feeStatus', label: '费用状态', minWidth: 112, slotName: 'feeStatus', afterColumn: 'inboundCode' }, + { key: 'feeReviewStatus', label: '费用审核状态', minWidth: 132, slotName: 'feeReviewStatus', afterColumn: 'inboundCode' }, + { key: 'loadingStatus', label: '配载状态', minWidth: 110, slotName: 'loadingStatus', defaultVisible: false, afterColumn: 'inboundCode' }, + { key: 'holdFlag', label: '是否扣货', width: 96, slotName: 'holdFlag', defaultVisible: false, afterColumn: 'inboundCode' }, { key: 'expectedArrivalAt', label: '预计到达时间', minWidth: 164, slotName: 'expectedArrivalAt' }, - { key: 'feeStatus', label: '费用状态', minWidth: 112, slotName: 'feeStatus' }, - { key: 'feeReviewStatus', label: '费用审核状态', minWidth: 132, slotName: 'feeReviewStatus' }, { key: 'customerName', label: '客户', minWidth: 142, slotName: 'customerName' }, { key: 'warehouseName', label: '仓库', minWidth: 140, slotName: 'warehouseName' }, { key: 'deliveryWarehouseName', label: '交货仓', minWidth: 140, slotName: 'deliveryWarehouseName' }, - { key: 'loadingStatus', label: '配载状态', minWidth: 110, slotName: 'loadingStatus', defaultVisible: false }, - { key: 'holdFlag', label: '是否扣货', width: 96, slotName: 'holdFlag', defaultVisible: false }, { key: 'currentHoldReason', label: '扣货理由', minWidth: 150, slotName: 'currentHoldReason', defaultVisible: false }, { key: 'containerNo', label: '集装箱号', minWidth: 132, slotName: 'containerNo', defaultVisible: false }, { key: 'cabinetNo', label: '柜号', minWidth: 118, slotName: 'cabinetNo', defaultVisible: false }, diff --git a/src/modules/loading-lists/components/ContainerFeesPanel.vue b/src/modules/loading-lists/components/ContainerFeesPanel.vue index 91bab57a..1464cd18 100644 --- a/src/modules/loading-lists/components/ContainerFeesPanel.vue +++ b/src/modules/loading-lists/components/ContainerFeesPanel.vue @@ -96,6 +96,8 @@ const selectedFeeItem = computed(() => feeItems.value.find((item) => item.id === const columns: DataTableColumn[] = [ { key: 'actions', label: '操作', fixed: 'left', width: 270, slotName: 'actions', hideable: false, movable: false }, { key: 'feeItemName', prop: 'feeItemName', label: '费用项目', minWidth: 150 }, + { key: 'reviewStatus', label: '审核状态', width: 110, slotName: 'reviewStatus', afterColumn: 'feeItemName' }, + { key: 'sourceStatus', label: '来源状态', width: 100, slotName: 'sourceStatus', afterColumn: 'feeItemName' }, { key: 'direction', label: '方向', width: 90, slotName: 'direction' }, { key: 'settlementMode', label: '结算方式', width: 120, slotName: 'settlementMode' }, { key: 'paidSummary', label: '已付摘要', minWidth: 290, slotName: 'paidSummary' }, @@ -103,8 +105,6 @@ const columns: DataTableColumn[] = [ { key: 'businessReference', label: '费用归属', minWidth: 220, slotName: 'businessReference' }, { key: 'amount', label: '金额(CNY)', width: 120, align: 'right', slotName: 'amount' }, { key: 'businessDate', prop: 'businessDate', label: '业务日期', width: 120 }, - { key: 'reviewStatus', label: '审核状态', width: 110, slotName: 'reviewStatus' }, - { key: 'sourceStatus', label: '来源状态', width: 100, slotName: 'sourceStatus' }, { key: 'remark', prop: 'remark', label: '备注', minWidth: 180, showOverflowTooltip: true }, ] const allocationColumns: DataTableColumn[] = [ diff --git a/src/modules/loading-lists/components/LoadingListsView.vue b/src/modules/loading-lists/components/LoadingListsView.vue index 723a463d..e9e9596b 100644 --- a/src/modules/loading-lists/components/LoadingListsView.vue +++ b/src/modules/loading-lists/components/LoadingListsView.vue @@ -60,13 +60,13 @@ const fields = computed(() => [ const columns = computed(() => isPreload.value ? [ { key: 'actions', label: '操作', fixed: 'left', width: 88, slotName: 'actions', hideable: false, movable: false }, { key: 'preloadNo', prop: 'preloadNo', label: '预装单号', minWidth: 120 }, + { key: 'status', label: '状态', width: 108, slotName: 'status', afterColumn: 'preloadNo' }, { key: 'containerNo', prop: 'containerNo', label: '集装箱号', minWidth: 120 }, { key: 'cabinetNo', prop: 'cabinetNo', label: '柜次号', minWidth: 140 }, // { key: 'customers', label: '客户', minWidth: 190, slotName: 'customers' }, { key: 'warehouseName', prop: 'warehouseName', label: '仓库', minWidth: 80 }, { key: 'vesselVoyage', label: '船名 / 航次', minWidth: 175, slotName: 'vesselVoyage' }, { key: 'containerType', prop: 'containerType', label: '箱型', width: 80 }, - { key: 'status', label: '状态', width: 108, slotName: 'status' }, { key: 'totalPackageCount', prop: 'totalPackageCount', label: '总箱数', width: 90, align: 'right' }, { key: 'skuCount', prop: 'skuCount', label: 'SKU 数', width: 90, align: 'right' }, { key: 'totalVolumeCbm', label: '总体积(m³)', width: 90, align: 'right', slotName: 'volume' }, @@ -75,10 +75,10 @@ const columns = computed(() => isPreload.value ? [ ] : [ { key: 'actions', label: '操作', fixed: 'left', width: 150, slotName: 'actions', hideable: false, movable: false }, { key: 'loadingListNo', prop: 'loadingListNo', label: '装柜清单号', minWidth: 175 }, + { key: 'status', label: '状态', width: 110, slotName: 'status', afterColumn: 'loadingListNo' }, { key: 'containerNo', prop: 'containerNo', label: '集装箱号', minWidth: 145 }, { key: 'cabinetNo', prop: 'cabinetNo', label: '柜次号', minWidth: 150 }, { key: 'warehouseName', prop: 'warehouseName', label: '仓库', minWidth: 140 }, - { key: 'status', label: '状态', width: 110, slotName: 'status' }, { key: 'totalPackageCount', prop: 'totalPackageCount', label: '总箱数', width: 90, align: 'right' }, { key: 'totalGrossWeightKg', label: '总毛重(kg)', width: 125, align: 'right', slotName: 'grossWeight' }, { key: 'totalNetWeightKg', label: '总净重(kg)', width: 125, align: 'right', slotName: 'netWeight' }, diff --git a/src/modules/loading-lists/pages/ConfirmedLoadingListDetailView.vue b/src/modules/loading-lists/pages/ConfirmedLoadingListDetailView.vue index 8ea1a00a..9abd9b3f 100644 --- a/src/modules/loading-lists/pages/ConfirmedLoadingListDetailView.vue +++ b/src/modules/loading-lists/pages/ConfirmedLoadingListDetailView.vue @@ -48,7 +48,7 @@ watch([planId, warehouseId], () => void loadDetail()) diff --git a/src/modules/master-data/counterparties/pages/PartnersView.vue b/src/modules/master-data/counterparties/pages/PartnersView.vue index 4bf2fdbd..a177741c 100644 --- a/src/modules/master-data/counterparties/pages/PartnersView.vue +++ b/src/modules/master-data/counterparties/pages/PartnersView.vue @@ -157,12 +157,12 @@ const partnerColumns: DataTableColumn[] = [ { key: 'actions', label: '操作', width: 250, fixed: 'left', slotName: 'actions' }, { key: 'partner', label: '单位名称', minWidth: 210, slotName: 'partner' }, { key: 'partnerCode', prop: 'partnerCode', label: '单位编码', minWidth: 130, defaultVisible: false }, + { key: 'status', label: '状态', width: 90, slotName: 'status', afterColumn: 'partnerCode' }, { key: 'type', label: '类型', minWidth: 115, slotName: 'type' }, { key: 'contact', prop: 'contactName', label: '联系人', minWidth: 130 }, { key: 'contactPhone', prop: 'contactPhone', label: '电话', minWidth: 145 }, { key: 'contactEmail', prop: 'contactEmail', label: '邮箱', minWidth: 190, defaultVisible: false, formatter: (row: PlatformPartnerResource) => row.contactEmail || '—' }, { key: 'address', label: '地址', minWidth: 240, slotName: 'address', defaultVisible: false }, - { key: 'status', label: '状态', width: 90, slotName: 'status' }, { key: 'updatedAt', prop: 'updatedAt', label: '更新时间', minWidth: 140, defaultVisible: false, formatter: (row: PlatformPartnerResource) => formatUpdatedAt(row.updatedAt) }, ] diff --git a/src/modules/master-data/express-companies/pages/ExpressCompaniesView.vue b/src/modules/master-data/express-companies/pages/ExpressCompaniesView.vue index be7244a0..0f5c241e 100644 --- a/src/modules/master-data/express-companies/pages/ExpressCompaniesView.vue +++ b/src/modules/master-data/express-companies/pages/ExpressCompaniesView.vue @@ -124,9 +124,9 @@ const expressCompanyColumns: DataTableColumn[] = [ { key: 'actions', label: '操作', width: 170, fixed: 'left', slotName: 'actions' }, { key: 'company', label: '公司名称', minWidth: 210, slotName: 'company' }, { key: 'companyCode', prop: 'companyCode', label: '编码', minWidth: 120 }, + { key: 'status', label: '状态', width: 100, slotName: 'status', afterColumn: 'companyCode' }, { key: 'website', label: '官方网站', minWidth: 210, slotName: 'website' }, { key: 'servicePhone', prop: 'servicePhone', label: '客服电话', minWidth: 130, formatter: (row: PlatformExpressCompanyResource) => row.servicePhone || '—' }, - { key: 'status', label: '状态', width: 100, slotName: 'status' }, { key: 'updatedAt', prop: 'updatedAt', label: '更新时间', minWidth: 150, defaultVisible: false, formatter: (row: PlatformExpressCompanyResource) => formatUpdatedAt(row.updatedAt) }, ] diff --git a/src/modules/notifications/pages/MessageManagementView.vue b/src/modules/notifications/pages/MessageManagementView.vue index 28b58dec..1d0ea580 100644 --- a/src/modules/notifications/pages/MessageManagementView.vue +++ b/src/modules/notifications/pages/MessageManagementView.vue @@ -9,7 +9,7 @@ import MessageComposerDialog from '../components/MessageComposerDialog.vue'; imp import { notificationAppLabel, notificationCategoryLabel, notificationRecipientStatusLabel } from '../presentation' const access=useAccessPreviewStore(),page=ref(1),pageSize=ref(20),loading=ref(false),actionId=ref(''),error=ref(null),composerOpen=ref(false),detailOpen=ref(false),detailLoading=ref(false),detailError=ref(null),detail=ref(null),templates=ref([]),result=ref({records:[] as NotificationTask[],total:0,pages:0}),filters=ref({category:'',status:'',keyword:''}) const queryFields:QueryField[]=[{key:'category',type:'input',label:'业务分类',placeholder:'业务分类'},{key:'status',type:'select',label:'任务状态',placeholder:'全部状态',options:['CREATED','SCHEDULED','DISPATCHING','RETRYING','SENT','DEAD','CANCELLED'].map(value=>({label:statusLabel(value as NotificationTaskStatus),value}))},{key:'keyword',type:'input',label:'任务号或标题',placeholder:'任务号/消息标题'}] -const columns:DataTableColumn[]=[{key:'actions',label:'操作',width:240,fixed:'left',slotName:'actions'},{key:'taskNo',prop:'taskNo',label:'任务号',minWidth:190},{key:'title',prop:'title',label:'标题',minWidth:220},{key:'category',label:'业务分类',width:130,slotName:'category'},{key:'recipientCount',prop:'recipientCount',label:'接收人数',width:100},{key:'scheduledAt',label:'定时发送',width:170,slotName:'scheduledAt'},{key:'sentAt',label:'发送时间',width:170,slotName:'sentAt'},{key:'status',label:'状态',width:110,slotName:'status'}] +const columns:DataTableColumn[]=[{key:'actions',label:'操作',width:240,fixed:'left',slotName:'actions'},{key:'taskNo',prop:'taskNo',label:'任务号',minWidth:190},{key:'status',label:'状态',width:110,slotName:'status', afterColumn: 'taskNo' },{key:'title',prop:'title',label:'标题',minWidth:220},{key:'category',label:'业务分类',width:130,slotName:'category'},{key:'recipientCount',prop:'recipientCount',label:'接收人数',width:100},{key:'scheduledAt',label:'定时发送',width:170,slotName:'scheduledAt'},{key:'sentAt',label:'发送时间',width:170,slotName:'sentAt'}] const detailRow = ref(null) let detailRequest = 0 watch(detailOpen, value => { if (!value) { detailRequest++; detailRow.value = null } }) @@ -43,8 +43,8 @@ watch([page,pageSize],load);onMounted(()=>{void load();void loadTemplates()})

收件人投递明细

- +
diff --git a/src/modules/notifications/pages/MessageTemplatesView.vue b/src/modules/notifications/pages/MessageTemplatesView.vue index d69e80b5..ec3ebd2e 100644 --- a/src/modules/notifications/pages/MessageTemplatesView.vue +++ b/src/modules/notifications/pages/MessageTemplatesView.vue @@ -5,7 +5,7 @@ import { onMounted, ref, watch } from 'vue'; import { Plus } from '@lucide/vue'; import AdminPage from '@/shared/components/AdminPage.vue'; import DataTable,{type DataTableColumn} from '@/shared/components/DataTable.vue'; import ListPageLayout from '@/shared/components/ListPageLayout.vue'; import QueryToolbar,{type QueryField,type QueryModel} from '@/shared/components/QueryToolbar.vue'; import LoadErrorState from '@/shared/components/LoadErrorState.vue'; import ApiErrorMeta from '@/shared/components/ApiErrorMeta.vue'; import TemplateEditorDialog from '../components/TemplateEditorDialog.vue'; import { changeTemplateStatus, listTemplates } from '../api/notificationCenter'; import type { NotificationTemplate, TemplateStatus } from '../types'; import { getApiErrorDetails,getApiErrorMessage,type ApiErrorDetails } from '@/shared/api/http'; import { useAccessPreviewStore } from '@/core/access/accessPreview' const access=useAccessPreviewStore(),page=ref(1),pageSize=ref(20),loading=ref(false),actionId=ref(''),error=ref(null),editorOpen=ref(false),editing=ref(null),result=ref({records:[] as NotificationTemplate[],total:0,pages:0}),filters=ref({code:'',category:'',title:'',status:''}) const queryFields:QueryField[]=[{key:'code',type:'input',label:'模板CODE',placeholder:'模板CODE'},{key:'category',type:'input',label:'业务分类',placeholder:'业务分类'},{key:'title',type:'input',label:'名称或标题',placeholder:'模板名称/标题'},{key:'status',type:'select',label:'状态',placeholder:'全部状态',options:[{label:'草稿',value:'DRAFT'},{label:'已激活',value:'ACTIVE'},{label:'已禁用',value:'DISABLED'}]}] -const columns:DataTableColumn[]=[{key:'actions',label:'操作',width:190,fixed:'left',slotName:'actions'},{key:'templateCode',prop:'templateCode',label:'模板CODE',minWidth:190},{key:'templateName',prop:'templateName',label:'模板名称',minWidth:170},{key:'category',prop:'category',label:'业务分类',width:130},{key:'titleTemplate',prop:'titleTemplate',label:'消息标题模板',minWidth:220},{key:'bodyTemplate',prop:'bodyTemplate',label:'正文模板',minWidth:320,showOverflowTooltip:true},{key:'status',label:'状态',width:100,slotName:'status'},{key:'version',prop:'version',label:'版本',width:80}] +const columns:DataTableColumn[]=[{key:'actions',label:'操作',width:190,fixed:'left',slotName:'actions'},{key:'templateCode',prop:'templateCode',label:'模板CODE',minWidth:190},{key:'status',label:'状态',width:100,slotName:'status', afterColumn: 'templateCode' },{key:'templateName',prop:'templateName',label:'模板名称',minWidth:170},{key:'category',prop:'category',label:'业务分类',width:130},{key:'titleTemplate',prop:'titleTemplate',label:'消息标题模板',minWidth:220},{key:'bodyTemplate',prop:'bodyTemplate',label:'正文模板',minWidth:320,showOverflowTooltip:true},{key:'version',prop:'version',label:'版本',width:80}] function statusLabel(v:TemplateStatus){return {DRAFT:'草稿',ACTIVE:'已激活',DISABLED:'已禁用'}[v]} async function load(){loading.value=true;error.value=null;try{const d=await listTemplates({page:page.value,size:pageSize.value,templateCode:String(filters.value.code||'').trim()||undefined,category:String(filters.value.category||'').trim()||undefined,title:String(filters.value.title||'').trim()||undefined,status:(filters.value.status||undefined) as TemplateStatus|undefined});result.value={records:d.records,total:d.total,pages:d.pages}}catch(e){error.value=getApiErrorDetails(e,'模板加载失败')}finally{loading.value=false}} function search(){page.value===1?void load():page.value=1}function create(){editing.value=null;editorOpen.value=true}function edit(row:NotificationTemplate){editing.value=row;editorOpen.value=true} diff --git a/src/modules/notifications/pages/PlatformNotificationsView.vue b/src/modules/notifications/pages/PlatformNotificationsView.vue index 972ad472..4155a7cd 100644 --- a/src/modules/notifications/pages/PlatformNotificationsView.vue +++ b/src/modules/notifications/pages/PlatformNotificationsView.vue @@ -41,10 +41,10 @@ const queryFields: QueryField[] = [ const columns: DataTableColumn[] = [ { key: 'actions', label: '操作', width: 190, fixed: 'left', slotName: 'actions' }, { key: 'title', prop: 'title', label: '消息标题', minWidth: 220 }, + { key: 'status', label: '状态', width: 90, slotName: 'status', afterColumn: 'title' }, { key: 'summary', prop: 'summary', label: '内容摘要', minWidth: 300, showOverflowTooltip: true }, { key: 'category', label: '分类', width: 130, slotName: 'category' }, { key: 'sentAt', label: '发送时间', width: 180, slotName: 'sentAt' }, - { key: 'status', label: '状态', width: 90, slotName: 'status' }, ] function statusLabel(status: NotificationInboxStatus) { return status === 'UNREAD' ? '未读' : '已读' } diff --git a/src/modules/ocean-bookings/pages/OceanBookingsView.vue b/src/modules/ocean-bookings/pages/OceanBookingsView.vue index 28659f1a..4672297a 100644 --- a/src/modules/ocean-bookings/pages/OceanBookingsView.vue +++ b/src/modules/ocean-bookings/pages/OceanBookingsView.vue @@ -43,6 +43,7 @@ const fields = computed(() => [ const columns = [ { key: 'actions', label: '操作', fixed: 'left', width: 196, slotName: 'actions', hideable: false, movable: false }, { prop: 'bookingNo', label: '订舱号', minWidth: 150 }, + { prop: 'status', label: '状态', minWidth: 110, slotName: 'status', afterColumn: 'bookingNo' }, { prop: 'customerName', label: '客户', minWidth: 160, formatter: (row: OceanBookingResource) => row.customerName || '—' }, { prop: 'billOfLadingNo', label: '提单号', minWidth: 155, formatter: (row: OceanBookingResource) => row.billOfLadingNo || '—' }, { prop: 'shipperName', label: '提单发货人', minWidth: 160, formatter: (row: OceanBookingResource) => row.shipperName || '—' }, @@ -55,7 +56,6 @@ const columns = [ { prop: 'pod', label: '目的港', minWidth: 125 }, { prop: 'etd', label: '预计开船', minWidth: 155, slotName: 'etd' }, { prop: 'eta', label: '预计到港', minWidth: 155, slotName: 'eta' }, - { prop: 'status', label: '状态', minWidth: 110, slotName: 'status' }, { prop: 'updatedAt', label: '更新时间', minWidth: 155, slotName: 'updatedAt' }, ] function formatDateTime(value?: string | null) { return value ? value.replace('T', ' ').slice(0, 16) : '—' } diff --git a/src/modules/outbound-dispatch/pages/OutboundDispatchesView.vue b/src/modules/outbound-dispatch/pages/OutboundDispatchesView.vue index bac20e6d..deec2c08 100644 --- a/src/modules/outbound-dispatch/pages/OutboundDispatchesView.vue +++ b/src/modules/outbound-dispatch/pages/OutboundDispatchesView.vue @@ -49,13 +49,13 @@ const fields = computed(() => [ const columns = [ { key: 'actions', label: '操作', fixed: 'left', width: 240, slotName: 'actions', hideable: false, movable: false }, { prop: 'inboundCode', label: '入仓码', minWidth: 145 }, + { prop: 'oceanBookingStatus', label: '订舱', width: 105, slotName: 'booking', afterColumn: 'inboundCode' }, + { prop: 'truckAppointmentStatus', label: '拖车', width: 105, slotName: 'truck', afterColumn: 'inboundCode' }, + { prop: 'pickTaskStatus', label: '拣货', width: 105, slotName: 'pick', afterColumn: 'inboundCode' }, + { prop: 'dispatchStatus', label: '出仓状态', width: 115, slotName: 'status', afterColumn: 'inboundCode' }, { prop: 'customerName', label: '客户', minWidth: 140 }, { prop: 'cabinetNo', label: '柜次号', minWidth: 145, formatter: (row: OutboundDispatchResource) => row.cabinetNo || '—' }, { prop: 'containerNo', label: '集装箱号', minWidth: 145, formatter: (row: OutboundDispatchResource) => row.containerNo || '—' }, - { prop: 'oceanBookingStatus', label: '订舱', width: 105, slotName: 'booking' }, - { prop: 'truckAppointmentStatus', label: '拖车', width: 105, slotName: 'truck' }, - { prop: 'pickTaskStatus', label: '拣货', width: 105, slotName: 'pick' }, - { prop: 'dispatchStatus', label: '出仓状态', width: 115, slotName: 'status' }, { key: 'gate', label: '门禁', width: 90, slotName: 'gate' }, ] diff --git a/src/modules/overseas-business/components/BusinessOrderFees.vue b/src/modules/overseas-business/components/BusinessOrderFees.vue index da72ff30..9e42e7d8 100644 --- a/src/modules/overseas-business/components/BusinessOrderFees.vue +++ b/src/modules/overseas-business/components/BusinessOrderFees.vue @@ -47,14 +47,14 @@ const collectionLabel = (fee: OverseasFee) => fee.direction === 'PAYABLE' ? '推 const columns = computed(() => [ { key: 'actions', label: '操作', width: 220, fixed: 'left', slotName: 'actions' }, { prop: 'feeItemName', label: '费用项目', minWidth: 150 }, + { prop: 'reviewStatus', label: '审核状态', width: 100, slotName: 'reviewStatus', afterColumn: 'feeItemName' }, + { key: 'chargeStatus', label: '费用状态', width: 120, formatter: feeProcessingLabel, afterColumn: 'feeItemName' }, ...(props.direction === 'PAYABLE' ? [{ prop: 'supplierName', label: '供应商', minWidth: 180 }] : []), { key: 'amount', label: '金额', width: 150, align: 'right', formatter: (row: OverseasFee) => feeMoney(row.originalAmount, row.originalCurrency) }, { key: 'settlementAmount', label: '结算金额(CNY)', width: 145, align: 'right', formatter: (row: OverseasFee) => feeMoney(row.amount) }, { prop: 'collectionMode', label: '结算方式', width: 110, formatter: collectionLabel }, - { prop: 'reviewStatus', label: '审核状态', width: 100, slotName: 'reviewStatus' }, { prop: 'reviewerName', label: '审核人', width: 120, formatter: (row: OverseasFee) => row.reviewerName || row.reviewerId || '—' }, { prop: 'reviewedAt', label: '审核时间', width: 160, formatter: (row: OverseasFee) => feeTime(row.reviewedAt) }, - { key: 'chargeStatus', label: '费用状态', width: 120, formatter: feeProcessingLabel }, { prop: 'businessDate', label: '费用日期', width: 110 }, ]) let loadSequence = 0, detailSequence = 0, itemsSequence = 0 diff --git a/src/modules/overseas-business/components/OverseasFeeEditor.vue b/src/modules/overseas-business/components/OverseasFeeEditor.vue index e6ac2735..92879c42 100644 --- a/src/modules/overseas-business/components/OverseasFeeEditor.vue +++ b/src/modules/overseas-business/components/OverseasFeeEditor.vue @@ -123,7 +123,7 @@ async function save() {