From b5f98bb6d826451ed80e84ac89a8376b281e8b10 Mon Sep 17 00:00:00 2001 From: lingniu Date: Sun, 5 Jul 2026 04:46:46 +0800 Subject: [PATCH] feat(platform): add customer mileage query board --- .../apps/web/src/pages/Mileage.tsx | 92 +++++++++++++++++++ .../apps/web/src/styles/global.css | 88 ++++++++++++++++++ .../apps/web/src/test/App.test.tsx | 7 ++ 3 files changed, 187 insertions(+) diff --git a/vehicle-data-platform/apps/web/src/pages/Mileage.tsx b/vehicle-data-platform/apps/web/src/pages/Mileage.tsx index 2e001791..d4e5099b 100644 --- a/vehicle-data-platform/apps/web/src/pages/Mileage.tsx +++ b/vehicle-data-platform/apps/web/src/pages/Mileage.tsx @@ -843,6 +843,61 @@ export function Mileage({ action: () => onOpenVehicle(currentVehicleKeyword, currentProtocol) } ]; + const mileageCustomerQuerySteps = [ + { + step: '01', + title: '选择范围', + value: currentVehicleKeyword || '全部车辆', + detail: `时间范围 ${mileageRangeText},来源 ${currentProtocol || '全部来源'}。`, + action: '调整筛选', + color: currentVehicleKeyword ? 'green' as const : 'blue' as const, + disabled: false, + onClick: () => { + const input = document.querySelector('input[name="keyword"]'); + input?.focus(); + } + }, + { + step: '02', + title: '区间里程', + value: `${formatKm(summary.totalMileageKm)} km`, + detail: `${summary.vehicleCount.toLocaleString()} 辆车,${summary.recordCount.toLocaleString()} 条日里程记录。`, + action: '复制摘要', + color: 'blue' as const, + disabled: false, + onClick: () => copyStatisticsSummary() + }, + { + step: '03', + title: '发布判断', + value: publishAuditConclusion, + detail: `闭合 ${closureStatus},覆盖 ${formatPercent(pageCoverageRate)}。`, + action: '发布审计', + color: publishAuditColor, + disabled: false, + onClick: () => copyPublishAudit() + }, + { + step: '04', + title: '复核证据', + value: anomalyRows.length > 0 ? `${anomalyRows.length.toLocaleString()} 异常` : '轨迹/RAW', + detail: anomalyRows.length > 0 ? '异常日优先回看轨迹和原始记录。' : '可从同一时间窗进入轨迹和RAW证据。', + action: '轨迹复核', + color: anomalyRows.length > 0 ? 'orange' as const : 'green' as const, + disabled: !currentVehicleKeyword || !onOpenHistory, + onClick: () => onOpenHistory?.({ keyword: currentVehicleKeyword, protocol: currentProtocol, dateFrom: filters.dateFrom ?? '', dateTo: filters.dateTo ?? '' }) + }, + { + step: '05', + title: '数据交付', + value: `${rows.length.toLocaleString()} 当前页`, + detail: '导出明细并附带统计口径,支撑BI、客户复盘和运营日报。', + action: '导出明细', + color: rows.length > 0 ? 'blue' as const : 'grey' as const, + disabled: rows.length === 0, + onClick: () => exportMileage() + } + ]; const loadSummary = (values: Record = filters) => { setSummaryLoading(true); @@ -1145,6 +1200,43 @@ export function Mileage({ 当前来源:{currentProtocol || '全部来源'} 里程统计按当前车辆与来源范围汇总。 + +
+ + 客户统计查询 + {mileageDeliveryState} + {closureStatus} + + 先定车辆和时间范围,再判断里程能否交付 + + 统计查询从车辆服务出发:选择车辆或车队、设定时间窗、判断区间里程和发布状态,再进入轨迹、RAW 和导出明细。 + + + + + + + +
+
+ {mileageCustomerQuerySteps.map((item) => ( + + ))} +
+
diff --git a/vehicle-data-platform/apps/web/src/styles/global.css b/vehicle-data-platform/apps/web/src/styles/global.css index aa55f8e0..9bd10e75 100644 --- a/vehicle-data-platform/apps/web/src/styles/global.css +++ b/vehicle-data-platform/apps/web/src/styles/global.css @@ -2711,6 +2711,92 @@ button.vp-realtime-command-item:focus-visible { gap: 16px; } +.vp-mileage-customer-query-board { + margin-top: 16px; + overflow: hidden; +} + +.vp-mileage-customer-query-board .semi-card-body { + display: grid; + grid-template-columns: minmax(280px, 0.58fr) minmax(0, 1fr); +} + +.vp-mileage-customer-query-summary { + padding: 18px; + border-right: 1px solid var(--vp-border); + background: #f8fbff; + display: grid; + gap: 12px; + align-content: start; +} + +.vp-mileage-customer-query-steps { + padding: 16px; + display: grid; + grid-template-columns: repeat(5, minmax(0, 1fr)); + gap: 10px; + background: #fff; +} + +.vp-mileage-customer-query-step { + min-height: 156px; + padding: 12px; + border: 1px solid var(--vp-border); + border-radius: 8px; + background: #fbfcff; + text-align: left; + cursor: pointer; + font: inherit; + display: grid; + gap: 8px; + align-content: start; +} + +.vp-mileage-customer-query-step:hover, +.vp-mileage-customer-query-step:focus-visible { + border-color: rgba(22, 100, 255, 0.45); + box-shadow: var(--vp-shadow-sm); + outline: none; +} + +.vp-mileage-customer-query-step:disabled { + cursor: not-allowed; + opacity: 0.62; +} + +.vp-mileage-customer-query-step > span { + width: 28px; + height: 28px; + border-radius: 50%; + background: #1664ff; + color: #fff; + font-size: 12px; + font-weight: 700; + display: inline-flex; + align-items: center; + justify-content: center; +} + +.vp-mileage-customer-query-step strong { + color: var(--vp-text); + font-size: 18px; + line-height: 24px; +} + +.vp-mileage-customer-query-step small { + color: var(--vp-text-muted); + font-size: 12px; + line-height: 18px; +} + +.vp-mileage-customer-query-step em { + color: #1664ff; + font-style: normal; + font-size: 12px; + font-weight: 600; + align-self: end; +} + .vp-mileage-delivery-board { display: grid; grid-template-columns: minmax(280px, 0.64fr) minmax(0, 1.36fr); @@ -4773,6 +4859,8 @@ button.vp-realtime-command-item:focus-visible { .vp-source-fusion-grid, .vp-source-fusion-facts, .vp-vehicle-map-layout, + .vp-mileage-customer-query-board .semi-card-body, + .vp-mileage-customer-query-steps, .vp-mileage-delivery-board, .vp-mileage-delivery-grid, .vp-mileage-console, diff --git a/vehicle-data-platform/apps/web/src/test/App.test.tsx b/vehicle-data-platform/apps/web/src/test/App.test.tsx index 363d8991..91fabc73 100644 --- a/vehicle-data-platform/apps/web/src/test/App.test.tsx +++ b/vehicle-data-platform/apps/web/src/test/App.test.tsx @@ -7979,6 +7979,13 @@ test('shows vehicle-first statistics domains for one vehicle service', async () render(); + expect(await screen.findByText('客户统计查询')).toBeInTheDocument(); + expect(screen.getByText('先定车辆和时间范围,再判断里程能否交付')).toBeInTheDocument(); + expect(screen.getByRole('button', { name: '客户统计查询 选择范围 调整筛选' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: '客户统计查询 区间里程 复制摘要' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: '客户统计查询 发布判断 发布审计' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: '客户统计查询 复核证据 轨迹复核' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: '客户统计查询 数据交付 导出明细' })).toBeInTheDocument(); expect(await screen.findByText('车辆统计服务总览')).toBeInTheDocument(); expect(screen.getAllByText('里程统计').length).toBeGreaterThanOrEqual(1); expect(screen.getByText('在线率与离线时长')).toBeInTheDocument();