From 751d290f1b2d30106fd5c97dc20bc98b2b37a263 Mon Sep 17 00:00:00 2001 From: lingniu Date: Sun, 5 Jul 2026 02:17:41 +0800 Subject: [PATCH] feat(platform): add mileage service task board --- .../apps/web/src/pages/Mileage.tsx | 85 +++++++++++++++++++ .../apps/web/src/styles/global.css | 36 ++++++++ .../apps/web/src/test/App.test.tsx | 9 ++ 3 files changed, 130 insertions(+) diff --git a/vehicle-data-platform/apps/web/src/pages/Mileage.tsx b/vehicle-data-platform/apps/web/src/pages/Mileage.tsx index 08e4ccf0..df02a521 100644 --- a/vehicle-data-platform/apps/web/src/pages/Mileage.tsx +++ b/vehicle-data-platform/apps/web/src/pages/Mileage.tsx @@ -848,6 +848,56 @@ export function Mileage({ '在线统计运营态势' ); }; + const mileageServiceTasks = [ + { + title: '发布判断', + value: publishAuditConclusion, + detail: publishAuditConclusion === '可发布' ? '当前统计范围可进入运营日报或 BI 口径。' : '发布前需要补齐闭合、覆盖、异常或在线证据。', + color: publishAuditColor, + primaryAction: 'BI口径', + secondaryAction: '发布审计', + disabled: false, + secondaryDisabled: false, + onPrimary: copyBIPublishText, + onSecondary: copyPublishAudit + }, + { + title: '区间复核', + value: closureStatus, + detail: `范围 ${mileageRangeText},${closureActionText}`, + color: closureStatusColor, + primaryAction: '轨迹证据', + secondaryAction: 'RAW证据', + disabled: !currentVehicleKeyword || !onOpenHistory, + secondaryDisabled: !currentVehicleKeyword || !onOpenRaw, + onPrimary: () => onOpenHistory?.({ keyword: currentVehicleKeyword, protocol: currentProtocol, dateFrom: filters.dateFrom ?? '', dateTo: filters.dateTo ?? '' }), + onSecondary: () => onOpenRaw?.({ keyword: currentVehicleKeyword, protocol: currentProtocol, dateFrom: filters.dateFrom ?? '', dateTo: filters.dateTo ?? '', includeFields: 'true' }) + }, + { + title: '异常处理', + value: `${anomalyRows.length.toLocaleString()} 条异常`, + detail: anomalyRows[0] ? `${anomalyRows[0].plate || anomalyRows[0].vin} / ${anomalyRows[0].date} 需要复核。` : '当前页未发现异常里程记录。', + color: anomalyRows.length > 0 ? 'orange' as const : 'green' as const, + primaryAction: '复制证据', + secondaryAction: '车辆档案', + disabled: !anomalyRows[0], + secondaryDisabled: !currentVehicleKeyword, + onPrimary: () => anomalyRows[0] && copyMileageEvidence(anomalyRows[0]), + onSecondary: () => currentVehicleKeyword && onOpenVehicle(currentVehicleKeyword, currentProtocol) + }, + { + title: '数据交付', + value: `${rows.length.toLocaleString()} 条当前页`, + detail: `累计 ${formatKm(summary.totalMileageKm)} km,当前页 ${formatKm(pageMileageTotal)} km。`, + color: 'blue' as const, + primaryAction: '导出明细', + secondaryAction: '统计摘要', + disabled: rows.length === 0, + secondaryDisabled: false, + onPrimary: exportMileage, + onSecondary: copyStatisticsSummary + } + ]; useEffect(() => { const nextFilters = mergeInitialFilters(initialVin, initialProtocol, initialFilters); @@ -945,6 +995,41 @@ export function Mileage({ + +
+ {mileageServiceTasks.map((item) => ( +
+
+ {item.title} + {item.value} +
+ {item.detail} + + + + +
+ ))} +
+
{ const nextFilters = values as Record; diff --git a/vehicle-data-platform/apps/web/src/styles/global.css b/vehicle-data-platform/apps/web/src/styles/global.css index 3a8e06a2..3ccc7ea8 100644 --- a/vehicle-data-platform/apps/web/src/styles/global.css +++ b/vehicle-data-platform/apps/web/src/styles/global.css @@ -1812,6 +1812,41 @@ button.vp-realtime-command-item:focus-visible { line-height: 18px; } +.vp-mileage-task-grid { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 12px; +} + +.vp-mileage-task-item { + min-height: 164px; + padding: 14px; + border: 1px solid var(--vp-border); + border-radius: var(--vp-radius); + background: #fbfcff; + display: grid; + align-content: space-between; + gap: 12px; +} + +.vp-mileage-task-head { + min-width: 0; + display: grid; + gap: 8px; +} + +.vp-mileage-task-head strong { + color: var(--vp-text); + font-size: 18px; + line-height: 24px; + word-break: break-word; +} + +.vp-mileage-task-item .semi-typography { + font-size: 13px; + line-height: 20px; +} + .vp-mileage-mini-charts { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); @@ -3308,6 +3343,7 @@ button.vp-realtime-command-item:focus-visible { .vp-vehicle-map-layout, .vp-mileage-console, .vp-mileage-kpi-grid, + .vp-mileage-task-grid, .vp-mileage-mini-charts, .vp-playback-layout, .vp-playback-timeline { 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 f9a4a7ab..53ee2d0f 100644 --- a/vehicle-data-platform/apps/web/src/test/App.test.tsx +++ b/vehicle-data-platform/apps/web/src/test/App.test.tsx @@ -7852,6 +7852,15 @@ test('shows vehicle-first statistics domains for one vehicle service', async () expect(screen.getByText('影响待复核')).toBeInTheDocument(); expect(screen.getByText('3 辆车')).toBeInTheDocument(); expect(screen.getByText('当前筛选会影响 6 条里程记录、210 km 统计口径和 1 辆离线车辆判断。')).toBeInTheDocument(); + expect(screen.getByText('里程服务任务板')).toBeInTheDocument(); + expect(screen.getAllByText('发布判断').length).toBeGreaterThanOrEqual(1); + expect(screen.getByText('区间复核')).toBeInTheDocument(); + expect(screen.getByText('异常处理')).toBeInTheDocument(); + expect(screen.getByText('数据交付')).toBeInTheDocument(); + expect(screen.getByRole('button', { name: '里程服务任务 发布判断 BI口径' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: '里程服务任务 区间复核 轨迹证据' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: '里程服务任务 异常处理 复制证据' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: '里程服务任务 数据交付 导出明细' })).toBeInTheDocument(); expect(screen.getByText('影响车辆')).toBeInTheDocument(); expect(screen.getByText('统计规模')).toBeInTheDocument(); expect(screen.getByText('闭合影响')).toBeInTheDocument();