import { Button, Card, Col, Form, Row, Select, Space, Spin, Table, Tag, Toast, Typography } from '@douyinfe/semi-ui'; import { useEffect, useMemo, useState } from 'react'; import { api } from '../api/client'; import type { DashboardSummary, OpsHealth, QualityIssueRow, VehicleCoverageRow, VehicleRealtimeRow, VehicleServiceSummary } from '../api/types'; import { PageHeader } from '../components/PageHeader'; import { SourceStatusTags } from '../components/SourceStatusTags'; import { StatusTag } from '../components/StatusTag'; import { VehicleMap, type VehicleMapPoint } from '../components/VehicleMap'; import { isAMapConfigured } from '../config/appConfig'; import { buildAppHash } from '../domain/appRoute'; import { buildCsv, downloadCsv, type CsvColumn } from '../domain/csvExport'; import { qualityIssueLabel, qualityProtocolLabel } from '../domain/qualityIssue'; import { qualityIssueVehicleLookup } from '../domain/vehicleLookup'; const serviceStatusTitle: Record = { healthy: '服务正常', degraded: '来源不完整', offline: '车辆离线', no_data: '暂无数据来源', identity_required: '身份未绑定' }; const missingProtocolTitle: Record = { GB32960: '缺 GB32960', JT808: '缺 JT808', YUTONG_MQTT: '缺 YUTONG_MQTT' }; type DashboardSnapshotRow = { section: string; item: string; value: string; detail: string; }; type FocusVehicleService = { label: string; lookupKey: string; protocol?: string; reason: string; statusLabel: string; statusColor: 'green' | 'orange' | 'red' | 'grey' | 'blue'; realtimeEvidence: string; historyEvidence: string; alertEvidence: string; statisticEvidence: string; issueType?: string; }; const dashboardSnapshotColumns: CsvColumn[] = [ { title: '模块', value: (row) => row.section }, { title: '指标', value: (row) => row.item }, { title: '值', value: (row) => row.value }, { title: '说明', value: (row) => row.detail } ]; function formatLag(value?: number | null) { return value == null ? '未接入' : value.toLocaleString(); } function formatCount(value?: number | null) { return value == null ? '0' : value.toLocaleString(); } function vehicleServiceOnlineText(serviceSummary: VehicleServiceSummary | null, summary: DashboardSummary | null) { const onlineVehicles = serviceSummary?.onlineVehicles ?? summary?.onlineVehicles; const totalVehicles = serviceSummary?.totalVehicles; if (onlineVehicles == null || totalVehicles == null) { return '未接入'; } return `${onlineVehicles.toLocaleString()} / ${totalVehicles.toLocaleString()} 在线`; } function rowServiceStatus(row: { serviceStatus?: { title: string; severity: string }; onlineSourceCount: number; sourceCount: number }) { 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: { onlineSourceCount: number; sourceCount: number }) { return `${row.onlineSourceCount}/${row.sourceCount} 来源在线`; } function hasValidCoordinate(row: VehicleRealtimeRow) { return Number.isFinite(row.longitude) && Number.isFinite(row.latitude) && row.longitude !== 0 && row.latitude !== 0; } function issueEvidenceDate(value?: string) { const match = String(value ?? '').match(/^(\d{4})-(\d{2})-(\d{2})/); return match ? `${match[1]}-${match[2]}-${match[3]}` : ''; } function nextDate(value: string) { const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value.trim()); if (!match) return ''; const date = new Date(Date.UTC(Number(match[1]), Number(match[2]) - 1, Number(match[3]) + 1)); return date.toISOString().slice(0, 10); } function todayDateString() { const date = new Date(); const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); return `${year}-${month}-${day}`; } function previousDateString(value: string) { const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value.trim()); if (!match) return value; const date = new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3]) - 1); const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); return `${year}-${month}-${day}`; } function dayRangeText(dateFrom?: string, dateTo?: string) { if (!dateFrom && !dateTo) return '未限定时间'; if (!dateFrom || !dateTo) return '单边时间窗'; const start = new Date(`${dateFrom}T00:00:00+08:00`).getTime(); const end = new Date(`${dateTo}T00:00:00+08:00`).getTime(); if (!Number.isFinite(start) || !Number.isFinite(end)) return '时间格式待确认'; const days = Math.max(0, Math.round((end - start) / 86400000)); return days <= 0 ? '同日或倒置时间窗' : `${days} 天窗口`; } function priorityIssueVehicleLabel(row: QualityIssueRow) { const identity = row.vin?.trim() && row.vin !== 'unknown' ? row.vin.trim() : row.phone?.trim(); return [row.plate?.trim(), identity].filter(Boolean).join(' / ') || row.sourceEndpoint || '-'; } function priorityIssueEvidenceFilters(row: QualityIssueRow) { const lookup = qualityIssueVehicleLookup(row); const dateFrom = issueEvidenceDate(row.lastSeen); return { keyword: lookup.key, protocol: row.protocol, ...(dateFrom ? { dateFrom, dateTo: nextDate(dateFrom) } : {}) }; } function appURL(hash: string) { return `${window.location.origin}${window.location.pathname}${hash}`; } async function copyText(value: string, label: string) { try { await navigator.clipboard.writeText(value); Toast.success(`已复制${label}`); } catch { Toast.error(`复制${label}失败`); } } function priorityIssueNotificationText(row: QualityIssueRow) { const lookup = qualityIssueVehicleLookup(row); const filters = priorityIssueEvidenceFilters(row); const rawFilters = { ...filters, tab: 'raw', includeFields: 'true' }; const priority = row.severity === 'error' ? 'P0' : 'P1'; return [ `【${priority} 告警通知】${qualityIssueLabel(row.issueType)}`, `车辆:${priorityIssueVehicleLabel(row)}`, `数据来源:${qualityProtocolLabel(row.protocol)}`, `问题:${qualityIssueLabel(row.issueType)}`, `最后时间:${row.lastSeen || '-'}`, `详情:${row.detail || '-'}`, `实时定位:${appURL(buildAppHash({ page: 'realtime', keyword: lookup.key, protocol: row.protocol }))}`, `轨迹证据:${appURL(buildAppHash({ page: 'history', keyword: filters.keyword, protocol: filters.protocol, filters }))}`, `原始记录:${appURL(buildAppHash({ page: 'history-query', keyword: rawFilters.keyword, protocol: rawFilters.protocol, filters: rawFilters }))}`, `车辆服务:${appURL(buildAppHash({ page: 'detail', keyword: lookup.key, protocol: row.protocol }))}`, `告警筛选:${appURL(buildAppHash({ page: 'alert-events', protocol: row.protocol, filters: { issueType: row.issueType } }))}` ].join('\n'); } function focusVehicleFromIssue(row: QualityIssueRow): FocusVehicleService { const lookup = qualityIssueVehicleLookup(row); const filters = priorityIssueEvidenceFilters(row); const label = priorityIssueVehicleLabel(row); return { label, lookupKey: lookup.key, protocol: row.protocol, reason: `${row.severity === 'error' ? 'P0' : 'P1'} ${qualityIssueLabel(row.issueType)}`, statusLabel: row.severity === 'error' ? '优先处置' : '持续跟踪', statusColor: row.severity === 'error' ? 'red' : 'orange', realtimeEvidence: `来源 ${qualityProtocolLabel(row.protocol)} / 最后 ${row.lastSeen || '-'}`, historyEvidence: filters.dateFrom ? `${filters.dateFrom} 至 ${filters.dateTo}` : '待确认时间窗', alertEvidence: row.detail || qualityIssueLabel(row.issueType), statisticEvidence: '里程统计需回溯车辆口径', issueType: row.issueType }; } function focusVehicleFromRealtime(row: VehicleRealtimeRow): FocusVehicleService { const status = rowServiceStatus(row); const label = [row.plate?.trim(), row.vin?.trim()].filter(Boolean).join(' / ') || row.phone || '-'; const location = hasValidCoordinate(row) ? `${row.longitude.toFixed(6)}, ${row.latitude.toFixed(6)}` : '无有效坐标'; return { label, lookupKey: row.vin || row.phone || row.plate, protocol: row.primaryProtocol, reason: row.online ? '最新在线车辆' : '最新车辆', statusLabel: status.label, statusColor: status.color, realtimeEvidence: `${row.onlineSourceCount}/${row.sourceCount} 来源在线 / ${row.lastSeen || '-'}`, historyEvidence: location, alertEvidence: row.onlineSourceCount < row.sourceCount ? '来源不完整,建议检查缺失协议' : '暂无高优先级告警', statisticEvidence: `速度 ${formatCount(row.speedKmh)} km/h / 里程 ${formatCount(row.totalMileageKm)} km` }; } function sourceConsistencyAction(row: VehicleCoverageRow, onFilter: (filters: Record) => void) { const consistency = row.sourceConsistency; if (!consistency) { return 未诊断; } const color = consistency.severity === 'ok' ? 'green' as const : consistency.severity === 'error' ? 'red' as const : 'orange' as const; const label = consistency.title || consistency.status; if ((consistency.missingProtocols ?? []).length > 0) { return ( ); } return {label}; } export function Dashboard({ onOpenVehicle, onOpenQuality, onOpenMap, onOpenRealtime, onOpenVehicles, onOpenHistory, onOpenMileage }: { onOpenVehicle: (vin: string, protocol?: string) => void; onOpenQuality: (filters?: Record) => void; onOpenMap: (filters?: Record) => void; onOpenRealtime: (filters?: Record) => void; onOpenVehicles: (filters?: Record) => void; onOpenHistory: (filters?: Record) => void; onOpenMileage: (filters?: Record) => void; }) { const [summary, setSummary] = useState(null); const [serviceSummary, setServiceSummary] = useState(null); const [coverage, setCoverage] = useState([]); const [locations, setLocations] = useState([]); const [qualityIssues, setQualityIssues] = useState([]); const [opsHealth, setOpsHealth] = useState(null); const [timeMonitorFilters, setTimeMonitorFilters] = useState>(() => { const today = todayDateString(); return { dateFrom: previousDateString(today), dateTo: today }; }); const [loading, setLoading] = useState(true); const [coverageLoading, setCoverageLoading] = useState(false); const [coverageServiceStatusTitle, setCoverageServiceStatusTitle] = useState(''); const [coverageFilters, setCoverageFilters] = useState>({}); const localAMapConfigured = isAMapConfigured(); const amapConfigured = opsHealth?.runtime?.amapWebJsConfigured ?? localAMapConfigured; const amapApiConfigured = opsHealth?.runtime?.amapApiConfigured ?? false; const amapSecurityProxyEnabled = opsHealth?.runtime?.amapSecurityProxyEnabled ?? false; const amapSecurityCodeExposed = opsHealth?.runtime?.amapSecurityCodeExposed ?? false; const loadCoverage = (values?: Record) => { const nextValues = values ?? {}; setCoverageLoading(true); setCoverageFilters(nextValues); const scopeTitle = [ nextValues.serviceStatus ? serviceStatusTitle[nextValues.serviceStatus] ?? nextValues.serviceStatus : '', nextValues.missingProtocol ? missingProtocolTitle[nextValues.missingProtocol] ?? `缺 ${nextValues.missingProtocol}` : '' ].filter(Boolean).join(' / '); setCoverageServiceStatusTitle(scopeTitle); const params = new URLSearchParams({ limit: '8' }); if (nextValues.keyword) params.set('keyword', nextValues.keyword); if (nextValues.coverage) params.set('coverage', nextValues.coverage); if (nextValues.missingProtocol) params.set('missingProtocol', nextValues.missingProtocol); if (nextValues.online) params.set('online', nextValues.online); if (nextValues.bindingStatus) params.set('bindingStatus', nextValues.bindingStatus); if (nextValues.serviceStatus) params.set('serviceStatus', nextValues.serviceStatus); api.vehicleCoverage(params) .then((page) => setCoverage(page.items)) .catch((error: Error) => Toast.error(error.message)) .finally(() => setCoverageLoading(false)); }; useEffect(() => { const tasks = [ api.dashboardSummary().then(setSummary), api.vehicleServiceSummary().then(setServiceSummary), api.vehicleCoverage(new URLSearchParams({ limit: '8' })).then((page) => setCoverage(page.items)), api.vehicleRealtime(new URLSearchParams({ limit: '8' })).then((page) => setLocations(page.items)), api.alertEvents(new URLSearchParams({ limit: '5' })).then((page) => setQualityIssues(page.items)), api.opsHealth().then(setOpsHealth) ]; Promise.allSettled(tasks) .then((results) => { const failed = results.find((result) => result.status === 'rejected'); if (failed?.status === 'rejected') { const reason = failed.reason; Toast.error(reason instanceof Error ? reason.message : '总览数据加载失败'); } }) .finally(() => setLoading(false)); }, []); const serviceActionQueue = useMemo; detail: string }>>(() => { const items: Array<{ label: string; count: number; filters: Record; detail: string }> = []; if ((serviceSummary?.noDataVehicles ?? 0) > 0) { items.push({ label: '确认平台转发', count: serviceSummary?.noDataVehicles ?? 0, filters: { serviceStatus: 'no_data' }, detail: '车辆没有形成任何来源证据,优先确认上游平台是否持续转发。' }); } if ((serviceSummary?.identityRequiredVehicles ?? 0) > 0) { items.push({ label: '维护身份绑定', count: serviceSummary?.identityRequiredVehicles ?? 0, filters: { serviceStatus: 'identity_required' }, detail: '已有数据但无法稳定归并到 VIN,会影响车辆服务聚合。' }); } if ((serviceSummary?.archiveIncompleteVehicles ?? 0) > 0) { items.push({ label: '完善车辆档案', count: serviceSummary?.archiveIncompleteVehicles ?? 0, filters: { archiveStatus: 'incomplete' }, detail: '车辆缺少车牌、手机号或 OEM 等基础档案,影响后续运营查询和治理。' }); } for (const field of serviceSummary?.archiveMissingFields ?? []) { if (field.count <= 0) continue; items.push({ label: `补齐${field.title}`, count: field.count, filters: { archiveMissing: field.field }, detail: `${field.title}会影响车辆档案检索、绑定确认和运营侧筛选。` }); } for (const source of serviceSummary?.missingSources ?? []) { if (source.count <= 0) continue; items.push({ label: `补齐 ${source.protocol} 来源`, count: source.count, filters: { serviceStatus: 'degraded', missingProtocol: source.protocol }, detail: `${source.protocol} 来源缺失会降低跨来源定位、里程和实时判断可信度。` }); } return items; }, [serviceSummary]); const commandOnlineCount = locations.filter((row) => row.online).length; const commandLocatedCount = locations.filter(hasValidCoordinate).length; const commandDegradedCount = locations.filter((row) => row.onlineSourceCount <= 0 || row.onlineSourceCount < row.sourceCount).length; const kpis: Array<{ label: string; value: string; filters: Record }> = [ { label: '总车辆', value: formatCount(serviceSummary?.totalVehicles), filters: {} }, { label: '在线车辆', value: formatCount(serviceSummary?.onlineVehicles ?? summary?.onlineVehicles), filters: { online: 'online' } }, { label: '今日活跃', value: formatCount(summary?.activeToday), filters: { online: 'online' } }, { label: '有效定位', value: commandLocatedCount.toLocaleString(), filters: { online: 'online' } }, { label: '告警车辆', value: formatCount(summary?.issueVehicles), filters: { serviceStatus: 'degraded' } }, { label: '今日数据', value: formatCount(summary?.frameToday), filters: {} }, { label: '档案不完整', value: formatCount(serviceSummary?.archiveIncompleteVehicles), filters: { archiveStatus: 'incomplete' } } ]; const highPriorityIssue = qualityIssues.find((item) => item.severity === 'error') ?? qualityIssues[0]; const highPriorityLookup = highPriorityIssue ? qualityIssueVehicleLookup(highPriorityIssue) : undefined; const highPriorityEvidenceFilters = highPriorityIssue ? priorityIssueEvidenceFilters(highPriorityIssue) : undefined; const focusVehicle = highPriorityIssue ? focusVehicleFromIssue(highPriorityIssue) : locations[0] ? focusVehicleFromRealtime(locations[0]) : undefined; const unhealthyLinkCount = (summary?.linkHealth ?? []).filter((item) => item.status !== 'ok').length; const commandMapPoints: VehicleMapPoint[] = locations.map((row, index) => ({ id: row.vin || `${row.primaryProtocol || 'source'}-${index}`, label: row.plate || row.vin || 'unknown', longitude: row.longitude, latitude: row.latitude, online: row.online, title: `${row.plate || row.vin || '-'} ${row.primaryProtocol || ''} ${row.lastSeen || ''}` })); const customerHeroActions = [ { title: '车辆地图', value: `${commandLocatedCount.toLocaleString()} 辆有定位`, detail: '按车查看实时位置、在线状态、速度和最新上报时间。', action: '进入地图', onClick: () => onOpenMap({ online: 'online' }) }, { title: '轨迹回放', value: `${formatCount(summary?.activeToday)} 今日活跃`, detail: '选择车辆和时间范围,回放历史路线并核对断点。', action: '回放轨迹', onClick: () => onOpenHistory() }, { title: '里程统计', value: `${formatCount(serviceSummary?.totalVehicles)} 辆可统计`, detail: '按自定义时间查询区间里程、日里程和异常记录。', action: '查看统计', onClick: () => onOpenMileage({}) }, { title: '历史数据', value: `${formatCount(summary?.frameToday)} 今日数据`, detail: '按车辆、时间和字段裁剪查询历史位置与原始记录。', action: '查询导出', onClick: () => onOpenHistory({ tab: 'raw', includeFields: 'true' }) } ]; const customerJourneySteps = [ { step: '01', title: '定位车辆', detail: '先在地图上确认车辆是否在线、坐标是否可信。', action: '车辆地图', onClick: () => onOpenMap({ online: 'online' }) }, { step: '02', title: '查看状态', detail: '进入实时监控查看速度、SOC、里程和最新上报。', action: '实时监控', onClick: () => onOpenRealtime({ online: 'online' }) }, { step: '03', title: '复盘轨迹', detail: '按自定义时间窗回放位置、速度和里程断点。', action: '轨迹回放', onClick: () => onOpenHistory() }, { step: '04', title: '统计里程', detail: '用同一时间窗核对区间里程和每日统计。', action: '里程统计', onClick: () => onOpenMileage({}) }, { step: '05', title: '查询导出', detail: '导出历史位置、原始记录、字段明细和告警证据。', action: '历史数据', onClick: () => openTimeMonitorRaw() } ]; const customerHealthItems = [ { label: '在线车辆', value: formatCount(serviceSummary?.onlineVehicles ?? summary?.onlineVehicles), detail: vehicleServiceOnlineText(serviceSummary, summary), color: 'green' as const, onClick: () => onOpenVehicles({ online: 'online' }) }, { label: '有效定位', value: commandLocatedCount.toLocaleString(), detail: `当前抽样 ${locations.length.toLocaleString()} 辆,定位可用于地图和回放。`, color: commandLocatedCount > 0 ? 'blue' as const : 'orange' as const, onClick: () => onOpenMap({ online: 'online' }) }, { label: '需要关注', value: formatCount(summary?.issueVehicles), detail: highPriorityIssue ? `${qualityIssueLabel(highPriorityIssue.issueType)} / ${priorityIssueVehicleLabel(highPriorityIssue)}` : '暂无高优先级告警。', color: (summary?.issueVehicles ?? 0) > 0 ? 'orange' as const : 'green' as const, onClick: () => onOpenQuality() }, { label: '时间范围监控', value: formatCount(summary?.activeToday), detail: '支持按任意时间窗查询轨迹、历史数据、里程和导出。', color: 'blue' as const, onClick: () => onOpenHistory() } ]; const taskTimeMonitorSummary = (() => { const vehicle = String(timeMonitorFilters.keyword ?? '').trim() || '全部车辆'; const source = String(timeMonitorFilters.protocol ?? '').trim() || '全部数据通道'; const dateFrom = String(timeMonitorFilters.dateFrom ?? '').trim(); const dateTo = String(timeMonitorFilters.dateTo ?? '').trim(); const range = dateFrom || dateTo ? `${dateFrom || '-'} 至 ${dateTo || '-'}` : '全部时间'; return `${vehicle} / ${source} / ${range}`; })(); const customerTaskBoard = [ { title: '先看全域在线', value: `${formatCount(serviceSummary?.onlineVehicles ?? summary?.onlineVehicles)} 在线`, detail: `地图有效定位 ${commandLocatedCount.toLocaleString()} 辆,优先确认车辆是否仍在上报。`, color: 'green' as const, primaryAction: '打开实时地图', secondaryAction: '查看在线车辆', onPrimary: () => onOpenMap({ online: 'online' }), onSecondary: () => onOpenRealtime({ online: 'online' }) }, { title: '再看重点车辆', value: focusVehicle?.label ?? '暂无重点车辆', detail: focusVehicle ? `${focusVehicle.reason},${focusVehicle.realtimeEvidence}` : '当前没有高优先级车辆,保持日常巡检。', color: focusVehicle?.statusColor ?? 'green' as const, primaryAction: '车辆服务', secondaryAction: '复制处置卡', onPrimary: () => focusVehicle?.lookupKey && onOpenVehicle(focusVehicle.lookupKey, focusVehicle.protocol), onSecondary: () => copyFocusVehicleService(), disabled: !focusVehicle?.lookupKey }, { title: '按时间窗复盘', value: taskTimeMonitorSummary, detail: '同一时间窗贯穿轨迹、里程、原始记录和告警,不需要跨页面重复输入。', color: 'blue' as const, primaryAction: '轨迹回放', secondaryAction: '里程统计', onPrimary: () => openTimeMonitorHistory(), onSecondary: () => openTimeMonitorMileage() }, { title: '导出证据数据', value: `${formatCount(summary?.frameToday)} 今日数据`, detail: '面向 BI、客户复盘和异常定位,按车辆、时间、字段裁剪导出。', color: 'blue' as const, primaryAction: '历史导出', secondaryAction: '导出概览', onPrimary: () => openTimeMonitorRaw(), onSecondary: () => exportDashboardSnapshot() }, { title: '闭环告警通知', value: `${formatCount(summary?.issueVehicles)} 告警车辆`, detail: highPriorityIssue ? `${qualityIssueLabel(highPriorityIssue.issueType)} / ${priorityIssueVehicleLabel(highPriorityIssue)}` : '暂无高优先级告警。', color: (summary?.issueVehicles ?? 0) > 0 ? 'orange' as const : 'green' as const, primaryAction: '告警事件', secondaryAction: '复制交接', onPrimary: () => onOpenQuality(highPriorityIssue?.issueType ? { issueType: highPriorityIssue.issueType } : {}), onSecondary: () => copyOperationsHandoff() } ]; const capabilities = [ { title: '实时地图', status: `在线态势 ${formatCount(serviceSummary?.onlineVehicles ?? summary?.onlineVehicles)}`, color: 'green' as const, description: '以高德地图为主视角查看车辆在线分布、定位有效性、来源一致性和地图接入状态。', action: '打开地图', onClick: () => onOpenMap({ online: 'online' }) }, { title: '实时监控', status: `在线 ${formatCount(serviceSummary?.onlineVehicles ?? summary?.onlineVehicles)}`, color: 'green' as const, description: '按车辆聚合最新位置、在线状态和多来源实时字段,支持实时地图查看。', action: '查看在线车辆', onClick: () => onOpenRealtime({ online: 'online' }) }, { title: '轨迹回放', status: `有效定位 ${formatCount(commandLocatedCount)}`, color: commandLocatedCount > 0 ? 'blue' as const : 'grey' as const, description: '围绕车辆回放历史位置轨迹,结合高德线路和原始记录定位异常。', action: '打开轨迹', onClick: () => onOpenHistory() }, { title: '历史数据查询', status: `今日帧 ${formatCount(summary?.frameToday)}`, color: 'blue' as const, description: '查询位置历史和原始记录字段,为车辆问题复盘提供依据。', action: '查询历史', onClick: () => onOpenHistory({ tab: 'raw' }) }, { title: '告警事件触发与通知', status: `告警 ${formatCount(summary?.issueVehicles)}`, color: (summary?.issueVehicles ?? 0) > 0 ? 'orange' as const : 'green' as const, description: '围绕断链、无来源、VIN 缺失、字段缺失形成告警和通知闭环。', action: '查看告警', onClick: () => onOpenQuality() }, { title: '里程统计', status: `统计车辆 ${formatCount(serviceSummary?.totalVehicles)}`, color: 'green' as const, description: '提供里程等运营统计,保证区间总值与每日统计口径闭合。', action: '查看统计', onClick: () => onOpenMileage() } ]; const vehicleServicePrinciples = [ { title: '一车一档', value: formatCount(serviceSummary?.totalVehicles), detail: '以 VIN 为主对象沉淀车牌、手机号、OEM、绑定状态和来源覆盖。' }, { title: '一车一实时', value: formatCount(serviceSummary?.onlineVehicles ?? summary?.onlineVehicles), detail: '实时状态合并位置、在线、速度、SOC、里程和来源新鲜度。' }, { title: '一车一轨迹', value: formatCount(commandLocatedCount), detail: '轨迹回放围绕车辆展开,协议来源只作为证据过滤条件。' }, { title: '一车一统计', value: formatCount(serviceSummary?.totalVehicles), detail: '里程等运营指标按车辆口径闭合,再回溯到来源证据。' } ]; const workflowSteps = [ { title: '接入巡检', value: unhealthyLinkCount > 0 ? `${unhealthyLinkCount.toLocaleString()} 项关注` : '链路正常', color: unhealthyLinkCount > 0 ? 'orange' as const : 'green' as const, detail: '检查平台转发、消息队列、缓存和存储等关键链路。', action: '查看告警', onClick: () => onOpenQuality() }, { title: '实时态势', value: `${formatCount(serviceSummary?.onlineVehicles ?? summary?.onlineVehicles)} 在线`, color: 'green' as const, detail: '确认车辆是否在线、是否有有效坐标以及来源是否完整。', action: '实时监控', onClick: () => onOpenRealtime({ online: 'online' }) }, { title: '轨迹复盘', value: `${formatCount(summary?.activeToday)} 活跃`, color: 'blue' as const, detail: '按车辆进入历史轨迹,复盘位置、速度、里程和断点。', action: '轨迹回放', onClick: () => onOpenHistory() }, { title: '历史证据', value: `${formatCount(summary?.frameToday)} 帧`, color: 'blue' as const, detail: '查询原始记录和字段明细,形成可追溯的数据依据。', action: '历史查询', onClick: () => onOpenHistory({ tab: 'raw' }) }, { title: '告警事件', value: `${formatCount(summary?.issueVehicles)} 车辆`, color: (summary?.issueVehicles ?? 0) > 0 ? 'orange' as const : 'green' as const, detail: '把断链、缺 VIN、字段缺失等问题进入通知和处置队列。', action: '告警事件', onClick: () => onOpenQuality() }, { title: '统计复核', value: `${formatCount(serviceSummary?.totalVehicles)} 车辆`, color: 'green' as const, detail: '核对里程统计口径,保证区间统计和每日统计闭合。', action: '里程统计', onClick: () => onOpenMileage() } ]; const mapReadiness = [ { title: '高德 Web JS', value: amapConfigured ? '已配置' : '待配置', color: amapConfigured ? 'green' as const : 'orange' as const, detail: amapConfigured ? '实时监控和轨迹回放可加载高德底图。' : '缺少 Web JS Key 时降级为坐标预览。' }, { title: '安全代理', value: amapSecurityProxyEnabled ? '已启用' : '未启用', color: amapSecurityProxyEnabled ? 'green' as const : 'orange' as const, detail: amapSecurityProxyEnabled ? '安全码由服务端代理追加,不下发到浏览器。' : '生产环境建议启用 /_AMapService。' }, { title: '服务端 API', value: amapApiConfigured ? '已配置' : '待配置', color: amapApiConfigured ? 'green' as const : 'orange' as const, detail: amapApiConfigured ? '逆地理、路线、围栏等服务端地图能力可扩展。' : '后端地图服务会降级。' }, { title: '安全码暴露', value: amapSecurityCodeExposed ? '需处理' : '未暴露', color: amapSecurityCodeExposed ? 'red' as const : 'green' as const, detail: amapSecurityCodeExposed ? '浏览器端可见安全码,需要切换到安全代理。' : '符合生产密钥收敛要求。' } ]; const mapWorkItems = [ { title: '实时监控', value: `${commandLocatedCount.toLocaleString()} 个有效坐标`, detail: '在线车辆进入地图态势,点击车辆后继续查看车辆服务和实时字段。', action: '实时地图', onClick: () => onOpenMap({ online: 'online' }) }, { title: '轨迹回放', value: `${formatCount(summary?.activeToday)} 今日活跃`, detail: '按车辆和时间窗回放历史位置,支持打开高德线路和原始记录。', action: '轨迹回放', onClick: () => onOpenHistory() }, { title: '历史证据', value: `${formatCount(summary?.frameToday)} 今日帧`, detail: '地图点位异常时直接回查位置历史、解析字段和原始帧。', action: '历史查询', onClick: () => onOpenHistory({ tab: 'raw', includeFields: 'true' }) }, { title: '告警通知', value: `${formatCount(summary?.issueVehicles)} 告警车辆`, detail: '断链、无来源、坐标异常进入告警队列并触发通知闭环。', action: '告警事件', onClick: () => onOpenQuality() } ]; const operationWorkbench = [ { title: '实时监控', value: `${formatCount(serviceSummary?.onlineVehicles ?? summary?.onlineVehicles)} 在线`, meta: `有效定位 ${commandLocatedCount.toLocaleString()}`, color: 'green' as const, detail: '面向值班人员确认车辆是否在线、坐标是否有效、来源是否完整。', actions: [ { label: '打开实时地图', onClick: () => onOpenMap({ online: 'online' }) }, { label: '查看在线车辆', onClick: () => onOpenRealtime({ online: 'online' }) } ] }, { title: '轨迹回放', value: `${formatCount(summary?.activeToday)} 今日活跃`, meta: '高德轨迹证据', color: 'blue' as const, detail: '按车辆回看位置、速度、里程和断点,用于定位异常复盘。', actions: [ { label: '打开轨迹回放', onClick: () => onOpenHistory() }, { label: '查历史明细', onClick: () => onOpenHistory({ tab: 'raw', includeFields: 'true' }) } ] }, { title: '历史数据查询', value: `${formatCount(summary?.frameToday)} 今日帧`, meta: '字段裁剪与导出', color: 'blue' as const, detail: '查询位置历史、历史明细和字段明细,为车辆服务提供可追溯依据。', actions: [ { label: '查询历史数据', onClick: () => onOpenHistory({ tab: 'location' }) }, { label: '查询解析字段', onClick: () => onOpenHistory({ tab: 'raw', includeFields: 'true' }) } ] }, { title: '告警事件与通知', value: `${formatCount(summary?.issueVehicles)} 告警车辆`, meta: highPriorityIssue ? `${highPriorityIssue.severity === 'error' ? 'P0' : 'P1'} ${qualityIssueLabel(highPriorityIssue.issueType)}` : '暂无高优先级', color: (summary?.issueVehicles ?? 0) > 0 ? 'orange' as const : 'green' as const, detail: '把断链、无来源、VIN 缺失、字段缺失转成可通知、可闭环的事件。', actions: [ { label: '查看告警事件', onClick: () => onOpenQuality() }, { label: '处理最高优先级', onClick: () => onOpenQuality(highPriorityIssue?.issueType ? { issueType: highPriorityIssue.issueType } : {}) } ] }, { title: '里程统计', value: `${formatCount(serviceSummary?.totalVehicles)} 车辆口径`, meta: '区间闭合复核', color: 'green' as const, detail: '里程等指标先按车辆口径闭合,再回溯轨迹和原始记录。', actions: [ { label: '打开里程统计', onClick: () => onOpenMileage() }, { label: '查看车辆中心', onClick: () => onOpenVehicles({}) } ] } ]; const timeMonitorScope = (values = timeMonitorFilters) => { const normalized: Record = {}; for (const key of ['keyword', 'protocol', 'dateFrom', 'dateTo'] as const) { const value = String(values[key] ?? '').trim(); if (value) { normalized[key] = value; } } return normalized; }; const openTimeMonitorHistory = () => { onOpenHistory(timeMonitorScope()); }; const openTimeMonitorMileage = () => { onOpenMileage(timeMonitorScope()); }; const openTimeMonitorRaw = () => { onOpenHistory({ ...timeMonitorScope(), tab: 'raw', includeFields: 'true' }); }; const openTimeMonitorAlerts = () => { const scope = timeMonitorScope(); onOpenQuality({ ...(scope.keyword ? { keyword: scope.keyword } : {}), ...(scope.protocol ? { protocol: scope.protocol } : {}) }); }; const timeMonitorSummary = (() => { const scope = timeMonitorScope(); const vehicle = scope.keyword || '全部车辆'; const source = scope.protocol || '全部数据通道'; const range = scope.dateFrom || scope.dateTo ? `${scope.dateFrom || '-'} 至 ${scope.dateTo || '-'}` : '全部时间'; return `${vehicle} / ${source} / ${range}`; })(); const timeMonitorReviewItems = [ { label: '车辆范围', value: timeMonitorScope().keyword || '全部车辆', detail: timeMonitorScope().protocol || '全部数据通道', color: timeMonitorScope().keyword ? 'green' as const : 'blue' as const }, { label: '复盘时间', value: timeMonitorScope().dateFrom || timeMonitorScope().dateTo ? `${timeMonitorScope().dateFrom || '-'} 至 ${timeMonitorScope().dateTo || '-'}` : '全部时间', detail: '同步带入轨迹、里程、历史证据和告警', color: 'blue' as const }, { label: '轨迹证据', value: `${formatCount(summary?.activeToday)} 活跃`, detail: '位置、速度、里程断点同窗复核', color: 'blue' as const }, { label: '统计证据', value: `${formatCount(serviceSummary?.totalVehicles)} 车辆`, detail: '区间里程、日统计和异常点闭合', color: 'green' as const }, { label: '风险事件', value: `${formatCount(summary?.issueVehicles)} 告警`, detail: highPriorityIssue ? qualityIssueLabel(highPriorityIssue.issueType) : '暂无高优先级', color: (summary?.issueVehicles ?? 0) > 0 ? 'orange' as const : 'green' as const } ]; const timeMonitorScopeValue = timeMonitorScope(); const timeMonitorWindowLabel = dayRangeText(timeMonitorScopeValue.dateFrom, timeMonitorScopeValue.dateTo); const timeMonitorChecklist = [ { label: '范围可解释', value: timeMonitorScopeValue.keyword ? '单车聚焦' : '全域巡检', detail: `${timeMonitorScopeValue.protocol || '全部数据通道'} / ${timeMonitorWindowLabel}`, color: timeMonitorScopeValue.dateFrom && timeMonitorScopeValue.dateTo ? 'green' as const : 'orange' as const }, { label: '轨迹可复盘', value: `${formatCount(summary?.activeToday)} 活跃车辆`, detail: '可进入轨迹页核对位置、速度、里程断点和停驶片段。', color: (summary?.activeToday ?? 0) > 0 ? 'green' as const : 'orange' as const }, { label: '数据可导出', value: `${formatCount(summary?.frameToday)} 今日数据`, detail: '历史查询默认带原始记录与解析字段,支持按字段缩小导出体积。', color: (summary?.frameToday ?? 0) > 0 ? 'blue' as const : 'orange' as const }, { label: '异常可闭环', value: `${formatCount(summary?.issueVehicles)} 告警车辆`, detail: highPriorityIssue ? `${qualityIssueLabel(highPriorityIssue.issueType)} / ${priorityIssueVehicleLabel(highPriorityIssue)}` : '当前没有高优先级告警。', color: (summary?.issueVehicles ?? 0) > 0 ? 'orange' as const : 'green' as const } ]; const copyTimeMonitorReviewPackage = () => { const scope = timeMonitorScope(); const rawFilters = { ...scope, tab: 'raw', includeFields: 'true' }; const lines = [ '【客户时间窗复盘包】', `范围:${timeMonitorSummary}`, `时间窗判定:${dayRangeText(scope.dateFrom, scope.dateTo)}`, '平台能力:实时地图 / 轨迹回放 / 里程统计 / 历史数据查询 / 告警通知', `车辆范围:${scope.keyword || '全部车辆'}`, `数据通道:${scope.protocol || '全部数据通道'}`, `时间范围:${scope.dateFrom || '-'} 至 ${scope.dateTo || '-'}`, `轨迹证据:今日活跃 ${formatCount(summary?.activeToday)},用于回放位置、速度、里程断点`, `统计证据:车辆口径 ${formatCount(serviceSummary?.totalVehicles)},用于区间里程和日统计闭合`, `历史证据:今日数据 ${formatCount(summary?.frameToday)},用于原始记录与解析字段复核`, `告警事件:${formatCount(summary?.issueVehicles)} 辆车存在告警`, '交付核对:', ...timeMonitorChecklist.map((item, index) => `${index + 1}. ${item.label}:${item.value};${item.detail}`), `轨迹回放:${appURL(buildAppHash({ page: 'history', keyword: scope.keyword, protocol: scope.protocol, filters: scope }))}`, `里程统计:${appURL(buildAppHash({ page: 'mileage', keyword: scope.keyword, protocol: scope.protocol, filters: scope }))}`, `历史数据查询:${appURL(buildAppHash({ page: 'history-query', keyword: scope.keyword, protocol: scope.protocol, filters: rawFilters }))}`, `告警通知:${appURL(buildAppHash({ page: 'alert-events', keyword: scope.keyword, protocol: scope.protocol, filters: scope }))}`, `实时地图:${appURL(buildAppHash({ page: 'map', keyword: scope.keyword, protocol: scope.protocol, filters: scope }))}` ]; copyText(lines.join('\n'), '时间窗复盘包'); }; const timeMonitorWorkbench = [ { label: '轨迹回放', value: `${formatCount(summary?.activeToday)} 活跃`, detail: '查看时间窗内车辆位置、速度、里程和断点。', action: '打开轨迹', color: 'blue' as const, onClick: openTimeMonitorHistory }, { label: '里程核对', value: `${formatCount(serviceSummary?.totalVehicles)} 车辆`, detail: '用同一范围核对区间里程和日统计闭合。', action: '核对里程', color: 'green' as const, onClick: openTimeMonitorMileage }, { label: '历史证据', value: `${formatCount(summary?.frameToday)} 帧`, detail: '按车辆、时间和字段裁剪导出原始记录与解析字段。', action: '导出证据', color: 'blue' as const, onClick: openTimeMonitorRaw }, { label: '告警复盘', value: `${formatCount(summary?.issueVehicles)} 告警`, detail: '查看该车辆或来源在当前范围内的风险事件。', action: '查看告警', color: (summary?.issueVehicles ?? 0) > 0 ? 'orange' as const : 'green' as const, onClick: openTimeMonitorAlerts } ]; const dataFlowStages = [ { stage: '01', title: '车辆接入', owner: '接入网关', evidence: `32960 / 808 / MQTT,连接 ${formatCount(opsHealth?.activeConnections)}`, detail: '只接收真实实时数据帧和必要鉴权注册帧,接入来源最终归并到车辆服务。', action: '运维质量', onClick: () => onOpenQuality() }, { stage: '02', title: '统一解析', owner: '字段映射', evidence: `今日帧 ${formatCount(summary?.frameToday)},Kafka Lag ${formatLag(summary?.kafkaLag)}`, detail: '一次解析生成扁平字段、原始证据和车辆身份关联,避免三处重复解析。', action: '历史字段', onClick: () => onOpenHistory({ tab: 'raw', includeFields: 'true' }) }, { stage: '03', title: '实时投影', owner: 'Redis KV', evidence: `在线 Key ${formatCount(opsHealth?.redisOnlineKeys)},在线车辆 ${formatCount(serviceSummary?.onlineVehicles ?? summary?.onlineVehicles)}`, detail: '快速写入车辆最新状态、在线 TTL 和全量协议字段,支撑 0-1 秒监控。', action: '实时监控', onClick: () => onOpenRealtime({ online: 'online' }) }, { stage: '04', title: '车辆归并', owner: 'Vehicle Service', evidence: `多源 ${formatCount(serviceSummary?.multiSourceVehicles)},单源 ${formatCount(serviceSummary?.singleSourceVehicles)}`, detail: '以 VIN 合并来源证据,车牌、手机号和 OEM 只是车辆服务的检索键。', action: '车辆中心', onClick: () => onOpenVehicles({}) }, { stage: '05', title: '历史证据', owner: 'TDengine / MySQL', evidence: `轨迹 ${formatCount(summary?.activeToday)} 活跃,原始记录 ${formatCount(summary?.frameToday)} 帧`, detail: '位置、原始记录和解析字段可分页查询,并能回跳轨迹、车辆服务和告警。', action: '轨迹回放', onClick: () => onOpenHistory() }, { stage: '06', title: '运营闭环', owner: '告警 / 统计', evidence: `告警 ${formatCount(summary?.issueVehicles)},统计车辆 ${formatCount(serviceSummary?.totalVehicles)}`, detail: '断链、缺字段、里程异常和离线时长进入通知、统计和复核工作流。', action: '里程统计', onClick: () => onOpenMileage() } ]; const scenarioNavigation = [ { title: '实时监控', objective: '确认车辆是否在线、位置是否可信、必要接入来源是否齐全。', evidence: `Redis 在线态 / 最新定位 / 有效坐标 ${commandLocatedCount.toLocaleString()}`, sla: '目标 0-1 秒内进入实时视图', primaryAction: '实时监控', secondaryAction: '地图态势', onPrimary: () => onOpenRealtime({ online: 'online' }), onSecondary: () => onOpenMap({ online: 'online' }) }, { title: '轨迹回放', objective: '按车辆复盘位置、速度、里程和断点,定位平台转发或车辆异常。', evidence: `高德轨迹底座 / 今日活跃 ${formatCount(summary?.activeToday)}`, sla: '支持按车辆和时间窗快速回放', primaryAction: '轨迹回放', secondaryAction: '原始记录', onPrimary: () => onOpenHistory(), onSecondary: () => onOpenHistory({ tab: 'raw', includeFields: 'true' }) }, { title: '历史数据查询', objective: '围绕车辆查询位置、原始记录和字段明细,给 BI、运维和业务复盘提供依据。', evidence: `今日数据 ${formatCount(summary?.frameToday)} / 支持字段裁剪与导出`, sla: '优先返回必要字段,避免大 JSON 拖慢查询', primaryAction: '历史查询', secondaryAction: '字段证据', onPrimary: () => onOpenHistory({ tab: 'location' }), onSecondary: () => onOpenHistory({ tab: 'raw', includeFields: 'true' }) }, { title: '告警事件触发和通知', objective: '把断链、无来源、VIN 缺失、字段缺失转换为可通知、可闭环事件。', evidence: `告警车辆 ${formatCount(summary?.issueVehicles)} / 最高优先级 ${highPriorityIssue ? qualityIssueLabel(highPriorityIssue.issueType) : '暂无'}`, sla: 'P0 进入通知队列,超时升级', primaryAction: '告警事件', secondaryAction: '通知规则', onPrimary: () => onOpenQuality(), onSecondary: () => onOpenQuality(highPriorityIssue?.issueType ? { issueType: highPriorityIssue.issueType } : {}) }, { title: '里程统计', objective: '按车辆口径查询里程等指标,保证区间统计和日统计可闭合。', evidence: `车辆口径 ${formatCount(serviceSummary?.totalVehicles)} / 区间统计可追溯轨迹证据`, sla: '统计值必须能追溯轨迹和原始记录', primaryAction: '里程统计', secondaryAction: '车辆中心', onPrimary: () => onOpenMileage(), onSecondary: () => onOpenVehicles({}) } ]; const copyOperationsHandoff = () => { const unhealthyLinks = (summary?.linkHealth ?? []).filter((item) => item.status !== 'ok'); const priorityAction = serviceActionQueue[0]; const highPriorityLookupKey = highPriorityLookup?.key; const highPriorityFilters = highPriorityEvidenceFilters; const lines = [ '【车辆服务中台运营交接摘要】', `在线车辆:${formatCount(serviceSummary?.onlineVehicles ?? summary?.onlineVehicles)} / ${formatCount(serviceSummary?.totalVehicles)}`, `今日活跃:${formatCount(summary?.activeToday)}`, `今日帧量:${formatCount(summary?.frameToday)}`, `实时地图有效定位:${commandLocatedCount.toLocaleString()}`, `告警车辆:${formatCount(summary?.issueVehicles)}`, `高德地图:Web JS ${amapConfigured ? '已配置' : '待配置'} / 服务端 API ${amapApiConfigured ? '已配置' : '待配置'} / 安全代理 ${amapSecurityProxyEnabled ? '已启用' : '未启用'}`, `优先动作:${priorityAction ? `${priorityAction.label} ${priorityAction.count.toLocaleString()}辆 - ${priorityAction.detail}` : '暂无待办'}`, highPriorityIssue ? `最高告警:${highPriorityIssue.severity === 'error' ? 'P0' : 'P1'} ${qualityIssueLabel(highPriorityIssue.issueType)} / ${priorityIssueVehicleLabel(highPriorityIssue)} / ${highPriorityIssue.lastSeen || '-'}` : '最高告警:暂无', highPriorityIssue ? `告警详情:${highPriorityIssue.detail || '-'}` : '', `实时监控:${appURL(buildAppHash({ page: 'realtime', filters: { online: 'online' } }))}`, `实时地图:${appURL(buildAppHash({ page: 'map', filters: { online: 'online' } }))}`, `轨迹回放:${appURL(buildAppHash({ page: 'history', keyword: highPriorityFilters?.keyword, protocol: highPriorityFilters?.protocol, filters: highPriorityFilters }))}`, `原始记录:${appURL(buildAppHash({ page: 'history-query', keyword: highPriorityFilters?.keyword, protocol: highPriorityFilters?.protocol, filters: { ...highPriorityFilters, tab: 'raw', includeFields: 'true' } }))}`, `告警事件:${appURL(buildAppHash({ page: 'alert-events', filters: highPriorityIssue?.issueType ? { issueType: highPriorityIssue.issueType } : {} }))}`, `里程统计:${appURL(buildAppHash({ page: 'mileage', keyword: highPriorityLookupKey, protocol: highPriorityIssue?.protocol }))}`, `车辆服务:${appURL(buildAppHash({ page: 'detail', keyword: highPriorityLookupKey, protocol: highPriorityIssue?.protocol }))}` ].filter(Boolean); copyText(lines.join('\n'), '运营交接摘要'); }; const copyScenarioBlueprint = () => { const lines = [ '【车辆服务中台功能蓝图】', '定位:32960 / 808 / 宇通 MQTT 都只是车辆接入来源,最终围绕一辆车提供实时、轨迹、历史、告警、统计能力。', `高德地图:Web JS ${amapConfigured ? '已配置' : '待配置'};服务端 API ${amapApiConfigured ? '已配置' : '待配置'};安全代理 ${amapSecurityProxyEnabled ? '已启用' : '未启用'};安全码${amapSecurityCodeExposed ? '已暴露' : '未暴露'}`, `车辆规模:${formatCount(serviceSummary?.totalVehicles)};在线:${formatCount(serviceSummary?.onlineVehicles ?? summary?.onlineVehicles)};告警:${formatCount(summary?.issueVehicles)}`, '', ...scenarioNavigation.map((item, index) => [ `${index + 1}. ${item.title}`, ` 目标:${item.objective}`, ` 证据:${item.evidence}`, ` SLA:${item.sla}` ].join('\n')), '', `入口:${appURL(buildAppHash({ page: 'dashboard' }))}` ]; copyText(lines.join('\n'), '功能蓝图'); }; const copyDataFlowBlueprint = () => { const lines = [ '【车辆服务中台数据流转图】', '原则:32960 / 808 / 宇通 MQTT 是车辆接入来源,最终都归并为同一辆车的车辆服务。', `运行态:连接 ${formatCount(opsHealth?.activeConnections)};在线 Key ${formatCount(opsHealth?.redisOnlineKeys)};Kafka Lag ${formatLag(summary?.kafkaLag)};今日帧 ${formatCount(summary?.frameToday)}`, '', ...dataFlowStages.map((item) => [ `${item.stage}. ${item.title} / ${item.owner}`, ` 证据:${item.evidence}`, ` 说明:${item.detail}` ].join('\n')), '', `实时监控:${appURL(buildAppHash({ page: 'realtime', filters: { online: 'online' } }))}`, `车辆中心:${appURL(buildAppHash({ page: 'vehicles' }))}`, `轨迹回放:${appURL(buildAppHash({ page: 'history' }))}`, `历史查询:${appURL(buildAppHash({ page: 'history-query', filters: { tab: 'raw', includeFields: 'true' } }))}`, `告警事件:${appURL(buildAppHash({ page: 'alert-events' }))}`, `里程统计:${appURL(buildAppHash({ page: 'mileage' }))}`, `运维质量:${appURL(buildAppHash({ page: 'ops-quality' }))}` ]; copyText(lines.join('\n'), '数据流转图'); }; const copyFocusVehicleService = () => { if (!focusVehicle) { Toast.warning('当前没有重点车辆服务可复制'); return; } const commonFilters = { keyword: focusVehicle.lookupKey, protocol: focusVehicle.protocol ?? '' }; const lines = [ '【重点车辆服务处置卡】', `车辆:${focusVehicle.label}`, `原因:${focusVehicle.reason}`, `服务状态:${focusVehicle.statusLabel}`, `实时证据:${focusVehicle.realtimeEvidence}`, `轨迹证据:${focusVehicle.historyEvidence}`, `告警证据:${focusVehicle.alertEvidence}`, `统计证据:${focusVehicle.statisticEvidence}`, `车辆服务:${appURL(buildAppHash({ page: 'detail', keyword: focusVehicle.lookupKey, protocol: focusVehicle.protocol }))}`, `实时监控:${appURL(buildAppHash({ page: 'realtime', keyword: focusVehicle.lookupKey, protocol: focusVehicle.protocol }))}`, `轨迹回放:${appURL(buildAppHash({ page: 'history', keyword: focusVehicle.lookupKey, protocol: focusVehicle.protocol }))}`, `原始记录:${appURL(buildAppHash({ page: 'history-query', keyword: focusVehicle.lookupKey, protocol: focusVehicle.protocol, filters: { ...commonFilters, tab: 'raw', includeFields: 'true' } }))}`, `里程统计:${appURL(buildAppHash({ page: 'mileage', keyword: focusVehicle.lookupKey, protocol: focusVehicle.protocol }))}`, `告警事件:${appURL(buildAppHash({ page: 'alert-events', keyword: focusVehicle.lookupKey, protocol: focusVehicle.protocol, filters: focusVehicle.issueType ? { issueType: focusVehicle.issueType } : {} }))}` ]; copyText(lines.join('\n'), '重点车辆服务处置卡'); }; const buildDashboardSnapshotRows = () => { const unhealthyLinks = (summary?.linkHealth ?? []).filter((item) => item.status !== 'ok'); const priorityAction = serviceActionQueue[0]; const rows: DashboardSnapshotRow[] = [ { section: '车辆服务', item: '在线车辆', value: `${formatCount(serviceSummary?.onlineVehicles ?? summary?.onlineVehicles)} / ${formatCount(serviceSummary?.totalVehicles)}`, detail: '统一车辆服务在线态势' }, { section: '车辆服务', item: '今日活跃', value: formatCount(summary?.activeToday), detail: '当天有有效上报或历史记录的车辆服务对象' }, { section: '实时监控', item: '有效定位', value: commandLocatedCount.toLocaleString(), detail: `当前预览 ${locations.length.toLocaleString()} 条车辆实时数据` }, { section: '实时监控', item: '来源异常', value: commandDegradedCount.toLocaleString(), detail: '当前预览中离线或来源不完整车辆' }, { section: '历史数据', item: '今日帧量', value: formatCount(summary?.frameToday), detail: '历史查询和导出可按车辆、时间、字段裁剪' }, { section: '告警事件', item: '告警车辆', value: formatCount(summary?.issueVehicles), detail: highPriorityIssue ? `${qualityIssueLabel(highPriorityIssue.issueType)} / ${priorityIssueVehicleLabel(highPriorityIssue)}` : '暂无最高优先级告警' }, { section: '地图能力', item: '高德地图', value: amapConfigured ? '已配置' : '待配置', detail: '用于实时监控和轨迹回放地图底座' }, { section: '优先动作', item: priorityAction?.label ?? '暂无待办', value: priorityAction ? priorityAction.count.toLocaleString() : '0', detail: priorityAction?.detail ?? '当前没有必须立即处理的车辆服务事项' } ]; (serviceSummary?.serviceStatuses ?? summary?.serviceStatuses ?? []).forEach((item) => { rows.push({ section: '服务状态', item: item.title || serviceStatusTitle[item.status] || item.status, value: item.count.toLocaleString(), detail: serviceStatusTitle[item.status] || item.status }); }); return rows; }; const exportDashboardSnapshot = () => { const rows = buildDashboardSnapshotRows(); if (rows.length === 0) { Toast.warning('当前没有可导出的驾驶舱摘要'); return; } downloadCsv('dashboard-snapshot.csv', buildCsv(dashboardSnapshotColumns, rows)); Toast.success(`已导出 ${rows.length.toLocaleString()} 条驾驶舱摘要`); }; const commandCenterItems = [ { title: '全域车辆监控', value: `${formatCount(serviceSummary?.onlineVehicles ?? summary?.onlineVehicles)} 在线`, detail: `有效定位 ${commandLocatedCount.toLocaleString()} 辆,先从地图确认车辆分布、在线状态和最新位置。`, color: 'green' as const, primaryAction: '打开车辆地图', secondaryAction: '在线车辆', onPrimary: () => onOpenMap({ online: 'online' }), onSecondary: () => onOpenVehicles({ online: 'online' }) }, { title: '异常车辆闭环', value: `${formatCount(summary?.issueVehicles)} 告警`, detail: highPriorityIssue ? `最高优先级:${qualityIssueLabel(highPriorityIssue.issueType)} / ${priorityIssueVehicleLabel(highPriorityIssue)}` : '当前没有高优先级告警,保持实时巡检。', color: (summary?.issueVehicles ?? 0) > 0 ? 'orange' as const : 'green' as const, primaryAction: '告警事件', secondaryAction: '通知规则', onPrimary: () => onOpenQuality(highPriorityIssue?.issueType ? { issueType: highPriorityIssue.issueType } : {}), onSecondary: () => onOpenQuality(highPriorityIssue?.issueType ? { issueType: highPriorityIssue.issueType } : {}) }, { title: '自定义时间复盘', value: taskTimeMonitorSummary, detail: '一个时间窗贯穿轨迹回放、里程统计、历史查询和告警复盘,适合客户问询与 BI 核对。', color: 'blue' as const, primaryAction: '轨迹复盘', secondaryAction: '里程统计', onPrimary: () => openTimeMonitorHistory(), onSecondary: () => openTimeMonitorMileage() }, { title: '数据交付与导出', value: `${formatCount(summary?.frameToday)} 今日数据`, detail: '按车辆、时间、字段裁剪导出位置、原始记录和字段证据,交付给业务或客户复盘。', color: 'blue' as const, primaryAction: '历史导出', secondaryAction: '导出概览', onPrimary: () => openTimeMonitorRaw(), onSecondary: () => exportDashboardSnapshot() } ]; const customerServicePathItems = [ { title: '找车', value: formatCount(serviceSummary?.totalVehicles), detail: '按 VIN、车牌、手机号或 OEM 进入车辆服务,协议来源只作为证据。', action: '车辆中心', color: 'blue' as const, onClick: () => onOpenVehicles({}) }, { title: '看位置', value: `${commandLocatedCount.toLocaleString()} 有定位`, detail: '优先打开实时地图,判断车辆是否在线、坐标是否有效、是否存在断链。', action: '实时地图', color: commandLocatedCount > 0 ? 'green' as const : 'orange' as const, onClick: () => onOpenMap({ online: 'online' }) }, { title: '复盘时间', value: dayRangeText(timeMonitorScopeValue.dateFrom, timeMonitorScopeValue.dateTo), detail: '同一个时间窗贯穿轨迹回放、里程统计、原始记录和告警复盘。', action: '轨迹回放', color: 'blue' as const, onClick: openTimeMonitorHistory }, { title: '交付数据', value: `${formatCount(summary?.frameToday)} 今日数据`, detail: '按车辆、时间、字段裁剪导出位置历史、原始记录和字段明细。', action: '查询导出', color: 'blue' as const, onClick: openTimeMonitorRaw }, { title: '闭环异常', value: `${formatCount(summary?.issueVehicles)} 告警`, detail: highPriorityIssue ? `${qualityIssueLabel(highPriorityIssue.issueType)} / ${priorityIssueVehicleLabel(highPriorityIssue)}` : '暂无高优先级告警,保持日常巡检。', action: '告警事件', color: (summary?.issueVehicles ?? 0) > 0 ? 'orange' as const : 'green' as const, onClick: () => onOpenQuality() } ]; const copyCustomerServiceGuide = () => { const scope = timeMonitorScope(); const rawFilters = { ...scope, tab: 'raw', includeFields: 'true' }; const lines = [ '【车辆客户服务说明】', '服务对象:车辆,不是接入来源。', `车辆规模:${formatCount(serviceSummary?.totalVehicles)},在线车辆:${formatCount(serviceSummary?.onlineVehicles ?? summary?.onlineVehicles)},有效定位:${commandLocatedCount.toLocaleString()}`, `当前时间窗:${timeMonitorSummary}(${dayRangeText(scope.dateFrom, scope.dateTo)})`, `告警车辆:${formatCount(summary?.issueVehicles)},最高优先级:${highPriorityIssue ? `${qualityIssueLabel(highPriorityIssue.issueType)} / ${priorityIssueVehicleLabel(highPriorityIssue)}` : '暂无'}`, '', '客户服务路径:', '1. 找车:先按 VIN、车牌、手机号或 OEM 锁定车辆。', '2. 看位置:打开实时地图确认车辆在线、坐标和最新上报。', '3. 复盘时间:同一时间窗查看轨迹、里程和告警。', '4. 交付数据:按车辆、时间、字段导出位置历史、原始记录和字段明细。', '5. 闭环异常:告警事件进入通知和处置流程。', `车辆中心:${appURL(buildAppHash({ page: 'vehicles' }))}`, `实时地图:${appURL(buildAppHash({ page: 'map', filters: { online: 'online' } }))}`, `轨迹回放:${appURL(buildAppHash({ page: 'history', keyword: scope.keyword, protocol: scope.protocol, filters: scope }))}`, `里程统计:${appURL(buildAppHash({ page: 'mileage', keyword: scope.keyword, protocol: scope.protocol, filters: scope }))}`, `历史数据:${appURL(buildAppHash({ page: 'history-query', keyword: scope.keyword, protocol: scope.protocol, filters: rawFilters }))}`, `告警事件:${appURL(buildAppHash({ page: 'alert-events', keyword: scope.keyword, protocol: scope.protocol, filters: scope }))}` ]; copyText(lines.join('\n'), '客户服务说明'); }; const customerCockpitMetrics = [ { label: '车辆总数', value: formatCount(serviceSummary?.totalVehicles), detail: '以 VIN 聚合车牌、手机号、OEM 和来源证据', color: 'blue' as const, onClick: () => onOpenVehicles({}) }, { label: '在线车辆', value: formatCount(serviceSummary?.onlineVehicles ?? summary?.onlineVehicles), detail: vehicleServiceOnlineText(serviceSummary, summary), color: 'green' as const, onClick: () => onOpenMap({ online: 'online' }) }, { label: '有效定位', value: commandLocatedCount.toLocaleString(), detail: '用于实时地图、轨迹回放和客户位置问询', color: commandLocatedCount > 0 ? 'green' as const : 'orange' as const, onClick: () => onOpenRealtime({ online: 'online' }) }, { label: '告警车辆', value: formatCount(summary?.issueVehicles), detail: highPriorityIssue ? `${qualityIssueLabel(highPriorityIssue.issueType)} / ${priorityIssueVehicleLabel(highPriorityIssue)}` : '暂无高优先级告警', color: (summary?.issueVehicles ?? 0) > 0 ? 'orange' as const : 'green' as const, onClick: () => onOpenQuality() } ]; const customerCockpitWorkflows = [ { title: '实时看车', description: '打开地图确认车辆在哪里、是否在线、最后上报是否新鲜。', action: '实时地图', evidence: `${commandLocatedCount.toLocaleString()} 辆有定位`, onClick: () => onOpenMap({ online: 'online' }) }, { title: '轨迹复盘', description: '用同一辆车和同一时间窗回放位置、速度、里程断点。', action: '轨迹回放', evidence: `${formatCount(summary?.activeToday)} 今日活跃`, onClick: openTimeMonitorHistory }, { title: '里程核对', description: '按车辆口径核对区间里程、日统计和异常来源。', action: '里程统计', evidence: `${formatCount(serviceSummary?.totalVehicles)} 辆可统计`, onClick: openTimeMonitorMileage }, { title: '数据交付', description: '按车辆、时间和字段导出位置历史、原始记录和解析字段。', action: '历史导出', evidence: `${formatCount(summary?.frameToday)} 今日数据`, onClick: openTimeMonitorRaw }, { title: '告警通知', description: '将断链、离线、定位异常和字段缺失变成可通知事件。', action: '告警事件', evidence: `${formatCount(summary?.issueVehicles)} 告警车辆`, onClick: () => onOpenQuality() } ]; const customerTrustSignals = [ { label: '数据来源', value: `${formatCount(serviceSummary?.multiSourceVehicles)} 多源 / ${formatCount(serviceSummary?.singleSourceVehicles)} 单源`, detail: '来源只作为可信度证据,不作为客户主导航。' }, { label: '地图能力', value: amapConfigured ? '高德地图可用' : '坐标预览', detail: amapConfigured ? '支持实时看车和轨迹底图。' : '地图配置缺失时仍可查看坐标证据。' }, { label: '时间窗', value: dayRangeText(timeMonitorScopeValue.dateFrom, timeMonitorScopeValue.dateTo), detail: '同一时间范围贯穿轨迹、里程、历史和告警。' } ]; return (
客户车辆监控中心 按车服务 0 ? 'orange' : 'green'}>{formatCount(summary?.issueVehicles)} 告警车辆 客户只需要回答:车在哪、跑了多少、发生了什么 32960、808、宇通 MQTT 都只是车辆数据来源。产品主线围绕车辆本身提供实时地图、轨迹回放、里程统计、时间窗监控、历史查询、告警通知和数据导出。
{customerCockpitMetrics.map((item) => ( ))}
{customerCockpitWorkflows.map((item) => ( ))}
{customerTrustSignals.map((item) => (
{item.label} {item.value} {item.detail}
))}
客户工作台 {amapConfigured ? '地图可用' : '地图待配置'} 0 ? 'orange' : 'green'}>{formatCount(summary?.issueVehicles)} 告警车辆 先看车在哪里,再看车发生了什么 平台已把多路接入数据归并为同一套车辆服务。客户每天要直接完成车辆地图监控、轨迹回放、里程统计、时间窗复盘、告警闭环和数据导出。
车辆服务路径
{customerJourneySteps.map((item) => ( ))}
{customerHealthItems.map((item) => ( ))}
{commandCenterItems.map((item) => (
{item.title} {item.value}
{item.detail}
))}
自定义时间监控{timeMonitorSummary}} style={{ marginBottom: 16 }} >
setTimeMonitorFilters(timeMonitorScope(values as Record))} style={{ marginBottom: 12 }} > GB32960 JT808 YUTONG_MQTT
同一时间窗会带入轨迹回放、里程统计和历史数据导出,避免跨页面重复输入。
客户时间窗复盘包 {timeMonitorScope().keyword ? '单车复盘' : '全域复盘'} {timeMonitorSummary} 客户问某辆车、某段时间发生了什么时,直接按这个时间窗串起轨迹、里程、历史数据、告警通知和地图定位。
{timeMonitorReviewItems.map((item) => (
{item.label} {item.value} {item.detail}
))}
时间窗服务核对 交付给客户前,先确认范围、轨迹、导出和异常闭环四件事都能讲清楚。
{timeMonitorChecklist.map((item) => (
{item.label} {item.value} {item.detail}
))}
当前时间窗 {timeMonitorScope().keyword ? '单车' : '全域'} {timeMonitorSummary} 这个范围会同步带入轨迹、里程、历史证据和告警复盘,适合客户问询、BI 核对和异常解释。
{timeMonitorWorkbench.map((item) => ( ))}
{[ { title: '今天车辆轨迹', detail: '查看当天活跃车辆的轨迹、速度和里程断点。', action: '查今天', onClick: openTimeMonitorHistory }, { title: '历史时间窗', detail: '按任意起止时间查询位置历史、原始记录和字段明细。', action: '自定义查询', onClick: openTimeMonitorRaw }, { title: '区间里程', detail: '按车辆和时间范围核对区间里程、日统计和异常点。', action: '里程统计', onClick: openTimeMonitorMileage }, { title: '告警复盘', detail: '按车辆或事件回放告警发生前后的轨迹和数据证据。', action: '告警事件', onClick: openTimeMonitorAlerts } ].map((item) => ( ))}
更多车辆服务入口 常用服务、今日任务、KPI 和车辆入口默认收起,主页面优先保留地图、指挥台和时间窗监控。
{customerHeroActions.map((item) => ( ))}
{customerTaskBoard.map((item) => (
{item.title} {item.value}
{item.detail}
))}
{kpis.map((item) => ( ))}
客户服务路径 {vehicleServiceOnlineText(serviceSummary, summary)} 0 ? 'orange' : 'green'}>{formatCount(summary?.issueVehicles)} 告警事件 从车辆出发,完成监控、复盘、统计、导出和告警闭环 客户看到的是车辆服务结果;接入来源只在需要追溯时作为数据通道出现。
{customerServicePathItems.map((item) => ( ))}
运维和证据层 来源覆盖、地图配置、告警预览和实时证据默认收起,客户主流程先看车辆监控、轨迹、统计、告警和导出。
{mapReadiness.map((item) => (
{item.title}
{item.value}
{item.detail}
))}
{mapWorkItems.map((item) => ( ))}
{focusVehicle ? ( 重点车辆服务{focusVehicle.statusLabel}} style={{ marginBottom: 16 }} >
{focusVehicle.reason} {focusVehicle.label} 把实时、轨迹、原始记录、里程和告警集中到同一辆车处理。
{[ { label: '实时证据', value: focusVehicle.realtimeEvidence }, { label: '轨迹证据', value: focusVehicle.historyEvidence }, { label: '告警证据', value: focusVehicle.alertEvidence }, { label: '统计证据', value: focusVehicle.statisticEvidence } ].map((item) => (
{item.label} {item.value}
))}
) : null}
{operationWorkbench.map((item) => (
{item.title} {item.meta}
{item.value}
{item.detail} {item.actions.map((action) => ( ))}
))}
{vehicleServicePrinciples.map((item) => (
{item.title}
{item.value}
{item.detail}
))}
车联网场景导航} style={{ marginBottom: 16 }} >
{scenarioNavigation.map((item) => (
{item.title} {item.objective}
{item.evidence}
{item.sla}
))}
{highPriorityIssue ? (
{highPriorityIssue.severity === 'error' ? 'P0' : 'P1'} {qualityIssueLabel(highPriorityIssue.issueType)} {qualityProtocolLabel(highPriorityIssue.protocol)} {highPriorityIssue.lastSeen || '-'} {priorityIssueVehicleLabel(highPriorityIssue)} {highPriorityIssue.detail || '暂无告警详情'}
) : null}
{capabilities.map((item) => (
{item.title} {item.description}
{item.status}
))}
{amapConfigured ? '高德地图配置就绪' : '高德地图待配置'} 在线 {commandOnlineCount.toLocaleString()} / {locations.length.toLocaleString()} 有效定位 {commandLocatedCount.toLocaleString()} 0 ? 'orange' : 'green'}>降级/离线 {commandDegradedCount.toLocaleString()}
实时作业
{commandOnlineCount.toLocaleString()}
当前预览车辆在线数,进入实时监控可按车辆、来源和服务状态筛选。
0 ? 'orange' : 'green'}>处置优先级
{commandDegradedCount.toLocaleString()}
优先处理离线、无来源、身份未绑定和来源不完整车辆。
{serviceActionQueue.length > 0 ? (
{serviceActionQueue.map((item) => (
{item.label} {item.count.toLocaleString()} {item.detail}
))}
) : null} 告警事件预览} bordered style={{ marginTop: 16 }} > `${row?.protocol ?? ''}-${row?.issueType ?? ''}-${row?.lastSeen ?? ''}-${row?.detail ?? ''}`} dataSource={qualityIssues} columns={[ { title: 'VIN', dataIndex: 'vin', width: 160 }, { title: '车牌', dataIndex: 'plate', width: 110 }, { title: '手机号', dataIndex: 'phone', width: 130 }, { title: '来源地址', dataIndex: 'sourceEndpoint', width: 180 }, { title: '来源', dataIndex: 'protocol', width: 110 }, { title: '问题', dataIndex: 'issueType', width: 140 }, { title: '级别', width: 90, render: (_: unknown, row: QualityIssueRow) => {row.severity} }, { title: '最后时间', dataIndex: 'lastSeen', width: 170 }, { title: '操作', width: 130, render: (_: unknown, row: QualityIssueRow) => { const lookup = qualityIssueVehicleLookup(row); return ; } } ]} /> 车辆服务覆盖} bordered style={{ marginTop: 16 }} > {coverageServiceStatusTitle ? (
当前筛选:{coverageServiceStatusTitle}
) : null}
loadCoverage(values as Record)} style={{ marginBottom: 12 }}> 单源 多源 缺 GB32960 缺 JT808 缺 YUTONG_MQTT 在线 离线 已绑定 未绑定 服务正常 来源不完整 车辆离线 暂无数据来源 身份未绑定
( ) }, { title: '证据覆盖', width: 130, render: (_: unknown, row: VehicleCoverageRow) => sourceEvidenceText(row) }, { title: '服务状态', width: 130, render: (_: unknown, row: VehicleCoverageRow) => { const status = rowServiceStatus(row); return {status.label}; } }, { title: '来源一致性', width: 140, render: (_: unknown, row: VehicleCoverageRow) => sourceConsistencyAction(row, loadCoverage) }, { title: '在线', width: 90, render: (_: unknown, row: VehicleCoverageRow) => }, { title: '绑定', width: 90, render: (_: unknown, row: VehicleCoverageRow) => {row.bindingStatus === 'bound' ? '已绑定' : '未绑定'} }, { title: '最后时间', dataIndex: 'lastSeen', width: 170 }, { title: '操作', width: 110, render: (_: unknown, row: VehicleCoverageRow) => } ]} />
{locations.map((row, index) => ( ))}
( {row.protocols.map((protocol) => {protocol})} ) }, { title: '服务状态', render: (_: unknown, row: VehicleRealtimeRow) => { const status = rowServiceStatus(row); return {status.label}; } }, { title: '最后时间', dataIndex: 'lastSeen' }, { title: '操作', width: 110, render: (_: unknown, row: VehicleRealtimeRow) => } ]} /> ); }