From 04e1d7dbfa7f542864aa89ec27df62a3380a0ce1 Mon Sep 17 00:00:00 2001
From: ouyangxq <775153912@qq.com>
Date: Wed, 7 Oct 2026 12:24:21 +0800
Subject: [PATCH] fix(admin): unify fleet driver trip and operation list
layouts
---
.../overseas-master-data/PROTOTYPE_MATRIX.md | 12 ++++++
.../components/TripOperationsCharts.vue | 9 ++--
.../pages/DriversBehavior.test.ts | 2 +-
.../pages/DriversView.vue | 41 +++++++++++--------
.../pages/FleetReportingViews.test.ts | 2 +-
.../pages/TripOperationsView.vue | 35 ++++++++++------
.../pages/TripsBehavior.test.ts | 2 +-
.../overseas-master-data/pages/TripsView.vue | 13 +++---
src/styles/adminDensity.test.ts | 1 +
9 files changed, 73 insertions(+), 44 deletions(-)
diff --git a/src/modules/overseas-master-data/PROTOTYPE_MATRIX.md b/src/modules/overseas-master-data/PROTOTYPE_MATRIX.md
index 3f16d0be..0ef4c2ff 100644
--- a/src/modules/overseas-master-data/PROTOTYPE_MATRIX.md
+++ b/src/modules/overseas-master-data/PROTOTYPE_MATRIX.md
@@ -1,5 +1,17 @@
# 海外通用资料原型核对矩阵
+## 2026-10-07 三个车辆列表样式统一
+
+依据:本轮用户提供的司机管理、车次管理、出车记录三张截图;对齐已确认的 `VehiclesView.vue` 连续列表及 `docs/Admin高密度界面规范.md`。仅改列表容器与排版,不改变下列操作承载、权限或请求。
+
+| 页面 | 操作与当前承载 | 本轮结论 |
+|---|---|---|
+| 司机管理 | 查询/到期统计筛选;详情独立路由;新增/编辑1080px居中;证照预览620px居中;启停原因确认;审核在详情 | 列表接入integrated标题、筛选、统计、表格、分页;三项统计保留点击筛选,紧凑排列;各操作承载保持 |
+| 车次管理 | 查询;新建1080px计划弹框;详情独立路由;编辑、审核、确认在详情;列表无文件预览/删除 | 接入相同integrated列表,详情操作保留在第一列;弹框与详情保持 |
+| 出车记录 | 日期/货源筛选、图表和汇总;导出;车次详情独立路由;实测记录820px居中及其中凭证预览;列表无删除/审核 | 图表、说明与汇总放入连续列表内,统一间距,空图表不重复显示空系列标题;保留未知金额、成本及统计说明 |
+
+改后视觉结论待用户在1920×1080、100%缩放提供截图确认;自动检查单独记录,不以测试代替视觉通过。
+
事实来源:`docs/悦/货代SAAS平台后台/prototype-src/modules/10-general.html`、`prototype-src/overlays.html`、`prototype-src/app.js`。
| 页面 | 操作 | 原型证据 | 原型承载方式 | 原型尺寸/布局 | 当前实现 | 结论 |
diff --git a/src/modules/overseas-master-data/components/TripOperationsCharts.vue b/src/modules/overseas-master-data/components/TripOperationsCharts.vue
index ce61a1af..4f1d0a58 100644
--- a/src/modules/overseas-master-data/components/TripOperationsCharts.vue
+++ b/src/modules/overseas-master-data/components/TripOperationsCharts.vue
@@ -13,14 +13,15 @@ function width(value: number, series: TripOperationsSeries) { const max = Math.m
{{ section.title }}{{ section.note }}
暂无对应统计
- {{ series.label }}({{ series.unit }})
+
{{ series.label }}({{ series.unit }})
{{ point.label }}{{ point.value }}
-
+
diff --git a/src/modules/overseas-master-data/pages/FleetReportingViews.test.ts b/src/modules/overseas-master-data/pages/FleetReportingViews.test.ts
index 1c6a5e70..b04bbf60 100644
--- a/src/modules/overseas-master-data/pages/FleetReportingViews.test.ts
+++ b/src/modules/overseas-master-data/pages/FleetReportingViews.test.ts
@@ -11,7 +11,7 @@ vi.mock('../api/tripOperations', () => ({ listTripOperations: vi.fn(), exportTri
const wrappers: VueWrapper[] = []
const layout = { template: '
' }
function mount(component: object) { const wrapper = shallowMount(component, { global: { stubs: { AdminPage: layout, ListPageLayout: layout, QueryToolbar: true,
- DataTable: { name: 'DataTable', props: ['data', 'columns'], template: '
' }, ElButton: { template: '' },
+ DataTable: { name: 'DataTable', props: ['data', 'columns'], template: '
' }, ElButton: { template: '' },
LoadErrorState: true, FleetQuoteEditor: true, FleetQuoteImport: true, ApiErrorMeta: true } } }); wrappers.push(wrapper); return wrapper }
afterEach(() => { wrappers.splice(0).forEach(w => w.unmount()); vi.clearAllMocks() })
it('quotes read-only view fetches a page once and hides all write/export tools', async () => {
diff --git a/src/modules/overseas-master-data/pages/TripOperationsView.vue b/src/modules/overseas-master-data/pages/TripOperationsView.vue
index 477b427d..8365c98c 100644
--- a/src/modules/overseas-master-data/pages/TripOperationsView.vue
+++ b/src/modules/overseas-master-data/pages/TripOperationsView.vue
@@ -45,19 +45,24 @@ async function download() { exporting.value = true; try { saveFleetCsv(await exp
const money = (rows: TripOperationsMoney[]) => rows.length ? rows.map(r => `${r.currency} ${r.amount.toFixed(2)}`).join(' / ') : '—'
watch([page, size], load); onMounted(load); onBeforeUnmount(() => { sequence++ })
-
-
-
-
-
- 实际出车 {{ result.summary.trips }} 次在途 {{ result.summary.inTransit }} 次货物结案 {{ result.summary.completed }} 次
- 实物托盘 {{ result.summary.physicalPallets }} 托外接约定 {{ money(result.summary.externalAgreedAmounts) }}
-
- 已登记直接成本:{{ result.summary.costs.totals.length ? result.summary.costs.totals.map(c => `${c.currency} ${c.directCost.toFixed(2)}`).join(' / ') : '—' }};未登记 {{ result.summary.costs.unrecordedTrips }} 趟,已登记但未能分摊 {{ result.summary.costs.unallocatedTrips }} 趟。
- 按实际发车时间查询,每趟车只计一次;货源筛选展示含该类货物的整趟车。约定金额不代表应收或实收,结案不代表车辆与司机已释放。
-
+
+
+ 出车记录
+
+
+
+
+
+
+ 实际出车 {{ result.summary.trips }} 次在途 {{ result.summary.inTransit }} 次货物结案 {{ result.summary.completed }} 次
+ 实物托盘 {{ result.summary.physicalPallets }} 托外接约定 {{ money(result.summary.externalAgreedAmounts) }}
+
+
已登记直接成本:{{ result.summary.costs.totals.length ? result.summary.costs.totals.map(c => `${c.currency} ${c.directCost.toFixed(2)}`).join(' / ') : '—' }};未登记 {{ result.summary.costs.unrecordedTrips }} 趟,已登记但未能分摊 {{ result.summary.costs.unallocatedTrips }} 趟。
+
按实际发车时间查询,每趟车只计一次;货源筛选展示含该类货物的整趟车。约定金额不代表应收或实收,结案不代表车辆与司机已释放。
+
+
导出CSV
- 详情实测记录
+ 详情实测记录
{{ row.status === 'COMPLETED' ? '货物已结案' : '在途' }}{{ row.destinations.map((d: TripOperationsRow['destinations'][number]) => d.label).join(' / ') }}
{{ row.measured?.actualDistanceMiles ?? '—' }}{{ row.measured?.actualFuelLitres ?? '—' }}
{{ money(row.externalAgreedAmounts) }}
@@ -66,4 +71,8 @@ watch([page, size], load); onMounted(load); onBeforeUnmount(() => { sequence++ }
-
+
diff --git a/src/modules/overseas-master-data/pages/TripsBehavior.test.ts b/src/modules/overseas-master-data/pages/TripsBehavior.test.ts
index d6b59f7a..94c0d7d3 100644
--- a/src/modules/overseas-master-data/pages/TripsBehavior.test.ts
+++ b/src/modules/overseas-master-data/pages/TripsBehavior.test.ts
@@ -25,7 +25,7 @@ const detail: TripDetail = { trip, driverQualification: null, dispatches: [], lo
const layout = { template: '
' }
const input = { name: 'ElInput', props: ['modelValue'], emits: ['update:modelValue'], template: '' }
const dialog = { name: 'AppDialog', props: ['modelValue'], emits: ['confirm'], template: '
' }
-const table = { name: 'DataTable', props: ['data', 'columns', 'page', 'pageSize'], emits: ['update:page'], template: '' }
+const table = { name: 'DataTable', props: ['data', 'columns', 'page', 'pageSize'], emits: ['update:page'], template: '' }
const wrappers: VueWrapper[] = []
function mount(component: object, props: Record = {}): VueWrapper {
const wrapper = shallowMount(component, { props, global: { directives: { loading: () => undefined }, stubs: {
diff --git a/src/modules/overseas-master-data/pages/TripsView.vue b/src/modules/overseas-master-data/pages/TripsView.vue
index 6ade0057..49382f0d 100644
--- a/src/modules/overseas-master-data/pages/TripsView.vue
+++ b/src/modules/overseas-master-data/pages/TripsView.vue
@@ -20,7 +20,7 @@ const rows = ref([]), total = ref(0), page = ref(1), size = ref(20)
const error = ref(null), filters = ref({ keyword: '', status: '' })
let applied: Omit = {}, sequence = 0
const fields: QueryField[] = [{ key: 'keyword', type: 'input', label: '关键词', placeholder: '车次号 / 车牌 / 司机', width: 250 },
- { key: 'status', type: 'select', label: '状态', width: 150, options: Object.entries(tripStatusLabels).map(([value, label]) => ({ value, label })) }]
+ { key: 'status', type: 'select', label: '状态', placeholder: '全部状态', width: 150, options: Object.entries(tripStatusLabels).map(([value, label]) => ({ value, label })) }]
const columns: DataTableColumn[] = [
{ key: 'actions', label: '操作', width: 85, fixed: 'left', slotName: 'actions' }, { key: 'tripNo', label: '车次号', minWidth: 215 },
{ key: 'status', label: '状态', slotName: 'status', width: 120 }, { key: 'warehouseName', label: '起运仓', minWidth: 160 },
@@ -44,12 +44,13 @@ function created(detail: TripDetail) { view(detail.trip.tripNo) }
watch([page, size], load); onMounted(load); onBeforeUnmount(() => { sequence++ })
-
-
-
-
+
+
+ 车次管理
+
+
新建车次
- 详情
+ 详情
{{ tripStatusLabels[row.status as keyof typeof tripStatusLabels] }}
{{ time(row.plannedDepartureAt) }}{{ time(row.expectedDriverEndAt) }}
{{ row.stops.map((stop: FleetTrip['stops'][number]) => stop.name).join(' → ') }}
diff --git a/src/styles/adminDensity.test.ts b/src/styles/adminDensity.test.ts
index 627359eb..0f546ec1 100644
--- a/src/styles/adminDensity.test.ts
+++ b/src/styles/adminDensity.test.ts
@@ -64,6 +64,7 @@ const generalCatalogConsumers = [
'overseas-master-data/pages/VehicleDetailView.vue',
'overseas-master-data/pages/DriversView.vue',
'overseas-master-data/pages/TripsView.vue',
+ 'overseas-master-data/pages/TripOperationsView.vue',
'overseas-master-data/pages/TripDetailView.vue',
'overseas-master-data/components/TripPlanDialog.vue',
'overseas-master-data/pages/DriverDetailView.vue',