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 }} |
emit('external', value)" />
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)
{{ detail.summary.containerNo || '柜号待核实' }} · {{ detail.summary.overseasWarehouseName || '接仓仓库待核实' }}
{{ detail.summary.blockingReason }}
| 操作 | 入库单号 | 入仓码 / 订单号 |
|---|
| 查看原接仓记录 | {{ row.planNo || '尚未生成有效计划' }} | {{ row.inboundCode || row.orderNo || '—' }} |
本柜尚无可读取的入仓来源。
核对本柜入库计划预览
-
核对本柜计划与托盘
{{ 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)
提交通过驳回调整作废—
{{ row.customerName || '—' }}
{{ feeItemLabel(row.feeItemCode) }}
+ {{ reviewLabels[row.reviewStatus] || row.reviewStatus }}
{{ row.direction === 'RECEIVABLE' ? '应收' : '应付' }}
{{ row.supplierName || '—' }}
¥{{ row.amount.toFixed(2) }}
{{ voucherNames[fileId] || `付款凭证 ${index + 1}` }}
—
- {{ reviewLabels[row.reviewStatus] || row.reviewStatus }}
{{ voidLabels[row.voidStatus] || '—' }}
{{ row.reviewReason || '—' }}
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)
- 箱规按权重系数计算勾选后客户下单数据才会按该规则调整;未勾选时默认使用实测原尺寸(不调整)。
设为默认规则
+ 箱规按权重系数计算勾选后才默认按该规则调整;两个默认规则均未勾选时使用实测原尺寸,仓库修正尺寸替换对应实测边。
系数分别作用于长、宽、高,加权体积 = 原体积 × 系数³。
设为默认规则
{{ row.dimension ? formatDimension(row.dimension.innerLengthCm, row.dimension.innerWidthCm, row.dimension.innerHeightCm) : '—' }}
{{ row.coefficient }}
{{ row.dimension ? Number(row.dimension.volumeCbm).toFixed(3) : '—' }}
- {{ row.dimension ? (Number(row.dimension.volumeCbm) * row.coefficient).toFixed(3) : '—' }}
+ {{ row.dimension ? (Number(row.dimension.volumeCbm) * row.coefficient ** 3).toFixed(3) : '—' }}
{{ row.weight ? row.weight.toFixed(2) : '—' }}
- {{ row.dimension ? (Number(row.dimension.volumeCbm) * row.coefficient * 167).toFixed(2) : '—' }}
+ {{ row.dimension ? (Number(row.dimension.volumeCbm) * row.coefficient ** 3 * 167).toFixed(2) : '—' }}
{{ row.remark || '—' }}
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)
- 退款—共 {{ ledgerTotal }} 条{{ displayLedgerId(row.businessNo || row.ledgerId) }}{{ row.occurredDate || formatDateTime(row.createdAt || row.occurredAt) }}{{ walletBusinessType(row.changeType || row.businessType) }}原消费:{{ displayDocumentNo(row.referenceNo || row.sourceNo || row.documentNo) }}{{ money(row.amount) }}{{ row.paymentMethod ? paymentMethodLabel(row.paymentMethod) : '—' }}{{ money(row.actualBalanceAfter) }}{{ money(row.frozenBalanceAfter) }}{{ money(row.availableBalanceAfter) }}{{ money(row.creditUsedAfter) }}{{ displayOperator(row.operatorName) }}
+ 退款—
共 {{ ledgerTotal }} 条{{ displayLedgerId(row.businessNo || row.ledgerId) }}{{ row.occurredDate || formatDateTime(row.createdAt || row.occurredAt) }}{{ walletBusinessType(row.changeType || row.businessType) }}原消费:{{ displayDocumentNo(row.referenceNo || row.sourceNo || row.documentNo) }}{{ money(row.amount) }}{{ row.paymentMethod ? paymentMethodLabel(row.paymentMethod) : '—' }}{{ money(row.actualBalanceAfter) }}{{ money(row.frozenBalanceAfter) }}{{ money(row.availableBalanceAfter) }}{{ money(row.creditUsedAfter) }}{{ displayOperator(row.operatorName) }}
@@ -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()})
收件人投递明细
- {{notificationAppLabel(row.appCode)}}
{{notificationRecipientStatusLabel(row.status)}}
+ {{notificationAppLabel(row.appCode)}}
{{formatDate(row.deliveredAt)}}
{{row.failureReason||'—'}}
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() {
-
+
@@ -144,7 +144,7 @@ async function save() {
.fee-editor-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 20px; }
.fee-editor-grid :deep(.el-select), .fee-editor-grid :deep(.el-date-editor), .fee-editor-grid :deep(.el-input-number) { width: 100%; }
.fee-remark { grid-column: 1 / -1; }
-.fee-existing { color: var(--cc-text-secondary, #606266); font-size: 13px; margin-bottom: 12px; }
+.fee-existing { color: var(--cc-text-secondary, #606266); font-size: var(--admin-font-body); margin-bottom: var(--admin-space-5); }
.fee-existing p { margin: 4px 0; }
.fee-existing-error { color: var(--cc-danger, #f56c6c); }
diff --git a/src/modules/overseas-business/components/ProcurementWorkflowPanel.vue b/src/modules/overseas-business/components/ProcurementWorkflowPanel.vue
index 9a3d41ed..49bc293b 100644
--- a/src/modules/overseas-business/components/ProcurementWorkflowPanel.vue
+++ b/src/modules/overseas-business/components/ProcurementWorkflowPanel.vue
@@ -93,8 +93,8 @@ async function save() {
const receiptColumns: DataTableColumn[] = [{ prop: 'quantity', label: '收货数量', width: 140 }, { key: 'receivedAt', label: '收货时间', minWidth: 170, formatter: (row: ProcurementWorkflowReceipt) => dateTime(row.receivedAt) }, { key: 'files', label: '照片', minWidth: 180, slotName: 'files' }]
const outboundColumns: DataTableColumn[] = [
{ key: 'actions', label: '操作', width: 180, slotName: 'actions', fixed: 'left' }, { prop: 'outboundNo', label: '出库单号', minWidth: 190 },
+ { key: 'status', label: '状态', width: 120, formatter: (row: ProcurementOutbound) => statusLabel(row.status), afterColumn: 'outboundNo' },
{ prop: 'quantity', label: '出库数量', width: 110 }, { prop: 'trackingNo', label: '物流跟踪号', minWidth: 190 },
- { key: 'status', label: '状态', width: 120, formatter: (row: ProcurementOutbound) => statusLabel(row.status) },
{ key: 'createdAt', label: '创建时间', minWidth: 170, formatter: (row: ProcurementOutbound) => dateTime(row.createdAt) },
{ key: 'completedAt', label: '签收时间', minWidth: 170, formatter: (row: ProcurementOutbound) => dateTime(row.completedAt) },
{ key: 'files', label: '签收凭证', minWidth: 170, slotName: 'files' },
@@ -113,8 +113,8 @@ const fileName = (id: string) => props.detail.files.find(file => file.fileId ===
- 累计收货
- {{ workflow.receivedQuantity }}
- 采购库存
- {{ workflow.stockQuantity }}
- 出库处理中
- {{ workflow.reservedQuantity }}
- 可出库
- {{ workflow.availableQuantity }}
收货记录
{{ fileName(id) }}
出库单
开始派送上传签收凭证{{ fileName(id) }}
-
-
+
+
通过拒绝
diff --git a/src/modules/overseas-business/overseasBusinessDensity.css b/src/modules/overseas-business/overseasBusinessDensity.css
index 1fd43460..4f0cf233 100644
--- a/src/modules/overseas-business/overseasBusinessDensity.css
+++ b/src/modules/overseas-business/overseasBusinessDensity.css
@@ -1,6 +1,5 @@
/* Shared business form layout for purchase and local transport editors. */
.admin-density.overseas-business-editor { width: 100%; }
-.admin-density.overseas-business-editor:not(.overseas-transport-editor) { max-width: 1120px; }
.overseas-business-editor .overseas-business-form-layout { gap: 0; }
.overseas-business-editor .form-reference { min-width: 0; color: var(--cc-muted); font-size: var(--admin-font-body); overflow-wrap: anywhere; }
.overseas-business-editor .form-panel { min-width: 0; }
diff --git a/src/modules/overseas-business/pages/OverseasBusinessViews.test.ts b/src/modules/overseas-business/pages/OverseasBusinessViews.test.ts
index b750046d..8284a1f7 100644
--- a/src/modules/overseas-business/pages/OverseasBusinessViews.test.ts
+++ b/src/modules/overseas-business/pages/OverseasBusinessViews.test.ts
@@ -146,7 +146,9 @@ describe('overseas business mounted pages', () => {
it('shows unknown pool vehicle type as a dash without inventing a capacity', async () => {
vi.mocked(listUsTruckOrders).mockResolvedValue({ records: [{ ...truck, vehicleType: null, fleetArrangement: arrangement }], total: 1, page: 1, size: 20, pages: 1 })
const wrapper = mountPage(TransportOrdersView, { domain: 'truck' }); await flushPromises()
- expect(wrapper.find('.el-table__body-wrapper tbody .el-table__row').findAll('td')[2]!.text()).toBe('—')
+ const vehicleColumn = wrapper.findAll('.el-table__header-wrapper th').findIndex(header => header.text() === '车辆类型')
+ expect(vehicleColumn).toBeGreaterThan(0)
+ expect(wrapper.find('.el-table__body-wrapper tbody .el-table__row').findAll('td')[vehicleColumn]!.text()).toBe('—')
expect(wrapper.find('.el-table__body-wrapper').text()).not.toContain('40HQ')
})
it('allows standalone demand editing without inventing pallet service or transport arrangements', async () => {
@@ -214,10 +216,14 @@ describe('overseas business mounted pages', () => {
await flushPromises()
const cells = wrapper.findAll('.el-table__body-wrapper tbody .el-table__row')[0]!.findAll('td')
expect(cells[0]!.classes()).toContain('el-table-fixed-column--left')
- for (const [index, value] of [record.orderNo, record.customerName, record.purchaseWarehouseName, record.purchaseCategory, record.productName, record.productSpecification].entries()) {
- expect(cells[index + 1]!.text()).toBe(value)
- expect(cells[index + 1]!.classes()).not.toContain('data-table__single-line-cell')
- expect(cells[index + 1]!.find('.cell').classes()).not.toContain('el-tooltip')
+ const headers = wrapper.findAll('.el-table__header-wrapper th').map(header => header.text())
+ expect(headers.slice(0, 3)).toEqual(['操作', '订单编号', '状态'])
+ for (const [label, value] of [['订单编号', record.orderNo], ['客户名称', record.customerName], ['采购仓库', record.purchaseWarehouseName], ['采购品类', record.purchaseCategory], ['商品名称', record.productName], ['商品规格', record.productSpecification]]) {
+ const index = headers.indexOf(label!)
+ expect(index).toBeGreaterThan(0)
+ expect(cells[index]!.text()).toBe(value)
+ expect(cells[index]!.classes()).not.toContain('data-table__single-line-cell')
+ expect(cells[index]!.find('.cell').classes()).not.toContain('el-tooltip')
}
})
it('refreshes the cached procurement list after returning from detail or edit', () => { expect(purchaseListSource).toContain('onActivated'); expect(purchaseListSource).toMatch(/onActivated\(\(\) => \{ if \(activatedOnce\) void load\(\)/) })
diff --git a/src/modules/overseas-business/pages/OverseasFeesView.vue b/src/modules/overseas-business/pages/OverseasFeesView.vue
index 1a4dab98..e593890c 100644
--- a/src/modules/overseas-business/pages/OverseasFeesView.vue
+++ b/src/modules/overseas-business/pages/OverseasFeesView.vue
@@ -53,13 +53,13 @@ const fields = computed(() => [
const columns: DataTableColumn[] = [
{ key: 'actions', label: '操作', width: 210, fixed: 'left', slotName: 'actions' },
{ prop: 'customerName', label: '客户', minWidth: 150 }, { prop: 'businessNo', label: '业务单号', minWidth: 185 },
+ { prop: 'reviewStatus', label: '审核状态', width: 100, slotName: 'reviewStatus', afterColumn: 'businessNo' },
+ { key: 'chargeStatus', label: '费用状态', minWidth: 115, formatter: feeProcessingLabel, afterColumn: 'businessNo' },
{ prop: 'businessType', label: '业务类型', width: 115, formatter: feeBusinessLabel },
{ prop: 'feeItemName', label: '费用项目', minWidth: 145 },
{ prop: 'supplierName', label: '供应商', minWidth: 150, defaultVisible: false },
{ key: 'amount', label: '结算金额(CNY)', width: 145, align: 'right', formatter: (row: OverseasFee) => feeMoney(row.amount) },
{ prop: 'collectionMode', label: '结算方式', width: 115, formatter: (row: OverseasFee) => row.direction === 'PAYABLE' ? '推送结算' : row.collectionMode === 'BILL' ? '账单结算' : '余额/授信' },
- { prop: 'reviewStatus', label: '审核状态', width: 100, slotName: 'reviewStatus' },
- { key: 'chargeStatus', label: '费用状态', minWidth: 115, formatter: feeProcessingLabel },
{ prop: 'businessDate', label: '费用日期', width: 110 },
{ prop: 'createdAt', label: '创建时间', minWidth: 165, defaultVisible: false, formatter: (row: OverseasFee) => feeTime(row.createdAt) },
]
diff --git a/src/modules/overseas-business/pages/PurchaseOrdersView.vue b/src/modules/overseas-business/pages/PurchaseOrdersView.vue
index b5b98749..b7f676d5 100644
--- a/src/modules/overseas-business/pages/PurchaseOrdersView.vue
+++ b/src/modules/overseas-business/pages/PurchaseOrdersView.vue
@@ -33,6 +33,7 @@ const fields = computed(() => [
const columns: DataTableColumn[] = [
{ key: 'actions', label: '操作', width: 120, fixed: 'left', slotName: 'actions' },
{ key: 'orderNo', prop: 'orderNo', label: '订单编号', minWidth: 220, singleLine: false },
+ { key: 'status', label: '状态', minWidth: 145, slotName: 'status', afterColumn: 'orderNo' },
{ key: 'customerName', prop: 'customerName', label: '客户名称', minWidth: 220, singleLine: false, formatter: (row: OverseasPurchaseSummary) => row.customerName || '—' },
{ key: 'purchaseWarehouseName', prop: 'purchaseWarehouseName', label: '采购仓库', minWidth: 160, singleLine: false, formatter: (row: OverseasPurchaseSummary) => row.purchaseWarehouseName || '—' },
{ key: 'purchaseCategory', prop: 'purchaseCategory', label: '采购品类', minWidth: 130, singleLine: false },
@@ -40,7 +41,6 @@ const columns: DataTableColumn[] = [
{ key: 'productSpecification', prop: 'productSpecification', label: '商品规格', minWidth: 180, singleLine: false, formatter: (row: OverseasPurchaseSummary) => row.productSpecification || '—' },
{ key: 'budgetUnitPrice', label: '预算单价', width: 120, align: 'right', formatter: (row: OverseasPurchaseSummary) => money(row.budgetUnitPrice, row.currency) },
{ key: 'purchaseQuantity', prop: 'purchaseQuantity', label: '采购数量', width: 100, align: 'right' },
- { key: 'status', label: '状态', minWidth: 145, slotName: 'status' },
{ key: 'createdAt', prop: 'createdAt', label: '创建时间', minWidth: 165, formatter: (row: OverseasPurchaseSummary) => formatDateTime(row.createdAt) },
{ key: 'procurementDirection', label: '采购方向', minWidth: 170, formatter: (row: OverseasPurchaseSummary) => directionLabel(row.procurementDirection) },
]
diff --git a/src/modules/overseas-business/pages/TransportOrdersView.vue b/src/modules/overseas-business/pages/TransportOrdersView.vue
index 7fc37d51..4213725d 100644
--- a/src/modules/overseas-business/pages/TransportOrdersView.vue
+++ b/src/modules/overseas-business/pages/TransportOrdersView.vue
@@ -55,7 +55,7 @@ const fields = computed(() => [
{ key: 'routeKeyword', type: 'input', label: '运输路线', placeholder: '起运地/目的地', width: 240 },
])
const truckColumns: DataTableColumn[] = [
- { key: 'actions', label: '操作', width: 220, fixed: 'left', slotName: 'actions' }, { key: 'orderNo', prop: 'orderNo', label: '订单编号', minWidth: 170 },
+ { key: 'actions', label: '操作', width: 220, fixed: 'left', slotName: 'actions' }, { key: 'orderNo', prop: 'orderNo', label: '订单编号', minWidth: 170 }, { key: 'status', label: '状态', width: 120, slotName: 'status', afterColumn: 'orderNo' },
{ key: 'vehicleType', label: '车辆类型', minWidth: 110, formatter: (row: LocalTransportOrder) => row.vehicleType ? ({ PALLET_15: '15拖', PALLET_30: '30拖' })[row.vehicleType] : '—' },
{ key: 'origin', prop: 'origin', label: '起运地', minWidth: 145 }, { key: 'destination', prop: 'destination', label: '目的地', minWidth: 145 },
{ key: 'originContactName', prop: 'originContactName', label: '起运地联系人', minWidth: 125 }, { key: 'originContactPhone', prop: 'originContactPhone', label: '起运地联系电话', minWidth: 155 },
@@ -63,16 +63,16 @@ const truckColumns: DataTableColumn[] = [
{ key: 'packageCount', prop: 'packageCount', label: '件数', width: 80, align: 'right' }, { key: 'totalWeightKg', prop: 'totalWeightKg', label: '总重量(kg)', width: 115, align: 'right' },
{ key: 'budgetAmount', label: '预算总价', width: 120, align: 'right', formatter: (row: LocalTransportOrder) => row.budgetAmount == null ? '—' : `${row.budgetAmount} ${row.currency}` },
{ key: 'pickupAt', label: '取货时间', width: 165, formatter: (row: LocalTransportOrder) => formatDateTime(row.pickupAt) },
- { key: 'deliveryAt', label: '送货时间', width: 165, formatter: (row: LocalTransportOrder) => formatDateTime(row.deliveryAt) }, { key: 'status', label: '状态', width: 120, slotName: 'status' },
+ { key: 'deliveryAt', label: '送货时间', width: 165, formatter: (row: LocalTransportOrder) => formatDateTime(row.deliveryAt) },
{ key: 'createdAt', prop: 'createdAt', label: '创建时间', minWidth: 165, formatter: (row: LocalTransportOrder) => formatDateTime(row.createdAt) },
]
const looseboardColumns: DataTableColumn[] = [
- { key: 'actions', label: '操作', width: 180, fixed: 'left', slotName: 'actions' }, { key: 'orderNo', prop: 'orderNo', label: '订单编号', minWidth: 170 },
+ { key: 'actions', label: '操作', width: 180, fixed: 'left', slotName: 'actions' }, { key: 'orderNo', prop: 'orderNo', label: '订单编号', minWidth: 170 }, { key: 'status', label: '状态', width: 105, slotName: 'status', afterColumn: 'orderNo' },
{ key: 'transportType', label: '运输类型', minWidth: 120, formatter: (row: LocalLooseboardOrder) => row.transportType ? transportTypeLabels[row.transportType] : '—' }, { key: 'origin', prop: 'origin', label: '起运地', minWidth: 145 },
{ key: 'destination', prop: 'destination', label: '目的地', minWidth: 145 }, { key: 'originContactName', prop: 'originContactName', label: '起运地联系人', minWidth: 125 }, { key: 'originContactPhone', prop: 'originContactPhone', label: '起运地联系电话', minWidth: 155 },
{ key: 'destinationContactName', prop: 'destinationContactName', label: '目的地联系人', minWidth: 125 }, { key: 'destinationContactPhone', prop: 'destinationContactPhone', label: '目的地联系电话', minWidth: 155 }, { key: 'packageCount', prop: 'packageCount', label: '件数', width: 80, align: 'right' },
{ key: 'totalWeightKg', prop: 'totalWeightKg', label: '总重量(kg)', width: 115, align: 'right' }, { key: 'budgetAmount', label: '预算总价', width: 115, align: 'right', formatter: (row: LocalLooseboardOrder) => row.budgetAmount == null ? '—' : `${row.budgetAmount} ${row.currency}` },
- { key: 'productName', prop: 'productName', label: '商品名称', minWidth: 150 }, { key: 'estimatedPalletCount', prop: 'estimatedPalletCount', label: '预计托数', width: 100 }, { key: 'pickupAt', label: '取货时间', width: 165, formatter: (row: LocalLooseboardOrder) => formatDateTime(row.pickupAt) }, { key: 'deliveryAt', label: '送货时间', width: 165, formatter: (row: LocalLooseboardOrder) => formatDateTime(row.deliveryAt) }, { key: 'status', label: '状态', width: 105, slotName: 'status' },
+ { key: 'productName', prop: 'productName', label: '商品名称', minWidth: 150 }, { key: 'estimatedPalletCount', prop: 'estimatedPalletCount', label: '预计托数', width: 100 }, { key: 'pickupAt', label: '取货时间', width: 165, formatter: (row: LocalLooseboardOrder) => formatDateTime(row.pickupAt) }, { key: 'deliveryAt', label: '送货时间', width: 165, formatter: (row: LocalLooseboardOrder) => formatDateTime(row.deliveryAt) },
{ key: 'createdAt', prop: 'createdAt', label: '创建时间', minWidth: 165, formatter: (row: LocalLooseboardOrder) => formatDateTime(row.createdAt) },
]
const columns = computed(() => {
diff --git a/src/modules/overseas-business/pages/TruckOrderDetailView.vue b/src/modules/overseas-business/pages/TruckOrderDetailView.vue
index a2e5e0be..dee7f55b 100644
--- a/src/modules/overseas-business/pages/TruckOrderDetailView.vue
+++ b/src/modules/overseas-business/pages/TruckOrderDetailView.vue
@@ -120,23 +120,23 @@ watch([orderNo, customerId], load, { immediate: true })
.truck-detail-actions { flex-wrap: wrap; }
.truck-detail-section { margin-bottom: var(--admin-space-6); }
.truck-section-heading { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin-bottom: 14px; }
-.truck-section-heading h2 { margin: 0; font-size: 16px; color: var(--cc-text); }
-.truck-flow-caption, .truck-detail-note { color: var(--cc-muted); font-size: 12px; }
+.truck-section-heading h2 { margin: 0; font-size: var(--admin-font-section); color: var(--cc-text); }
+.truck-flow-caption, .truck-detail-note { color: var(--cc-muted); font-size: var(--admin-font-caption); }
.truck-detail-note { margin: 10px 0 0; }
.truck-flow { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 0; margin: 0; padding: 0; list-style: none; }
.truck-flow li { position: relative; display: flex; flex-direction: column; align-items: center; gap: 8px; min-width: 0; color: var(--cc-muted); text-align: center; }
.truck-flow li::before { content: ''; position: absolute; top: 15px; left: -50%; width: 100%; height: 1px; background: var(--cc-border); }
.truck-flow li:first-child::before { display: none; }
.truck-flow li.done::before, .truck-flow li.active::before { background: var(--el-color-primary); }
-.truck-flow-marker { position: relative; z-index: 1; display: grid; place-items: center; width: 30px; height: 30px; border: 1px solid var(--cc-border); border-radius: 50%; background: var(--cc-surface); color: var(--cc-muted); font-size: 12px; }
+.truck-flow-marker { position: relative; z-index: 1; display: grid; place-items: center; width: 30px; height: 30px; border: 1px solid var(--cc-border); border-radius: 50%; background: var(--cc-surface); color: var(--cc-muted); font-size: var(--admin-font-caption); }
.truck-flow li.done .truck-flow-marker, .truck-flow li.active .truck-flow-marker { border-color: var(--el-color-primary); color: var(--el-color-primary); }
.truck-flow li.active .truck-flow-marker { background: var(--el-color-primary); color: #fff; box-shadow: 0 0 0 4px color-mix(in srgb, var(--el-color-primary) 14%, transparent); }
-.truck-flow-label { font-size: 12px; white-space: nowrap; }
+.truck-flow-label { font-size: var(--admin-font-caption); white-space: nowrap; }
.truck-flow li.active .truck-flow-label { color: var(--el-color-primary); font-weight: 600; }
.truck-detail-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 1px; margin: 0; border: 1px solid var(--cc-border); border-radius: var(--cc-radius-lg); overflow: hidden; background: var(--cc-border); }
.truck-detail-fact { min-width: 0; display: grid; grid-template-columns: 90px minmax(0, 1fr); gap: 10px; padding: 11px 14px; background: var(--cc-surface); }
-.truck-detail-fact dt { color: var(--cc-muted); font-size: 12px; }
-.truck-detail-fact dd { min-width: 0; margin: 0; color: var(--cc-text); overflow-wrap: anywhere; font-size: 13px; }
+.truck-detail-fact dt { color: var(--cc-muted); font-size: var(--admin-font-caption); }
+.truck-detail-fact dd { min-width: 0; margin: 0; color: var(--cc-text); overflow-wrap: anywhere; font-size: var(--admin-font-body); }
.truck-detail-fact:last-child { grid-column: span 2; }
@media (max-width: 1100px) { .truck-detail-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 760px) { .truck-flow { overflow-x: auto; grid-template-columns: repeat(5, 100px); padding-bottom: 4px; } .truck-detail-grid { grid-template-columns: 1fr; } .truck-detail-fact:last-child { grid-column: auto; } }
diff --git a/src/modules/overseas-master-data/components/CapacityInquiryTable.vue b/src/modules/overseas-master-data/components/CapacityInquiryTable.vue
index d5fac877..564b57ba 100644
--- a/src/modules/overseas-master-data/components/CapacityInquiryTable.vue
+++ b/src/modules/overseas-master-data/components/CapacityInquiryTable.vue
@@ -25,9 +25,9 @@ function actions(row: CapacityInquiry, busy: boolean): RowActionItem[] {
}
const columns: DataTableColumn[] = [
{ key: 'actions', label: '操作', width: 320, fixed: 'left', slotName: 'actions' },
- { key: 'bookingNo', label: '意向 / 预订号', minWidth: 210, singleLine: false }, { key: 'customerName', label: '客户', minWidth: 210, singleLine: false },
- { key: 'tripNo', label: '车次号', minWidth: 210 }, { key: 'destinationLabel', label: '卸货站', minWidth: 130 },
- { key: 'status', label: '当前进度 / 下一步', minWidth: 270, slotName: 'status', singleLine: false }, { key: 'cargo', label: '货量', minWidth: 270, slotName: 'cargo', singleLine: false },
+ { key: 'bookingNo', label: '意向 / 预订号', minWidth: 210, singleLine: false },
+ { key: 'status', label: '当前进度 / 下一步', minWidth: 270, slotName: 'status', singleLine: false, afterColumn: 'bookingNo' }, { key: 'customerName', label: '客户', minWidth: 210, singleLine: false },
+ { key: 'tripNo', label: '车次号', minWidth: 210 }, { key: 'destinationLabel', label: '卸货站', minWidth: 130 }, { key: 'cargo', label: '货量', minWidth: 270, slotName: 'cargo', singleLine: false },
{ key: 'agreedAmount', label: '约定运费(非实收)', minWidth: 165, slotName: 'amount' }, { key: 'businessOrderNo', label: '本土订单号', minWidth: 210 },
{ key: 'updatedAt', label: '更新时间', minWidth: 180, slotName: 'time' },
]
diff --git a/src/modules/overseas-master-data/components/TripBookingPanel.vue b/src/modules/overseas-master-data/components/TripBookingPanel.vue
index a5cfa79c..cf294814 100644
--- a/src/modules/overseas-master-data/components/TripBookingPanel.vue
+++ b/src/modules/overseas-master-data/components/TripBookingPanel.vue
@@ -67,9 +67,9 @@ const quotedAmount = computed(() => form.unitPrice == null ? null : form.priceBa
const columns = computed(() => [
{ key: 'actions', label: '操作', slotName: 'actions', width: planned.value ? 320 : 170, fixed: 'left' },
{ key: 'bookingNo', prop: 'bookingNo', label: '外接预订号', minWidth: 225 },
+ { key: 'status', label: '状态', slotName: 'status', width: 130, afterColumn: 'bookingNo' },
{ key: 'customerName', prop: 'customerName', label: '委托客户', minWidth: 170 },
{ key: 'destinationLabel', prop: 'destinationLabel', label: '卸货站', minWidth: 140 },
- { key: 'status', label: '状态', slotName: 'status', width: 130 },
{ key: 'cargo', label: '承运货量', slotName: 'cargo', minWidth: 225 },
{ key: 'amount', label: '约定运费', slotName: 'amount', minWidth: 160 },
{ key: 'documents', label: '来货 / 排车', slotName: 'documents', minWidth: 230 },
diff --git a/src/modules/overseas-master-data/components/TripPickupPanel.vue b/src/modules/overseas-master-data/components/TripPickupPanel.vue
index d9da89d8..79c89f05 100644
--- a/src/modules/overseas-master-data/components/TripPickupPanel.vue
+++ b/src/modules/overseas-master-data/components/TripPickupPanel.vue
@@ -25,7 +25,7 @@ const canComplete = computed(() => rows.value.length > 0 && rows.value.every(row
const columns: DataTableColumn[] = [
{ key: 'actions', label: '操作', width: 175, fixed: 'left', slotName: 'actions' },
{ key: 'transferNo', prop: 'transferNo', label: '仓储转运单', minWidth: 220 },
- { key: 'status', label: '取货进度', slotName: 'status', width: 110 }, { key: 'pickupAddress', prop: 'pickupAddress', label: '取货地址', minWidth: 270 },
+ { key: 'status', label: '取货进度', slotName: 'status', width: 110, afterColumn: 'transferNo' }, { key: 'pickupAddress', prop: 'pickupAddress', label: '取货地址', minWidth: 270 },
{ key: 'contactName', prop: 'contactName', label: '联系人', width: 110 }, { key: 'contactPhone', prop: 'contactPhone', label: '电话', minWidth: 145 },
{ key: 'packageCount', prop: 'packageCount', label: '预报箱数', width: 100 }, { key: 'actualPackages', label: '实提箱数', slotName: 'actual', width: 100 },
{ key: 'evidence', label: '取货凭证', slotName: 'evidence', minWidth: 180 }, { key: 'remark', prop: 'remark', label: '取货备注', minWidth: 180 },
diff --git a/src/modules/overseas-master-data/components/VehicleCareTable.vue b/src/modules/overseas-master-data/components/VehicleCareTable.vue
index a5d847b4..918464c1 100644
--- a/src/modules/overseas-master-data/components/VehicleCareTable.vue
+++ b/src/modules/overseas-master-data/components/VehicleCareTable.vue
@@ -48,6 +48,8 @@ const columns = computed(() => {
const result: DataTableColumn[] = [{ key: 'actions', label: '操作', width: costRecord.value ? 170 : 100, fixed: 'left', slotName: 'actions' }]
if (props.recordType === 'EXCEPTION') result.push({ key: 'exceptionType', label: '异常类型', minWidth: 135, slotName: 'exceptionType' })
result.push({ key: 'date', label: '发生时间', minWidth: 165, slotName: 'date' })
+ result.push({ key: 'status', label: meterRecord.value ? '核验状态' : '处理状态', width: 105, slotName: 'status', afterColumn: 'date' })
+ if (costRecord.value) result.push({ key: 'payment', label: '付款状态', minWidth: 130, slotName: 'payment', afterColumn: 'date' })
if (meterRecord.value) result.push(...(props.recordType === 'MAINTENANCE' ? [
{ key: 'last', label: '上次保养', minWidth: 120, slotName: 'last' },
{ key: 'current', label: '记录时里程', minWidth: 120, slotName: 'current' },
@@ -61,9 +63,8 @@ const columns = computed(() => {
const place = { key: 'place', prop: 'details.incident.place', label: props.recordType === 'REPAIR' ? '维修地点' : '救援地点', minWidth: 190 }
result.push(...(props.recordType === 'REPAIR' ? [project, place] : [place, project]), { key: 'cost', label: '费用(申报金额)', minWidth: 160, slotName: 'cost' })
} else result.push({ key: 'description', prop: 'details.incident.description', label: '描述', minWidth: 260 })
- result.push({ key: 'status', label: meterRecord.value ? '核验状态' : '处理状态', width: 105, slotName: 'status' })
if (!meterRecord.value) result.push({ key: 'handler', prop: 'details.incident.handler', label: '处理人', minWidth: 120 })
- if (['REPAIR', 'RESCUE'].includes(props.recordType)) result.push({ key: 'payment', label: '付款状态', minWidth: 130, slotName: 'payment' }, { key: 'description', prop: 'details.incident.description', label: '描述', minWidth: 260 })
+ if (['REPAIR', 'RESCUE'].includes(props.recordType)) result.push({ key: 'description', prop: 'details.incident.description', label: '描述', minWidth: 260 })
result.push({ key: 'remark', prop: 'details.remark', label: '备注', minWidth: 150 })
return result
})
diff --git a/src/modules/overseas-master-data/components/VehicleFulfillmentTable.vue b/src/modules/overseas-master-data/components/VehicleFulfillmentTable.vue
index 41af5845..480aec4c 100644
--- a/src/modules/overseas-master-data/components/VehicleFulfillmentTable.vue
+++ b/src/modules/overseas-master-data/components/VehicleFulfillmentTable.vue
@@ -19,11 +19,11 @@ const title = computed(() => props.kind === 'deliveries' ? '派送订单明细'
let generation = 0
const columns = computed(() => props.kind === 'deliveries' ? [
{ key: 'deliveryNo', prop: 'deliveryNo', label: '派送单号', minWidth: 210 },
+ { key: 'status', label: '派送状态', width: 115, slotName: 'status', afterColumn: 'deliveryNo' },
{ key: 'source', label: 'BOL / 来源排车', minWidth: 215, slotName: 'source' },
{ key: 'customerName', label: '客户', minWidth: 160, slotName: 'customer' },
{ key: 'destination', label: '目的仓 / 地址', minWidth: 180, slotName: 'destination' },
{ key: 'plannedDeliveryAt', label: '预约日期', minWidth: 165, slotName: 'date' },
- { key: 'status', label: '派送状态', width: 115, slotName: 'status' },
{ key: 'relationship', label: '关联关系', minWidth: 220, slotName: 'relationship' },
] : [
{ key: 'occurredAt', label: '时间', minWidth: 165, slotName: 'date' },
diff --git a/src/modules/overseas-master-data/pages/CapacityOffersView.vue b/src/modules/overseas-master-data/pages/CapacityOffersView.vue
index fffd3425..d509ae40 100644
--- a/src/modules/overseas-master-data/pages/CapacityOffersView.vue
+++ b/src/modules/overseas-master-data/pages/CapacityOffersView.vue
@@ -66,10 +66,10 @@ const fields = computed(() => tab.value === 'offers' ? [
])
const columns = computed(() => [
{ key: 'actions', label: '操作', width: 200, fixed: 'left', slotName: 'actions' }, { key: 'offerNo', label: '发布号 / 车次号', minWidth: 220, slotName: 'identity' },
+ { key: 'status', label: '发布状态', minWidth: 170, slotName: 'status', singleLine: false, afterColumn: 'offerNo' },
{ key: 'warehouseName', label: '起运仓', minWidth: 155 }, { key: 'destinations', label: '目的站与可接余位', minWidth: 310, slotName: 'destinations', singleLine: false },
{ key: 'plannedDepartureAt', label: '发车 / 最晚送仓', minWidth: 195, slotName: 'times' },
- ...(manage.value ? [{ key: 'segments', label: '分段计划占用 / 外接预留', minWidth: 320, slotName: 'loads', singleLine: false }] : []),
- { key: 'status', label: '发布状态', minWidth: 170, slotName: 'status', singleLine: false }, { key: 'contactName', label: '调度联系信息', minWidth: 180, slotName: 'contact' },
+ ...(manage.value ? [{ key: 'segments', label: '分段计划占用 / 外接预留', minWidth: 320, slotName: 'loads', singleLine: false }] : []), { key: 'contactName', label: '调度联系信息', minWidth: 180, slotName: 'contact' },
])
const canCreate = computed(() => manage.value || access.can('platform:fleet:capacity-offer:list') && access.can('platform:fleet:capacity-offer:inquiry-create'))
async function create(row: CapacityOffer) {
diff --git a/src/modules/overseas-master-data/pages/DispatchCapacityDemandsView.vue b/src/modules/overseas-master-data/pages/DispatchCapacityDemandsView.vue
index 67113672..680a65f1 100644
--- a/src/modules/overseas-master-data/pages/DispatchCapacityDemandsView.vue
+++ b/src/modules/overseas-master-data/pages/DispatchCapacityDemandsView.vue
@@ -16,7 +16,8 @@ const filters = ref({ origin: '', destination: '' }), applied = ref(
const fields: QueryField[] = [{ key: 'origin', label: '起运仓', type: 'input', placeholder: '起运仓名称', width: 190 }, { key: 'destination', label: '目的地', type: 'input', placeholder: '目的地名称', width: 190 }]
const amount = (value: number | null | undefined) => value == null ? '—' : String(value)
const columns: DataTableColumn[] = [
- { key: 'dispatchNo', label: '排车单号', minWidth: 200 }, { key: 'warehouseName', label: '起运仓', minWidth: 160 },
+ { key: 'dispatchNo', label: '排车单号', minWidth: 200 },
+ { key: 'capacityReason', label: '余位状态', minWidth: 220, formatter: (row: DispatchCapacityDemand) => row.capacityKnown ? '余位已确认' : row.capacityReason || '余位待确认', afterColumn: 'dispatchNo' }, { key: 'warehouseName', label: '起运仓', minWidth: 160 },
{ key: 'route', label: '路线', minWidth: 240, formatter: (row: DispatchCapacityDemand) => row.route || '—' },
{ key: 'plannedDepartureAt', label: '计划发车', minWidth: 180, formatter: (row: DispatchCapacityDemand) => offerTime(row.plannedDepartureAt) },
{ key: 'vehiclePalletSpaces', label: '车辆容量(托位)', width: 145, align: 'right', formatter: (row: DispatchCapacityDemand) => amount(row.vehiclePalletSpaces) },
@@ -24,7 +25,6 @@ const columns: DataTableColumn[] = [
{ key: 'remainingPalletSpaces', label: '剩余标准托位', width: 140, align: 'right', formatter: (row: DispatchCapacityDemand) => row.capacityKnown ? amount(row.remainingPalletSpaces) : '待确认' },
{ key: 'remainingWeightKg', label: '剩余载重(kg)', width: 145, align: 'right', formatter: (row: DispatchCapacityDemand) => row.capacityKnown ? amount(row.remainingWeightKg) : '待确认' },
{ key: 'remainingVolumeCbm', label: '剩余容积(m³)', width: 145, align: 'right', formatter: (row: DispatchCapacityDemand) => row.capacityKnown ? amount(row.remainingVolumeCbm) : '待确认' },
- { key: 'capacityReason', label: '余位状态', minWidth: 220, formatter: (row: DispatchCapacityDemand) => row.capacityKnown ? '余位已确认' : row.capacityReason || '余位待确认' },
]
let sequence = 0
async function load() {
diff --git a/src/modules/overseas-master-data/pages/DriverDetailView.vue b/src/modules/overseas-master-data/pages/DriverDetailView.vue
index b35f910f..54bd27ef 100644
--- a/src/modules/overseas-master-data/pages/DriverDetailView.vue
+++ b/src/modules/overseas-master-data/pages/DriverDetailView.vue
@@ -185,7 +185,7 @@ onBeforeUnmount(() => { detailActive = false; resetDetailRequests() })