diff --git a/vehicle-data-platform/apps/web/src/pages/Vehicles.tsx b/vehicle-data-platform/apps/web/src/pages/Vehicles.tsx index 283f8b5c..72ae4611 100644 --- a/vehicle-data-platform/apps/web/src/pages/Vehicles.tsx +++ b/vehicle-data-platform/apps/web/src/pages/Vehicles.tsx @@ -69,6 +69,22 @@ function sourceDiagnosisText(row: VehicleCoverageRow) { return parts.join(','); } +function vehicleActionRecommendation(row: VehicleCoverageRow) { + if (row.bindingStatus !== 'bound') { + return { label: '维护身份绑定', color: 'orange' as const }; + } + if ((row.missingProtocols ?? []).length > 0) { + return { label: `补齐 ${row.missingProtocols.join('、')} 来源`, color: 'orange' as const }; + } + if (row.sourceCount <= 0) { + return { label: '确认平台转发配置', color: 'orange' as const }; + } + if (row.onlineSourceCount <= 0) { + return { label: '排查离线链路', color: 'red' as const }; + } + return { label: '持续观察', color: 'green' as const }; +} + function sourceConsistencyAction(row: VehicleCoverageRow, onFilter: (filters: Record) => void) { const consistency = row.sourceConsistency; if (!consistency) { @@ -225,6 +241,14 @@ export function Vehicles({ render: (_: unknown, row: VehicleCoverageRow) => sourceConsistencyAction(row, (nextFilters) => applyFilters({ ...filters, ...nextFilters })) }, { title: '诊断摘要', width: 240, render: (_: unknown, row: VehicleCoverageRow) => sourceDiagnosisText(row) }, + { + title: '建议动作', + width: 180, + render: (_: unknown, row: VehicleCoverageRow) => { + const action = vehicleActionRecommendation(row); + return {action.label}; + } + }, { title: '证据覆盖', width: 120, render: (_: unknown, row: VehicleCoverageRow) => sourceEvidenceText(row) }, { title: '在线', width: 90, render: (_: unknown, row: VehicleCoverageRow) => }, { title: '最后在线', dataIndex: 'lastSeen', width: 170 }, 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 fb749708..3c090dbb 100644 --- a/vehicle-data-platform/apps/web/src/test/App.test.tsx +++ b/vehicle-data-platform/apps/web/src/test/App.test.tsx @@ -584,6 +584,8 @@ test('shows vehicle service result summary on vehicle list filters', async () => expect(screen.getByText('YUTONG_MQTT 未接入')).toBeInTheDocument(); expect(screen.getByText('1/2 来源在线')).toBeInTheDocument(); expect(screen.getByText('1/2 来源在线,缺 YUTONG_MQTT')).toBeInTheDocument(); + expect(screen.getByText('建议动作')).toBeInTheDocument(); + expect(screen.getByText('补齐 YUTONG_MQTT 来源')).toBeInTheDocument(); }); test('shows and clears current vehicle service filters', async () => {