import { Button, Card, Form, Select, Space, Table, Tabs, Tag, Toast, Typography } from '@douyinfe/semi-ui'; import { IconCopy } from '@douyinfe/semi-icons'; import { useEffect, useState } from 'react'; import { api } from '../api/client'; import type { OpsHealth, VehicleRealtimeRow } from '../api/types'; import { DataEmpty } from '../components/DataEmpty'; import { PageHeader } from '../components/PageHeader'; import { SourceStatusTags } from '../components/SourceStatusTags'; import { StatusTag } from '../components/StatusTag'; import { VehicleMap, type VehicleMapPoint } from '../components/VehicleMap'; import { getAMapConfig, isAMapConfigured } from '../config/appConfig'; import { buildAppHash } from '../domain/appRoute'; import { buildCsv, downloadCsv, type CsvColumn } from '../domain/csvExport'; function canOpenVehicle(vin?: string) { const value = vin?.trim(); return Boolean(value && value !== 'unknown'); } function vehicleServiceStatus(row: VehicleRealtimeRow) { if (row.serviceStatus) { return { label: row.serviceStatus.title, color: row.serviceStatus.severity === 'ok' ? 'green' as const : row.serviceStatus.severity === 'error' ? 'red' as const : 'orange' as const }; } if (row.onlineSourceCount <= 0) { return { label: '车辆离线', color: 'red' as const }; } if (row.onlineSourceCount < row.sourceCount) { return { label: '来源不完整', color: 'orange' as const }; } return { label: '服务正常', color: 'green' as const }; } function sourceEvidenceText(row: VehicleRealtimeRow) { return `${row.onlineSourceCount}/${row.sourceCount} 来源在线`; } function formatPercent(value: number) { return Number.isFinite(value) ? `${value.toLocaleString(undefined, { maximumFractionDigits: 1 })}%` : '0%'; } function serviceStatusWeight(row: VehicleRealtimeRow) { const severity = row.serviceStatus?.severity; if (severity === 'error') return 4; if (severity === 'warning') return 3; if (row.onlineSourceCount <= 0) return 4; if (row.onlineSourceCount < row.sourceCount) return 3; return 1; } function sourceIssueTags(row: VehicleRealtimeRow) { const tags = (row.sourceStatus ?? []) .filter((source) => !source.online || !source.hasRealtime) .map((source) => `${source.protocol} ${source.hasRealtime ? '离线' : '未接入'}`) ?? []; if (tags.length > 0) return tags; if (row.onlineSourceCount < row.sourceCount) return ['来源缺失']; return []; } function hasSourceIssue(row: VehicleRealtimeRow) { const severity = row.serviceStatus?.severity; return severity === 'warning' || severity === 'error' || row.onlineSourceCount < row.sourceCount || sourceIssueTags(row).length > 0; } function isValidCoordinate(row: VehicleRealtimeRow) { return Number.isFinite(row.longitude) && Number.isFinite(row.latitude) && row.longitude !== 0 && row.latitude !== 0; } function realtimeMapPointId(row: VehicleRealtimeRow, index = 0) { return row.vin?.trim() || `${row.primaryProtocol || 'source'}-${index}`; } function formatRefreshTime(date: Date) { return date.toLocaleTimeString('zh-CN', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' }); } function parseVehicleTime(value?: string) { const normalized = value?.trim(); if (!normalized) { return Number.NaN; } return new Date(normalized.replace(' ', 'T')).getTime(); } function dataFreshness(row: VehicleRealtimeRow) { const timestamp = parseVehicleTime(row.lastSeen); if (!Number.isFinite(timestamp)) { return { label: '无时间', color: 'grey' as const, detail: '未上报最后时间', stale: true }; } const ageSeconds = Math.floor((Date.now() - timestamp) / 1000); if (ageSeconds <= 300) { return { label: '数据新鲜', color: 'green' as const, detail: ageSeconds <= 0 ? '刚刚更新' : `${Math.max(1, Math.ceil(ageSeconds / 60))} 分钟内更新`, stale: false }; } if (ageSeconds < 3600) { return { label: '更新超时', color: 'orange' as const, detail: `${Math.ceil(ageSeconds / 60)} 分钟未更新`, stale: true }; } if (ageSeconds < 86400) { return { label: '更新超时', color: 'red' as const, detail: `${Math.ceil(ageSeconds / 3600)} 小时未更新`, stale: true }; } return { label: '更新超时', color: 'red' as const, detail: `${Math.ceil(ageSeconds / 86400)} 天未更新`, stale: true }; } function amapMarkerURL(row: VehicleRealtimeRow) { const name = encodeURIComponent(row.plate || row.vin || '车辆位置'); return `https://uri.amap.com/marker?position=${row.longitude},${row.latitude}&name=${name}&src=lingniu-vehicle-platform`; } const onlineLabel: Record = { online: '在线', offline: '离线' }; const serviceStatusLabel: Record = { healthy: '服务正常', degraded: '来源不完整', offline: '车辆离线', identity_required: '身份未绑定' }; const realtimeExportColumns: CsvColumn[] = [ { title: 'VIN', value: (row) => row.vin }, { title: '车牌', value: (row) => row.plate }, { title: '手机号', value: (row) => row.phone }, { title: 'OEM', value: (row) => row.oem }, { title: '主来源', value: (row) => row.primaryProtocol }, { title: '来源列表', value: (row) => row.protocols?.join('|') }, { title: '在线', value: (row) => row.online ? '在线' : '离线' }, { title: '车辆服务状态', value: (row) => vehicleServiceStatus(row).label }, { title: '来源在线', value: (row) => sourceEvidenceText(row) }, { title: '经度', value: (row) => row.longitude }, { title: '纬度', value: (row) => row.latitude }, { title: '速度km/h', value: (row) => row.speedKmh }, { title: 'SOC%', value: (row) => row.socPercent }, { title: '总里程km', value: (row) => row.totalMileageKm }, { title: '最后时间', value: (row) => row.lastSeen }, { title: '绑定状态', value: (row) => row.bindingStatus } ]; type RealtimeSourceCoverage = { protocol: string; total: number; online: number; located: number; degraded: number; stale: number; }; function realtimeExportFileName(filters: Record) { const keyword = filters.keyword?.trim() || 'all'; const protocol = filters.protocol?.trim() || 'all-source'; const online = filters.online?.trim() || 'all-online'; return `realtime-vehicles-${keyword}-${protocol}-${online}.csv`; } function realtimeFilterSummary(filters: Record) { return [ filters.keyword ? `关键词:${filters.keyword}` : '', filters.protocol ? `数据来源:${filters.protocol}` : '', filters.online ? `在线:${onlineLabel[filters.online] ?? filters.online}` : '', filters.serviceStatus ? `服务状态:${serviceStatusLabel[filters.serviceStatus] ?? filters.serviceStatus}` : '' ].filter(Boolean); } function realtimeOperationsSummaryText({ filters, rows, total, onlineCount, locatedCount, degradedCount, sourceTypeCount, amapConfigured, sourceIssueRows }: { filters: Record; rows: VehicleRealtimeRow[]; total: number; onlineCount: number; locatedCount: number; degradedCount: number; sourceTypeCount: number; amapConfigured: boolean; sourceIssueRows: VehicleRealtimeRow[]; }) { const issueLines = sourceIssueRows.length > 0 ? sourceIssueRows.map((row, index) => { const status = vehicleServiceStatus(row); return `${index + 1}. ${row.plate || row.vin} / ${row.primaryProtocol || '-'} / ${status.label} / ${row.serviceStatus?.detail || sourceEvidenceText(row)}`; }).join('\n') : '暂无重点车辆'; return [ '【实时监控摘要】', `当前筛选:${realtimeFilterSummary(filters).join(';') || '全部实时车辆'}`, `车辆总数:${total.toLocaleString()},当前页:${rows.length.toLocaleString()}`, `在线车辆:${onlineCount.toLocaleString()},定位有效:${locatedCount.toLocaleString()}`, `降级服务:${degradedCount.toLocaleString()},来源类型:${sourceTypeCount.toLocaleString()}`, `地图配置:${amapConfigured ? '已配置' : '未配置'}`, '重点车辆:', issueLines, `实时页面:${window.location.origin}${window.location.pathname}${window.location.hash}` ].join('\n'); } function appURL(hash: string) { return `${window.location.origin}${window.location.pathname}${hash}`; } function rawFrameAPIURL(row: VehicleRealtimeRow, protocol: string) { const params = new URLSearchParams({ protocol, vin: row.vin || '', limit: '20', includeFields: 'true' }); return `${window.location.origin}/api/history/raw-frames?${params.toString()}`; } function realtimeIssueLabels(row: VehicleRealtimeRow) { const labels: string[] = []; const freshness = dataFreshness(row); if (freshness.stale) { labels.push(freshness.detail); } if (!isValidCoordinate(row)) { labels.push('坐标无效'); } if (hasSourceIssue(row)) { labels.push(row.serviceStatus?.detail || sourceIssueTags(row).join('、') || sourceEvidenceText(row)); } return labels.length > 0 ? labels : ['暂无异常']; } function realtimeIssueAction(row: VehicleRealtimeRow) { const issues = realtimeIssueLabels(row).join(';'); if (!isValidCoordinate(row)) { return `核对${row.primaryProtocol || '实时来源'}定位字段解析和平台转发`; } if (dataFreshness(row).stale) { return `确认${row.primaryProtocol || '实时来源'}是否持续上报,必要时检查接入链路`; } if (hasSourceIssue(row)) { return '补齐离线来源,确认多来源实时状态一致'; } return issues === '暂无异常' ? '持续观察' : '结合车辆服务详情继续排查'; } function realtimeIssueChecklistText({ filters, rows, total }: { filters: Record; rows: VehicleRealtimeRow[]; total: number; }) { const issueRows = rows .filter((row) => canOpenVehicle(row.vin) && (dataFreshness(row).stale || !isValidCoordinate(row) || hasSourceIssue(row))) .sort((a, b) => { const statusDelta = serviceStatusWeight(b) - serviceStatusWeight(a); if (statusDelta !== 0) return statusDelta; return Number(dataFreshness(b).stale) - Number(dataFreshness(a).stale); }); const lines = [ '【实时异常处置清单】', `当前筛选:${realtimeFilterSummary(filters).join(';') || '全部实时车辆'}`, `异常车辆:${issueRows.length.toLocaleString()} / 当前页 ${rows.length.toLocaleString()} / 总计 ${total.toLocaleString()}`, '' ]; if (issueRows.length === 0) { lines.push('当前页暂无实时异常车辆。'); } issueRows.forEach((row, index) => { const protocol = filters.protocol || row.primaryProtocol || ''; const status = vehicleServiceStatus(row); const issues = realtimeIssueLabels(row).join(';'); lines.push( `${index + 1}. ${row.plate || '-'} / ${row.vin} / ${protocol || '-'}`, ` 状态:${status.label};在线:${row.online ? '在线' : '离线'};问题:${issues}`, ` 位置:${isValidCoordinate(row) ? `${row.longitude},${row.latitude}` : '无有效坐标'};速度:${row.speedKmh ?? '-'} km/h;SOC:${row.socPercent ?? '-'}%`, ` 最后时间:${row.lastSeen || '-'};建议动作:${realtimeIssueAction(row)}`, ` 车辆服务:${appURL(buildAppHash({ page: 'detail', keyword: row.vin, protocol }))}`, ` 实时监控:${appURL(buildAppHash({ page: 'realtime', keyword: row.vin, protocol }))}`, ` 轨迹回放:${appURL(buildAppHash({ page: 'history', keyword: row.vin, protocol }))}`, ` 告警事件:${appURL(buildAppHash({ page: 'alert-events', keyword: row.vin, protocol }))}` ); }); return lines.join('\n'); } function realtimeDutyHandoffText({ filters, rows, total, onlineCount, locatedCount, degradedCount, staleCount, sourceTypeCount, amapConfigured, runtimeRelease }: { filters: Record; rows: VehicleRealtimeRow[]; total: number; onlineCount: number; locatedCount: number; degradedCount: number; staleCount: number; sourceTypeCount: number; amapConfigured: boolean; runtimeRelease?: string; }) { const sourceCoverage = new Map(); rows.forEach((row) => { row.sourceStatus?.forEach((source) => { const current = sourceCoverage.get(source.protocol) ?? { total: 0, online: 0, realtime: 0 }; current.total += 1; current.online += source.online ? 1 : 0; current.realtime += source.hasRealtime ? 1 : 0; sourceCoverage.set(source.protocol, current); }); }); const sourceLines = [...sourceCoverage.entries()] .sort(([left], [right]) => left.localeCompare(right)) .map(([protocol, item]) => `${protocol}:在线 ${item.online}/${item.total},实时 ${item.realtime}/${item.total}`); const sampleRows = rows .filter((row) => canOpenVehicle(row.vin)) .sort((a, b) => { const statusDelta = serviceStatusWeight(b) - serviceStatusWeight(a); if (statusDelta !== 0) return statusDelta; return Number(dataFreshness(b).stale) - Number(dataFreshness(a).stale); }) .slice(0, 8); const lines = [ '【实时值班交接包】', `当前筛选:${realtimeFilterSummary(filters).join(';') || '全部实时车辆'}`, `页面车辆:${rows.length.toLocaleString()} / 总计 ${total.toLocaleString()};版本:${runtimeRelease || '未标记'}`, `在线:${onlineCount.toLocaleString()};离线:${(rows.length - onlineCount).toLocaleString()};定位有效:${locatedCount.toLocaleString()};降级:${degradedCount.toLocaleString()};超时:${staleCount.toLocaleString()};来源类型:${sourceTypeCount.toLocaleString()}`, `地图:${amapConfigured ? '高德已配置' : '高德未配置'}`, `来源覆盖:${sourceLines.length > 0 ? sourceLines.join(';') : '当前页暂无来源明细'}`, `值班入口:${appURL(buildAppHash({ page: 'realtime', protocol: filters.protocol, filters }))}`, '' ]; if (sampleRows.length === 0) { lines.push('当前页暂无可交接 VIN 样本。'); return lines.join('\n'); } lines.push('车辆样本:'); sampleRows.forEach((row, index) => { const protocol = filters.protocol || row.primaryProtocol || ''; const status = vehicleServiceStatus(row); const freshness = dataFreshness(row); lines.push( `${index + 1}. ${row.plate || '-'} / ${row.vin} / ${protocol || '-'} / ${status.label}`, ` 最后上报:${row.lastSeen || '-'};新鲜度:${freshness.label};位置:${isValidCoordinate(row) ? `${row.longitude},${row.latitude}` : '无有效坐标'};速度:${row.speedKmh ?? '-'} km/h;SOC:${row.socPercent ?? '-'}%`, ` 来源:${sourceEvidenceText(row)};问题:${realtimeIssueLabels(row).join(';')}`, ` 车辆服务:${appURL(buildAppHash({ page: 'detail', keyword: row.vin, protocol }))}`, ` 实时监控:${appURL(buildAppHash({ page: 'realtime', keyword: row.vin, protocol }))}`, ` 轨迹回放:${appURL(buildAppHash({ page: 'history', keyword: row.vin, protocol }))}`, ` 里程统计:${appURL(buildAppHash({ page: 'mileage', keyword: row.vin, protocol }))}`, ` 历史RAW:${protocol ? rawFrameAPIURL(row, protocol) : '-'}` ); }); return lines.join('\n'); } function realtimeImpactReportText({ filters, rows, total, onlineCount, locatedCount, degradedCount, staleCount, pageCoverageRate, amapConfigured, runtimeRelease }: { filters: Record; rows: VehicleRealtimeRow[]; total: number; onlineCount: number; locatedCount: number; degradedCount: number; staleCount: number; pageCoverageRate: number; amapConfigured: boolean; runtimeRelease?: string; }) { const offlineCount = Math.max(0, rows.length - onlineCount); const impactLevel = degradedCount > 0 || staleCount > 0 || offlineCount > 0 ? '需要处置' : '实时稳定'; return [ '【实时运营影响】', `当前筛选:${realtimeFilterSummary(filters).join(';') || '全部实时车辆'}`, `运行版本:${runtimeRelease || '未标记'}`, `影响等级:${impactLevel}`, `车辆范围:当前页 ${rows.length.toLocaleString()} / 总计 ${total.toLocaleString()},覆盖率 ${formatPercent(pageCoverageRate)}`, `在线状态:${onlineCount.toLocaleString()} 在线 / ${offlineCount.toLocaleString()} 离线`, `定位影响:${locatedCount.toLocaleString()} 辆有有效坐标`, `服务影响:${degradedCount.toLocaleString()} 辆降级 / ${staleCount.toLocaleString()} 辆超时`, `地图能力:${amapConfigured ? '高德地图可用' : '高德地图未配置,使用坐标预览'}`, `建议动作:${impactLevel === '实时稳定' ? '保持监控并关注告警队列' : '优先处理离线、超时和来源不完整车辆,并回到轨迹/RAW证据复核'}`, `实时监控:${appURL(buildAppHash({ page: 'realtime', protocol: filters.protocol, filters }))}` ].join('\n'); } function buildRealtimeSourceCoverage(rows: VehicleRealtimeRow[]) { const sourceMap = new Map(); rows.forEach((row) => { const protocols = row.sourceStatus?.length ? row.sourceStatus.map((source) => source.protocol) : row.primaryProtocol ? [row.primaryProtocol] : []; protocols.forEach((protocol) => { const source = row.sourceStatus?.find((item) => item.protocol === protocol); const current = sourceMap.get(protocol) ?? { protocol, total: 0, online: 0, located: 0, degraded: 0, stale: 0 }; current.total += 1; current.online += (source ? source.online : row.online) ? 1 : 0; current.located += isValidCoordinate(row) ? 1 : 0; current.degraded += (!source || !source.online || !source.hasRealtime || row.onlineSourceCount < row.sourceCount) ? 1 : 0; current.stale += dataFreshness(row).stale ? 1 : 0; sourceMap.set(protocol, current); }); }); return [...sourceMap.values()].sort((left, right) => right.total - left.total || left.protocol.localeCompare(right.protocol)); } async function copyText(value: string, label: string) { const text = value.trim(); if (!text) { Toast.warning(`${label}为空`); return; } try { await navigator.clipboard.writeText(text); Toast.success(`已复制${label}`); } catch { Toast.error(`复制${label}失败`); } } export function Realtime({ title = '实时监控', description = '以车辆为主对象查看最新位置、在线来源、核心实时数据和地图作业状态', onOpenVehicle, onOpenHistory, onOpenQuality, onFiltersChange, initialFilters = {} }: { title?: string; description?: string; onOpenVehicle: (vin: string, protocol?: string) => void; onOpenHistory?: (vin: string, protocol?: string) => void; onOpenQuality?: (filters: Record) => void; onFiltersChange?: (filters: Record) => void; initialFilters?: Record; }) { const [rows, setRows] = useState([]); const [loading, setLoading] = useState(true); const [filters, setFilters] = useState>(initialFilters); const [pagination, setPagination] = useState({ currentPage: 1, pageSize: 50, total: 0 }); const [opsHealth, setOpsHealth] = useState(null); const [selectedMapPointId, setSelectedMapPointId] = useState(''); const [lastRefreshAt, setLastRefreshAt] = useState(''); const [autoRefresh, setAutoRefresh] = useState(true); const refreshIntervalSeconds = 15; const amapConfig = getAMapConfig(); const runtime = opsHealth?.runtime; const amapConfigured = runtime?.amapWebJsConfigured ?? isAMapConfigured(amapConfig); const amapSecurityServiceHost = runtime?.amapSecurityServiceHost || amapConfig.securityServiceHost; const amapSecurityProxyEnabled = runtime?.amapSecurityProxyEnabled ?? Boolean(amapSecurityServiceHost); const amapSecurityCodeExposed = runtime?.amapSecurityCodeExposed ?? Boolean(amapConfig.securityJsCode && !amapSecurityServiceHost); const load = (values: Record = filters, page = pagination.currentPage, pageSize = pagination.pageSize) => { setLoading(true); const params = new URLSearchParams({ limit: String(pageSize), offset: String((page - 1) * pageSize) }); if (values?.keyword) params.set('keyword', values.keyword); if (values?.protocol) params.set('protocol', values.protocol); if (values?.online) params.set('online', values.online); if (values?.serviceStatus) params.set('serviceStatus', values.serviceStatus); return api.vehicleRealtime(params) .then((nextPage) => { setRows(nextPage.items); setPagination({ currentPage: page, pageSize, total: nextPage.total }); setLastRefreshAt(formatRefreshTime(new Date())); }) .catch((error: Error) => Toast.error(error.message)) .finally(() => setLoading(false)); }; useEffect(() => { setFilters(initialFilters); load(initialFilters, 1, pagination.pageSize); api.opsHealth() .then(setOpsHealth) .catch(() => setOpsHealth(null)); }, [JSON.stringify(initialFilters)]); useEffect(() => { if (!autoRefresh) { return undefined; } const timer = window.setInterval(() => { load(filters, pagination.currentPage, pagination.pageSize); }, refreshIntervalSeconds * 1000); return () => window.clearInterval(timer); }, [autoRefresh, JSON.stringify(filters), pagination.currentPage, pagination.pageSize]); const applyFilters = (nextFilters: Record) => { setFilters(nextFilters); onFiltersChange?.(nextFilters); load(nextFilters, 1, pagination.pageSize); }; const openQualityEvidence = (row: VehicleRealtimeRow) => { if (!canOpenVehicle(row.vin)) return; onOpenQuality?.({ keyword: row.vin, protocol: filters.protocol || row.primaryProtocol || '' }); }; const exportRealtime = () => { if (rows.length === 0) { Toast.warning('当前没有可导出的实时车辆'); return; } downloadCsv(realtimeExportFileName(filters), buildCsv(realtimeExportColumns, rows)); Toast.success(`已导出 ${rows.length} 条实时车辆`); }; const filterSummary = realtimeFilterSummary(filters); const onlineCount = rows.filter((row) => row.online).length; const locatedCount = rows.filter(isValidCoordinate).length; const degradedCount = rows.filter((row) => row.onlineSourceCount < row.sourceCount).length; const staleCount = rows.filter((row) => dataFreshness(row).stale).length; const onlineRate = rows.length > 0 ? (onlineCount / rows.length) * 100 : 0; const locatedRate = rows.length > 0 ? (locatedCount / rows.length) * 100 : 0; const degradedRate = rows.length > 0 ? (degradedCount / rows.length) * 100 : 0; const pageCoverageRate = pagination.total > 0 ? (rows.length / pagination.total) * 100 : 0; const primaryProtocols = new Set(rows.map((row) => row.primaryProtocol).filter(Boolean)); const sourceCoverageRows = buildRealtimeSourceCoverage(rows); const sourceIssueRows = rows .filter((row) => canOpenVehicle(row.vin) && hasSourceIssue(row)) .sort((a, b) => { const statusDelta = serviceStatusWeight(b) - serviceStatusWeight(a); if (statusDelta !== 0) return statusDelta; return String(b.lastSeen ?? '').localeCompare(String(a.lastSeen ?? '')); }) .slice(0, 4); const mapServiceRows = rows .filter((row) => isValidCoordinate(row) && canOpenVehicle(row.vin)) .sort((a, b) => { const statusDelta = serviceStatusWeight(b) - serviceStatusWeight(a); if (statusDelta !== 0) return statusDelta; return String(b.lastSeen ?? '').localeCompare(String(a.lastSeen ?? '')); }) .slice(0, 5); const defaultMapRow = mapServiceRows[0] ?? rows.find((row) => isValidCoordinate(row) && canOpenVehicle(row.vin)); const selectedMapRow = rows.find((row, index) => realtimeMapPointId(row, index) === selectedMapPointId) ?? defaultMapRow; const selectedMapPointKey = selectedMapRow ? realtimeMapPointId(selectedMapRow, rows.indexOf(selectedMapRow)) : selectedMapPointId; const mapPoints: VehicleMapPoint[] = rows.map((row, index) => ({ id: realtimeMapPointId(row, index), label: row.plate || row.vin || 'unknown', longitude: row.longitude, latitude: row.latitude, online: row.online, title: `${row.plate || row.vin || '-'} ${vehicleServiceStatus(row).label} ${row.primaryProtocol || ''} ${row.lastSeen || ''}` })); const mapIntegrationRows = [ { label: 'Web JS Key', value: amapConfigured ? '已配置' : '未配置', color: amapConfigured ? 'green' as const : 'orange' as const, detail: amapConfigured ? '前端可加载高德 Web JS API。' : '缺少公开 Key,地图将使用坐标预览。' }, { label: '服务端 API Key', value: runtime?.amapApiConfigured ? '已配置' : '未配置', color: runtime?.amapApiConfigured ? 'green' as const : 'orange' as const, detail: runtime?.amapApiConfigured ? '后端可支持地理编码、路线、围栏等服务端地图能力。' : '服务端地图能力未配置,后续地理服务会降级。' }, { label: '安全代理', value: amapSecurityServiceHost || '未启用', color: amapSecurityProxyEnabled ? 'green' as const : 'grey' as const, detail: amapSecurityProxyEnabled ? '安全密钥由服务端追加,不下发到浏览器。' : '未配置代理时会退回前端安全码模式。' }, { label: '安全码', value: amapSecurityCodeExposed ? '已暴露' : '未暴露', color: amapSecurityCodeExposed ? 'red' as const : 'green' as const, detail: amapSecurityCodeExposed ? '当前安全码会下发到浏览器,只建议调试使用。' : '生产安全码保持在服务端环境变量中。' }, { label: '当前版本', value: runtime?.platformRelease || '未标记', color: runtime?.platformRelease ? 'blue' as const : 'grey' as const, detail: runtime?.platformRelease ? '当前 ECS 正在运行的车辆中台 release。' : '未注入 PLATFORM_RELEASE,部署追踪会降级。' }, { label: '定位覆盖', value: `${locatedCount.toLocaleString()} / ${rows.length.toLocaleString()}`, color: locatedCount > 0 ? 'blue' as const : 'orange' as const, detail: '当前页有效经纬度车辆数。' }, { label: '高德 URI', value: '坐标跳转', color: 'blue' as const, detail: '车辆队列可直接打开高德坐标,轨迹页可打开线路。' } ]; const copyRealtimeSummary = () => copyText(realtimeOperationsSummaryText({ filters, rows, total: pagination.total, onlineCount, locatedCount, degradedCount, sourceTypeCount: primaryProtocols.size, amapConfigured, sourceIssueRows }), '实时摘要'); const copyRealtimeIssueChecklist = () => copyText(realtimeIssueChecklistText({ filters, rows, total: pagination.total }), '实时异常处置清单'); const copyRealtimeDutyHandoff = () => copyText(realtimeDutyHandoffText({ filters, rows, total: pagination.total, onlineCount, locatedCount, degradedCount, staleCount, sourceTypeCount: primaryProtocols.size, amapConfigured, runtimeRelease: runtime?.platformRelease }), '实时值班交接包'); const realtimeImpactLevel = degradedCount > 0 || staleCount > 0 || rows.length - onlineCount > 0 ? '需要处置' : '实时稳定'; const realtimeImpactColor = realtimeImpactLevel === '实时稳定' ? 'green' as const : degradedCount > 0 || staleCount > 0 ? 'orange' as const : 'blue' as const; const realtimeImpactItems = [ { label: '车辆范围', value: `${rows.length.toLocaleString()} / ${pagination.total.toLocaleString()}`, detail: `当前页覆盖 ${formatPercent(pageCoverageRate)}`, color: pageCoverageRate >= 100 ? 'green' as const : 'grey' as const }, { label: '在线影响', value: `${onlineCount.toLocaleString()} 在线`, detail: `${(rows.length - onlineCount).toLocaleString()} 辆离线`, color: rows.length - onlineCount > 0 ? 'orange' as const : 'green' as const }, { label: '定位影响', value: `${locatedCount.toLocaleString()} 辆`, detail: `有效定位率 ${formatPercent(locatedRate)}`, color: locatedCount > 0 ? 'blue' as const : 'orange' as const }, { label: '服务影响', value: `${degradedCount.toLocaleString()} 降级`, detail: `${staleCount.toLocaleString()} 辆更新超时`, color: degradedCount > 0 || staleCount > 0 ? 'orange' as const : 'green' as const }, { label: '地图能力', value: amapConfigured ? '可用' : '坐标预览', detail: amapConfigured ? '高德地图配置就绪' : '高德未配置,页面降级展示坐标。', color: amapConfigured ? 'green' as const : 'orange' as const } ]; const copyRealtimeImpact = () => copyText(realtimeImpactReportText({ filters, rows, total: pagination.total, onlineCount, locatedCount, degradedCount, staleCount, pageCoverageRate, amapConfigured, runtimeRelease: runtime?.platformRelease }), '实时运营影响'); const selectRealtimeRow = (row: VehicleRealtimeRow) => { const index = rows.indexOf(row); if (index < 0) return; setSelectedMapPointId(realtimeMapPointId(row, index)); }; const selectRealtimeMapPoint = (point: VehicleMapPoint) => { setSelectedMapPointId(point.id); }; return (
{ const nextFilters = values as Record; applyFilters(nextFilters); }} style={{ marginBottom: 12 }}> GB32960 JT808 YUTONG_MQTT 在线 离线 服务正常 来源不完整 车辆离线 身份未绑定
自动刷新 {refreshIntervalSeconds}秒 最后刷新:{lastRefreshAt || '等待首次刷新'}
{filterSummary.length > 0 ? ( {filterSummary.map((item) => ( {item} ))} ) : null}
实时作业入口
{[ { label: '在线车辆', value: onlineCount, color: 'green' as const, action: () => applyFilters({ ...filters, online: 'online' }) }, { label: '离线车辆', value: rows.length - onlineCount, color: rows.length - onlineCount > 0 ? 'orange' as const : 'green' as const, action: () => applyFilters({ ...filters, online: 'offline' }) }, { label: '定位有效', value: locatedCount, color: locatedCount > 0 ? 'blue' as const : 'orange' as const }, { label: '降级服务', value: degradedCount, color: degradedCount > 0 ? 'orange' as const : 'green' as const, action: () => applyFilters({ ...filters, serviceStatus: 'degraded' }) }, { label: '更新超时', value: staleCount, color: staleCount > 0 ? 'red' as const : 'green' as const }, { label: '来源类型', value: primaryProtocols.size, color: 'blue' as const } ].map((item) => ( item.action ? ( ) : (
{item.label} {item.value.toLocaleString()}
) ))}
建议处置
{degradedCount > 0 ? ( ) : 服务状态稳定} {staleCount > 0 ? 核对超时车辆 {staleCount.toLocaleString()} : 实时新鲜} {sourceIssueRows.length > 0 && onOpenQuality ? ( ) : null}
协议地图覆盖
{sourceCoverageRows.length === 0 ? ( 当前页暂无协议来源。 ) : sourceCoverageRows.map((item) => ( ))}
{amapConfigured ? '高德地图配置就绪' : '高德地图待配置'} {locatedCount.toLocaleString()} 辆有定位 {onlineCount.toLocaleString()} 辆在线
{[ { label: '当前车辆', value: pagination.total.toLocaleString(), color: 'blue' as const }, { label: '在线车辆', value: onlineCount.toLocaleString(), color: 'green' as const }, { label: '定位有效', value: locatedCount.toLocaleString(), color: 'green' as const }, { label: '超时车辆', value: staleCount.toLocaleString(), color: staleCount > 0 ? 'orange' as const : 'green' as const }, { label: '降级服务', value: degradedCount.toLocaleString(), color: degradedCount > 0 ? 'orange' as const : 'green' as const }, { label: '来源类型', value: primaryProtocols.size.toLocaleString(), color: 'blue' as const } ].map((item) => (
{item.label}
{item.value}
))} 高德 Web JS Key 和安全密钥通过运行环境配置,前端只读取公开 Key;安全密钥后续走服务端代理,避免明文下发。 {selectedMapRow ? (
当前选中车辆
{vehicleServiceStatus(selectedMapRow).label} {dataFreshness(selectedMapRow).label} {selectedMapRow.online ? '在线' : '离线'} {selectedMapRow.primaryProtocol || '未知来源'} {selectedMapRow.plate || selectedMapRow.vin} {selectedMapRow.vin}
速度{selectedMapRow.speedKmh ?? '-'} km/h SOC{selectedMapRow.socPercent ?? '-'}% 里程{selectedMapRow.totalMileageKm ?? '-'} km 新鲜度{dataFreshness(selectedMapRow).detail} 最后时间{selectedMapRow.lastSeen || '-'}
) : null}
地图接入状态
{mapIntegrationRows.map((item) => (
{item.label} {item.detail}
{item.value}
))}
多来源一致性检查
{sourceIssueRows.length === 0 ? ( 当前页车辆来源一致 ) : ( sourceIssueRows.map((row) => { const status = vehicleServiceStatus(row); return (
{row.plate || row.vin} {row.vin}
{status.label}
{sourceEvidenceText(row)} {dataFreshness(row).label} {sourceIssueTags(row).map((item) => ( 一致性:{item} ))} 一致性诊断:{row.serviceStatus?.detail || sourceEvidenceText(row)}
{row.lastSeen || '-'}
); }) )}
地图车辆服务队列
{mapServiceRows.length === 0 ? ( 暂无可定位车辆 ) : ( mapServiceRows.map((row) => { const status = vehicleServiceStatus(row); return (
selectRealtimeRow(row)} onKeyDown={(event) => { if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); selectRealtimeRow(row); } }} >
{row.plate || row.vin} {row.vin}
{dataFreshness(row).label} {status.label}
{row.serviceStatus?.detail || sourceEvidenceText(row)} 新鲜度:{dataFreshness(row).detail}
{row.lastSeen || '-'}
); }) )}
实时运营影响} style={{ marginBottom: 16 }} >
{realtimeImpactLevel}
{pagination.total.toLocaleString()} 辆
当前页 {rows.length.toLocaleString()} 辆,在线 {onlineCount.toLocaleString()} 辆,定位有效 {locatedCount.toLocaleString()} 辆,降级 {degradedCount.toLocaleString()} 辆,超时 {staleCount.toLocaleString()} 辆。 = 100 ? 'green' : 'grey'}>覆盖 {formatPercent(pageCoverageRate)} {amapConfigured ? '高德可用' : '坐标预览'} 0 ? 'orange' : 'green'}>{(rows.length - onlineCount).toLocaleString()} 辆离线
{realtimeImpactItems.map((item) => (
{item.label} {item.value} {item.detail}
))}
{[ { label: '在线率', value: formatPercent(onlineRate), color: onlineRate >= 80 ? 'green' as const : 'orange' as const, detail: `${onlineCount.toLocaleString()} / ${rows.length.toLocaleString()} 辆在线。` }, { label: '定位有效率', value: formatPercent(locatedRate), color: locatedRate >= 80 ? 'green' as const : 'orange' as const, detail: `${locatedCount.toLocaleString()} / ${rows.length.toLocaleString()} 辆坐标有效。` }, { label: '新鲜数据', value: `${(rows.length - staleCount).toLocaleString()} 辆`, color: staleCount > 0 ? 'orange' as const : 'green' as const, detail: `${staleCount.toLocaleString()} 辆超过 5 分钟未更新。` }, { label: '降级率', value: formatPercent(degradedRate), color: degradedCount > 0 ? 'orange' as const : 'green' as const, detail: `${degradedCount.toLocaleString()} 辆存在来源不完整或离线。` }, { label: '分页覆盖率', value: formatPercent(pageCoverageRate), color: pageCoverageRate >= 100 ? 'green' as const : 'grey' as const, detail: '当前页车辆数 / 当前筛选总车辆数。' } ].map((item) => ( {item.label}
{item.value}
{item.detail}
))}
当前页 {rows.length.toLocaleString()} 条
{rows.length === 0 && !loading ? ( ) : ( load(filters, page, pagination.pageSize), onPageSizeChange: (pageSize) => load(filters, 1, pageSize) }} columns={[ { title: '车牌', dataIndex: 'plate', width: 120 }, { title: 'VIN', dataIndex: 'vin', width: 190 }, { title: '车辆服务状态', width: 130, render: (_: unknown, row: VehicleRealtimeRow) => { const status = vehicleServiceStatus(row); return {status.label}; } }, { title: '数据新鲜度', width: 130, render: (_: unknown, row: VehicleRealtimeRow) => { const freshness = dataFreshness(row); return {freshness.label}; } }, { title: '车辆核心数据', width: 230, render: (_: unknown, row: VehicleRealtimeRow) => ( {row.speedKmh ?? '-'} km/h SOC {row.socPercent ?? '-'}% {row.totalMileageKm ?? '-'} km ) }, { title: '来源证据', width: 260, render: (_: unknown, row: VehicleRealtimeRow) => ( ) }, { title: '证据覆盖', width: 120, render: (_: unknown, row: VehicleRealtimeRow) => sourceEvidenceText(row) }, { title: '在线', width: 90, render: (_: unknown, row: VehicleRealtimeRow) => }, { title: '最后时间', dataIndex: 'lastSeen', width: 170 }, { title: '操作', width: 190, render: (_: unknown, row: VehicleRealtimeRow) => ( ) } ]} /> )} ); }