diff --git a/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx b/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx index d0cd7691..285bc3b8 100644 --- a/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx +++ b/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx @@ -107,6 +107,24 @@ function nextDate(value: string) { 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 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 || '-'; @@ -237,6 +255,13 @@ export function Dashboard({ 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(''); @@ -655,6 +680,39 @@ export function Dashboard({ ] } ]; + 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 dataFlowStages = [ { stage: '01', @@ -1009,13 +1067,48 @@ export function Dashboard({ - + 自定义时间监控{timeMonitorSummary}} + style={{ marginBottom: 16 }} + > +
setTimeMonitorFilters(timeMonitorScope(values as Record))} + style={{ marginBottom: 12 }} + > + + + GB32960 + JT808 + YUTONG_MQTT + + + + + + + + +
+ + + + +
+ + 同一时间窗会带入轨迹回放、里程统计和历史数据导出,避免跨页面重复输入。 +
{[ - { title: '今天车辆轨迹', detail: '查看当天活跃车辆的轨迹、速度和里程断点。', action: '查今天', onClick: () => onOpenHistory() }, - { title: '历史时间窗', detail: '按任意起止时间查询位置历史、原始记录和字段明细。', action: '自定义查询', onClick: () => onOpenHistory({ tab: 'raw', includeFields: 'true' }) }, - { title: '区间里程', detail: '按车辆和时间范围核对区间里程、日统计和异常点。', action: '里程统计', onClick: () => onOpenMileage() }, - { title: '告警复盘', detail: '按车辆或事件回放告警发生前后的轨迹和数据证据。', action: '告警事件', onClick: () => onOpenQuality() } + { title: '今天车辆轨迹', detail: '查看当天活跃车辆的轨迹、速度和里程断点。', action: '查今天', onClick: openTimeMonitorHistory }, + { title: '历史时间窗', detail: '按任意起止时间查询位置历史、原始记录和字段明细。', action: '自定义查询', onClick: openTimeMonitorRaw }, + { title: '区间里程', detail: '按车辆和时间范围核对区间里程、日统计和异常点。', action: '里程统计', onClick: openTimeMonitorMileage }, + { title: '告警复盘', detail: '按车辆或事件回放告警发生前后的轨迹和数据证据。', action: '告警事件', onClick: openTimeMonitorAlerts } ].map((item) => (