From 311a29a1dc8accefc80ffc6a19c91d5a63739c23 Mon Sep 17 00:00:00 2001 From: lingniu Date: Mon, 6 Jul 2026 03:05:56 +0800 Subject: [PATCH] feat(platform): add map status filter strip --- .../apps/web/src/pages/Realtime.tsx | 67 +++++++++++++++++ .../apps/web/src/styles/global.css | 74 ++++++++++++++++++- .../apps/web/src/test/App.test.tsx | 6 ++ 3 files changed, 146 insertions(+), 1 deletion(-) diff --git a/vehicle-data-platform/apps/web/src/pages/Realtime.tsx b/vehicle-data-platform/apps/web/src/pages/Realtime.tsx index d8e0ad4d..93ec46a7 100644 --- a/vehicle-data-platform/apps/web/src/pages/Realtime.tsx +++ b/vehicle-data-platform/apps/web/src/pages/Realtime.tsx @@ -1074,8 +1074,46 @@ export function Realtime({ ? `${realtimePriorityRows.length.toLocaleString()} 辆优先处置` : '暂无阻断'; const missingCoordinateCount = Math.max(0, rows.length - locatedCount); + const noCoordinateRows = rows.filter((row) => !isValidCoordinate(row)); const selectedVehicleProtocol = selectedMapRow ? filters.protocol || selectedMapRow.primaryProtocol || '' : ''; const selectedVehicleLabel = selectedMapRow?.plate || selectedMapRow?.vin || '未选择车辆'; + const mapStatusFilterItems = [ + { + label: '在线车辆', + value: `${onlineCount.toLocaleString()} 在线`, + detail: `在线率 ${formatPercent(onlineRate)},客户当前可实时看车。`, + color: onlineCount > 0 ? 'green' as const : 'orange' as const, + onClick: () => applyFilters({ ...filters, online: 'online' }) + }, + { + label: '离线车辆', + value: `${Math.max(0, rows.length - onlineCount).toLocaleString()} 离线`, + detail: '优先判断平台是否仍在转发,或车辆是否停止上报。', + color: rows.length - onlineCount > 0 ? 'orange' as const : 'green' as const, + onClick: () => applyFilters({ ...filters, online: 'offline' }) + }, + { + label: '无坐标车辆', + value: `${missingCoordinateCount.toLocaleString()} 无坐标`, + detail: '没有有效经纬度时,地图、轨迹和围栏都需要先复核。', + color: missingCoordinateCount > 0 ? 'orange' as const : 'green' as const, + onClick: () => { + const firstNoCoordinate = noCoordinateRows.find((row) => canOpenVehicle(row.vin)); + if (firstNoCoordinate) { + selectRealtimeRow(firstNoCoordinate); + return; + } + load(filters, pagination.currentPage, pagination.pageSize); + } + }, + { + label: '需关注车辆', + value: `${mapAttentionRows.length.toLocaleString()} 关注`, + detail: mapAttentionRows[0] ? `${mapAttentionRows[0].plate || mapAttentionRows[0].vin}:${realtimeIssueLabels(mapAttentionRows[0]).join(';')}` : '当前筛选下暂无离线、超时或通道异常。', + color: mapAttentionRows.length > 0 ? 'orange' as const : 'green' as const, + onClick: () => applyFilters({ ...filters, serviceStatus: 'degraded' }) + } + ]; const openSelectedVehicleRaw = () => { if (!selectedMapRow || !canOpenVehicle(selectedMapRow.vin)) { Toast.warning('请先选择可查询的车辆'); @@ -2158,6 +2196,35 @@ export function Realtime({ +
+
+ + 地图状态筛选条 + 0 ? 'orange' : 'green'}> + {mapAttentionRows.length > 0 ? `${mapAttentionRows.length.toLocaleString()} 关注` : '状态稳定'} + + + + 先按客户最关心的在线、离线、无坐标和需关注分组看车,再进入轨迹、统计、导出或告警。 + +
+
+ {mapStatusFilterItems.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 1af02d8e..f4e4cb89 100644 --- a/vehicle-data-platform/apps/web/src/styles/global.css +++ b/vehicle-data-platform/apps/web/src/styles/global.css @@ -4659,6 +4659,75 @@ body { line-height: 18px; } +.vp-map-status-filter-strip { + margin-bottom: 16px; + border: 1px solid rgba(22, 100, 255, 0.16); + border-radius: 8px; + background: #ffffff; + display: grid; + grid-template-columns: minmax(260px, 0.58fr) minmax(0, 1.42fr); + overflow: hidden; +} + +.vp-map-status-filter-copy { + padding: 16px; + border-right: 1px solid var(--vp-border); + background: #f7fbff; + display: grid; + gap: 9px; + align-content: center; +} + +.vp-map-status-filter-copy .semi-typography { + color: var(--vp-text); + line-height: 22px; +} + +.vp-map-status-filter-grid { + padding: 12px; + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 10px; +} + +.vp-map-status-filter-item { + min-height: 108px; + padding: 12px; + border: 1px solid var(--vp-border); + border-radius: 8px; + background: #fbfcff; + color: inherit; + cursor: pointer; + font: inherit; + text-align: left; + display: grid; + gap: 7px; + align-content: start; + transition: border-color 0.16s ease, box-shadow 0.16s ease, background 0.16s ease; +} + +.vp-map-status-filter-item:hover, +.vp-map-status-filter-item:focus-visible { + border-color: rgba(22, 100, 255, 0.42); + background: #f5f9ff; + box-shadow: var(--vp-shadow-sm); + outline: none; +} + +.vp-map-status-filter-item strong { + color: var(--vp-text); + font-size: 18px; + line-height: 24px; + word-break: break-word; +} + +.vp-map-status-filter-item span { + color: var(--vp-text-muted); + font-size: 12px; + line-height: 18px; + word-break: break-word; +} + .vp-realtime-customer-steps { padding: 16px; display: grid; @@ -13472,6 +13541,8 @@ button.vp-realtime-command-item:focus-visible { .vp-map-ops-board, .vp-map-ops-readiness, .vp-map-ops-work, + .vp-map-status-filter-strip, + .vp-map-status-filter-grid, .vp-customer-service-command-main, .vp-customer-service-command-grid, .vp-customer-fleet-groups, @@ -13715,7 +13786,8 @@ button.vp-realtime-command-item:focus-visible { grid-template-columns: 1fr; } - .vp-customer-cockpit-summary { + .vp-customer-cockpit-summary, + .vp-map-status-filter-copy { border-right: 0; border-bottom: 1px solid var(--vp-border); } 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 d8a1e0e4..cd62e0de 100644 --- a/vehicle-data-platform/apps/web/src/test/App.test.tsx +++ b/vehicle-data-platform/apps/web/src/test/App.test.tsx @@ -10870,6 +10870,12 @@ test('shows realtime freshness status for recently updated and stale vehicles', expect(screen.getByRole('button', { name: '车辆服务地图任务 历史查询 JT808' })).toBeInTheDocument(); expect(screen.getByRole('button', { name: '车辆服务地图任务 告警通知 1 关注' })).toBeInTheDocument(); expect(screen.getByRole('button', { name: '车辆服务地图任务 统计导出 2 辆' })).toBeInTheDocument(); + expect(screen.getByText('地图状态筛选条')).toBeInTheDocument(); + expect(screen.getByText('先按客户最关心的在线、离线、无坐标和需关注分组看车,再进入轨迹、统计、导出或告警。')).toBeInTheDocument(); + expect(screen.getByRole('button', { name: '地图状态筛选条 在线车辆 1 在线' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: '地图状态筛选条 离线车辆 1 离线' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: '地图状态筛选条 无坐标车辆 0 无坐标' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: '地图状态筛选条 需关注车辆 1 关注' })).toBeInTheDocument(); expect(screen.getAllByText('数据新鲜').length).toBeGreaterThan(0); expect(screen.getAllByText('更新超时').length).toBeGreaterThan(0); expect(screen.getByText('车辆服务闭环')).toBeInTheDocument();