diff --git a/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx b/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx index c30549e4..450ebca3 100644 --- a/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx +++ b/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx @@ -125,6 +125,16 @@ function previousDateString(value: string) { 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 || '-'; @@ -806,12 +816,41 @@ export function Dashboard({ 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: '历史查询默认带 RAW 与解析字段,支持按字段缩小导出体积。', + 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 || '全部数据通道'}`, @@ -820,6 +859,8 @@ export function Dashboard({ `统计证据:车辆口径 ${formatCount(serviceSummary?.totalVehicles)},用于区间里程和日统计闭合`, `历史证据:今日数据 ${formatCount(summary?.frameToday)},用于 RAW 与解析字段复核`, `告警事件:${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 }))}`, @@ -1390,6 +1431,23 @@ export function Dashboard({ ))} +