diff --git a/vehicle-data-platform/apps/web/src/pages/Realtime.tsx b/vehicle-data-platform/apps/web/src/pages/Realtime.tsx index 1bff4bb1..92153d09 100644 --- a/vehicle-data-platform/apps/web/src/pages/Realtime.tsx +++ b/vehicle-data-platform/apps/web/src/pages/Realtime.tsx @@ -480,6 +480,47 @@ function mapCustomerPackageText({ ].filter(Boolean).join('\n'); } +function mapCustomerDecisionText({ + filters, + rows, + total, + onlineCount, + locatedCount, + attentionRows, + selectedRow, + selectedProtocol, + amapConfigured +}: { + filters: Record; + rows: VehicleRealtimeRow[]; + total: number; + onlineCount: number; + locatedCount: number; + attentionRows: VehicleRealtimeRow[]; + selectedRow?: VehicleRealtimeRow; + selectedProtocol?: string; + amapConfigured: boolean; +}) { + const selectedVIN = selectedRow?.vin || ''; + const topAttention = attentionRows[0]; + return [ + '【地图客户决策说明】', + `当前筛选:${realtimeFilterSummary(filters).join(';') || '全部车辆'}`, + `车辆范围:当前页 ${rows.length.toLocaleString()} / 总计 ${total.toLocaleString()}`, + `在线判断:${onlineCount.toLocaleString()} 在线 / ${(rows.length - onlineCount).toLocaleString()} 离线`, + `定位判断:${locatedCount.toLocaleString()} 辆有有效坐标 / ${Math.max(0, rows.length - locatedCount).toLocaleString()} 辆无坐标`, + `关注判断:${attentionRows.length.toLocaleString()} 辆需要关注${topAttention ? `;优先车辆 ${topAttention.plate || topAttention.vin}:${realtimeIssueLabels(topAttention).join(';')}` : ''}`, + `地图能力:${amapConfigured ? '高德地图可用' : '坐标预览'}`, + `选中车辆:${selectedRow ? `${selectedRow.plate || '-'} / ${selectedVIN} / ${selectedProtocol || selectedRow.primaryProtocol || '-'}` : '未选择'}`, + selectedRow ? `选中车辆下一步:${vehicleServiceStatus(selectedRow).label};${dataFreshness(selectedRow).detail};${realtimeIssueLabels(selectedRow).join(';')}` : '选中车辆下一步:先从地图或车辆列表选择一辆车', + `实时地图:${appURL(buildAppHash({ page: 'map', protocol: filters.protocol, filters }))}`, + `在线车辆:${appURL(buildAppHash({ page: 'map', protocol: filters.protocol, filters: { ...filters, online: 'online' } }))}`, + selectedVIN ? `车辆服务:${appURL(buildAppHash({ page: 'detail', keyword: selectedVIN, protocol: selectedProtocol }))}` : '', + selectedVIN ? `轨迹回放:${appURL(buildAppHash({ page: 'history', keyword: selectedVIN, protocol: selectedProtocol }))}` : '', + selectedVIN ? `数据导出:${appURL(buildAppHash({ page: 'history-query', keyword: selectedVIN, protocol: selectedProtocol, filters: { tab: 'raw', includeFields: 'true' } }))}` : '' + ].filter(Boolean).join('\n'); +} + function buildRealtimeSourceCoverage(rows: VehicleRealtimeRow[]) { const sourceMap = new Map(); rows.forEach((row) => { @@ -774,6 +815,7 @@ export function Realtime({ { label: '有效定位', value: locatedCount.toLocaleString(), color: locatedCount > 0 ? 'green' as const : 'orange' as const, helper: `定位率 ${formatPercent(locatedRate)}` }, { label: '需关注', value: mapAttentionRows.length.toLocaleString(), color: mapAttentionRows.length > 0 ? 'orange' as const : 'green' as const, helper: `${staleCount.toLocaleString()} 辆更新超时` } ]; + const missingCoordinateCount = Math.max(0, rows.length - locatedCount); const selectedVehicleProtocol = selectedMapRow ? filters.protocol || selectedMapRow.primaryProtocol || '' : ''; const selectedVehicleLabel = selectedMapRow?.plate || selectedMapRow?.vin || '未选择车辆'; const openSelectedVehicleRaw = () => { @@ -886,6 +928,41 @@ export function Realtime({ secondaryDisabled: !selectedMapRow || !canOpenVehicle(selectedMapRow.vin) } ]; + const mapCustomerDecisionItems = [ + { + label: '先看在线', + value: `${onlineCount.toLocaleString()} 在线`, + detail: `${(rows.length - onlineCount).toLocaleString()} 辆离线,先确认客户能看到哪些车还在上报。`, + color: onlineCount > 0 ? 'green' as const : 'orange' as const, + action: '只看在线', + onClick: () => applyFilters({ ...filters, online: 'online' }) + }, + { + label: '再看定位', + value: `${locatedCount.toLocaleString()} 有坐标`, + detail: `${missingCoordinateCount.toLocaleString()} 辆无有效坐标,地图可视范围决定客户能否看车。`, + color: locatedCount > 0 ? 'blue' as const : 'orange' as const, + action: '地图态势', + onClick: () => 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, + action: '告警事件', + onClick: () => onOpenQuality?.({ serviceStatus: 'degraded' }) + }, + { + label: '选车复盘', + value: selectedVehicleLabel, + detail: selectedMapRow ? `${vehicleServiceStatus(selectedMapRow).label},${dataFreshness(selectedMapRow).detail}` : '从地图或车辆列表选择一辆车后进入轨迹、里程和数据导出。', + color: selectedMapRow ? vehicleServiceStatus(selectedMapRow).color : 'grey' as const, + action: '轨迹回放', + disabled: !selectedMapRow || !canOpenVehicle(selectedMapRow.vin), + onClick: () => selectedMapRow && onOpenHistory?.(selectedMapRow.vin, selectedVehicleProtocol) + } + ]; const mapCustomerPackageItems = [ { label: '车辆范围', @@ -930,6 +1007,17 @@ export function Realtime({ selectedProtocol: selectedVehicleProtocol, amapConfigured }), '客户地图监控包'); + const copyMapCustomerDecision = () => copyText(mapCustomerDecisionText({ + filters, + rows, + total: pagination.total, + onlineCount, + locatedCount, + attentionRows: mapAttentionRows, + selectedRow: selectedMapRow, + selectedProtocol: selectedVehicleProtocol, + amapConfigured + }), '地图客户决策说明'); if (mode === 'map') { return ( @@ -960,10 +1048,45 @@ export function Realtime({ - - -
- {mapFleetKpis.map((item) => ( +
+ +
+
+ + 地图客户决策 + {amapConfigured ? '高德地图可用' : '坐标预览'} + 0 ? 'orange' : 'green'}>{mapAttentionRows.length.toLocaleString()} 辆关注 + + 先判断车辆是否在线、是否有位置、是否有异常,再进入单车轨迹和数据交付。 + + 地图页面向客户展示车辆服务状态,协议来源只作为定位、在线和异常追溯证据。 + + + + + +
+
+ {mapCustomerDecisionItems.map((item) => ( + + ))} +
+
+ +
+ {mapFleetKpis.map((item) => (