From 3cdded42c5e8e7091e4b58dc3148caa6951a1102 Mon Sep 17 00:00:00 2001 From: lingniu Date: Sun, 5 Jul 2026 13:01:34 +0800 Subject: [PATCH] feat(platform): refine vehicle service workspace --- vehicle-data-platform/apps/web/src/App.tsx | 6 +- .../apps/web/src/pages/VehicleDetail.tsx | 239 +++++++++--------- .../apps/web/src/pages/Vehicles.tsx | 2 +- .../apps/web/src/test/App.test.tsx | 124 +++++---- 4 files changed, 196 insertions(+), 175 deletions(-) diff --git a/vehicle-data-platform/apps/web/src/App.tsx b/vehicle-data-platform/apps/web/src/App.tsx index 29d41e94..63af7c50 100644 --- a/vehicle-data-platform/apps/web/src/App.tsx +++ b/vehicle-data-platform/apps/web/src/App.tsx @@ -84,7 +84,7 @@ export default function App() { api.alertEventNotificationPlan(new URLSearchParams({ limit: '5' })).catch(() => null) ]) .then(([health, notificationPlan]) => { - setLinkIssueCount(health.linkHealth.filter((item) => item.status !== 'ok').length); + setLinkIssueCount((health.linkHealth ?? []).filter((item) => item.status !== 'ok').length); setPlatformRelease(health.runtime?.platformRelease ?? ''); applyNotificationPlanSummary(notificationPlan); return health; @@ -458,11 +458,11 @@ export default function App() { 'history-query': , mileage: , 'alert-events': navigatePage('notification-rules')} onHealthLoaded={(health) => { - setLinkIssueCount(health.linkHealth.filter((item) => item.status !== 'ok').length); + setLinkIssueCount((health.linkHealth ?? []).filter((item) => item.status !== 'ok').length); setPlatformRelease(health.runtime?.platformRelease ?? ''); }} onNotificationPlanLoaded={applyNotificationPlanSummary} onFiltersChange={updateQualityFilters} initialFilters={qualityFilters} />, quality: navigatePage('notification-rules')} onHealthLoaded={(health) => { - setLinkIssueCount(health.linkHealth.filter((item) => item.status !== 'ok').length); + setLinkIssueCount((health.linkHealth ?? []).filter((item) => item.status !== 'ok').length); setPlatformRelease(health.runtime?.platformRelease ?? ''); }} onNotificationPlanLoaded={applyNotificationPlanSummary} onFiltersChange={updateQualityFilters} initialFilters={qualityFilters} />, 'notification-rules': , diff --git a/vehicle-data-platform/apps/web/src/pages/VehicleDetail.tsx b/vehicle-data-platform/apps/web/src/pages/VehicleDetail.tsx index 28799950..dfd8add6 100644 --- a/vehicle-data-platform/apps/web/src/pages/VehicleDetail.tsx +++ b/vehicle-data-platform/apps/web/src/pages/VehicleDetail.tsx @@ -20,8 +20,8 @@ type VehicleQuery = { const sourceCapabilities: Array<{ key: keyof Pick; label: string }> = [ { key: 'hasRealtime', label: '实时' }, - { key: 'hasHistory', label: '历史' }, - { key: 'hasRaw', label: '原始记录' }, + { key: 'hasHistory', label: '轨迹' }, + { key: 'hasRaw', label: '历史数据' }, { key: 'hasMileage', label: '里程' } ]; @@ -57,9 +57,18 @@ function formatSeconds(value?: number) { return `${Math.round(value)} 秒`; } +function customerStatusTitle(title?: string) { + if (!title) return ''; + return title === '来源不完整' ? '数据通道不完整' : title; +} + +function customerScopeText(protocol?: string) { + return protocol ? `单一数据通道:${protocol}` : '全部数据通道聚合'; +} + function sourceServiceRole(source: VehicleSourceStatus, primaryProtocol?: string) { - if (source.protocol === primaryProtocol) return { label: '主证据', color: 'blue' as const }; - if (source.online) return { label: '在线证据', color: 'green' as const }; + if (source.protocol === primaryProtocol) return { label: '主数据通道', color: 'blue' as const }; + if (source.online) return { label: '在线数据通道', color: 'green' as const }; if (!source.lastSeen && !source.hasRealtime && !source.hasHistory && !source.hasRaw && !source.hasMileage) { return { label: '暂无来源', color: 'grey' as const }; } @@ -70,12 +79,12 @@ function sourceFusionRow(source: VehicleSourceStatus, realtime: RealtimeLocation const hasLocation = !!realtime && isFiniteNumber(realtime.longitude) && isFiniteNumber(realtime.latitude) && realtime.longitude !== 0 && realtime.latitude !== 0; const hasMileage = isFiniteNumber(realtime?.totalMileageKm) || source.hasMileage; const contribution = [ - source.protocol === primaryProtocol ? '主实时证据' : '', + source.protocol === primaryProtocol ? '主实时通道' : '', source.online ? '在线心跳' : '', source.hasRealtime || realtime ? '实时字段' : '', - hasLocation ? '定位证据' : '', + hasLocation ? '定位数据' : '', source.hasHistory ? '轨迹回放' : '', - source.hasRaw ? '原始记录' : '', + source.hasRaw ? '历史数据' : '', hasMileage ? '统计口径' : '' ].filter(Boolean); return { @@ -85,8 +94,8 @@ function sourceFusionRow(source: VehicleSourceStatus, realtime: RealtimeLocation freshness: source.online ? `在线 / ${source.lastSeen || realtime?.lastSeen || '-'}` : source.lastSeen ? `离线 / ${source.lastSeen}` : '未接入', realtimeText: source.hasRealtime || realtime ? `速度 ${formatCompactNumber(realtime?.speedKmh, ' km/h')} / SOC ${formatCompactNumber(realtime?.socPercent, '%')}` : '无实时字段', locationText: hasLocation ? `${realtime?.longitude}, ${realtime?.latitude}` : '无有效坐标', - rawText: source.hasRaw ? '可查原始记录' : '无原始记录', - mileageText: hasMileage ? formatCompactNumber(realtime?.totalMileageKm, ' km') : '无里程证据', + rawText: source.hasRaw ? '可查历史数据' : '无历史数据', + mileageText: hasMileage ? formatCompactNumber(realtime?.totalMileageKm, ' km') : '无里程数据', contribution: contribution.length > 0 ? contribution : ['待补来源'] }; } @@ -327,7 +336,7 @@ export function VehicleDetail({ const evidenceMileageDelta = consistencyMileageDelta; const evidenceTimeDeltaSeconds = consistencyTimeDeltaSeconds; const activeProtocol = query.protocol?.trim() ?? ''; - const scopeText = activeProtocol ? `单一来源:${activeProtocol}` : '全部来源聚合'; + const scopeText = customerScopeText(activeProtocol); const archivePlate = resolution?.plate || summary?.plate || identity?.plate || latest?.plate || ''; const archivePhone = resolution?.phone || summary?.phone || identity?.phone || ''; const archiveOEM = resolution?.oem || summary?.oem || identity?.oem || ''; @@ -406,39 +415,39 @@ export function VehicleDetail({ `车辆:${vehicleName}`, `查询关键词:${reportText(displayLookupKey)}`, `当前范围:${scopeText}`, - `服务状态:${serviceStatus?.title ?? serviceConclusion.status}`, + `服务状态:${customerStatusTitle(serviceStatus?.title) || serviceConclusion.status}`, `服务说明:${serviceStatus?.detail ?? '-'}`, `服务判断:${serviceConclusion.status}`, `主要风险:${serviceConclusion.risk}`, `下一步:${serviceConclusion.nextStep}`, - `证据来源:${evidenceSourceCount > 0 ? `${evidenceOnlineSourceCount}/${evidenceSourceCount} 在线` : '-'}`, - `定位证据:${evidenceLocatedSourceCount > 0 ? `${evidenceLocatedSourceCount} 个证据有位置` : '暂无位置'}`, + `数据通道:${evidenceSourceCount > 0 ? `${evidenceOnlineSourceCount}/${evidenceSourceCount} 在线` : '-'}`, + `定位数据:${evidenceLocatedSourceCount > 0 ? `${evidenceLocatedSourceCount} 个通道有位置` : '暂无位置'}`, `里程差异:${formatCompactNumber(evidenceMileageDelta, ' km')}`, `时间差异:${formatSeconds(evidenceTimeDeltaSeconds)}`, - `服务数据:实时 ${overview?.realtimeCount ?? detail.realtime.length} / 轨迹 ${overview?.historyCount ?? detail.history.total ?? 0} / 原始记录 ${overview?.rawCount ?? detail.raw.total ?? 0} / 里程 ${overview?.mileageCount ?? detail.mileage.total ?? 0} / 告警 ${overview?.qualityIssueCount ?? qualityCount}`, - `一致性:${consistencyTitle}`, + `服务数据:实时 ${overview?.realtimeCount ?? detail.realtime.length} / 轨迹 ${overview?.historyCount ?? detail.history.total ?? 0} / 历史数据 ${overview?.rawCount ?? detail.raw.total ?? 0} / 里程 ${overview?.mileageCount ?? detail.mileage.total ?? 0} / 告警 ${overview?.qualityIssueCount ?? qualityCount}`, + `一致性:${customerStatusTitle(consistencyTitle) || consistencyTitle}`, `一致性说明:${consistencyDetail}`, `车辆服务:${vehicleServiceURL(vehicleServiceHash('detail'))}`, `实时:${vehicleServiceURL(vehicleServiceHash('realtime'))}`, `轨迹:${vehicleServiceURL(vehicleServiceHash('history'))}`, - `原始记录:${vehicleServiceURL(vehicleServiceHash('history-query', { tab: 'raw', includeFields: 'true' }))}`, + `历史数据:${vehicleServiceURL(vehicleServiceHash('history-query', { tab: 'raw', includeFields: 'true' }))}`, `里程:${vehicleServiceURL(vehicleServiceHash('mileage'))}` ]; copyText(lines.join('\n'), '诊断摘要'); }; const copySourceFusionReport = () => { if (!detail || sourceFusionRows.length === 0) { - Toast.warning('当前没有可复制的多证据归并矩阵'); + Toast.warning('当前没有可复制的数据通道归并矩阵'); return; } const vehicleName = [archivePlate, displayVIN].filter((item) => item && item !== '-').join(' / ') || displayLookupKey; const lines = [ - '【单车多证据归并矩阵】', + '【单车数据通道归并矩阵】', `车辆:${vehicleName}`, `当前范围:${scopeText}`, - `主证据来源:${overview?.primaryProtocol || summary?.primaryProtocol || '-'}`, - `证据覆盖:${evidenceSourceCount > 0 ? `${evidenceOnlineSourceCount}/${evidenceSourceCount} 在线` : '-'}`, - `一致性:${consistencyTitle}`, + `主数据通道:${overview?.primaryProtocol || summary?.primaryProtocol || '-'}`, + `通道覆盖:${evidenceSourceCount > 0 ? `${evidenceOnlineSourceCount}/${evidenceSourceCount} 在线` : '-'}`, + `一致性:${customerStatusTitle(consistencyTitle) || consistencyTitle}`, '', ...sourceFusionRows.map((row, index) => [ `${index + 1}. ${row.source.protocol} / ${row.role.label}`, @@ -446,17 +455,17 @@ export function VehicleDetail({ ` 贡献:${row.contribution.join('、')}`, ` 实时:${row.realtimeText}`, ` 定位:${row.locationText}`, - ` 原始记录:${row.rawText}`, + ` 历史数据:${row.rawText}`, ` 里程:${row.mileageText}` ].join('\n')), '', `车辆服务:${vehicleServiceURL(vehicleServiceHash('detail'))}`, `实时监控:${vehicleServiceURL(vehicleServiceHash('realtime'))}`, `轨迹回放:${vehicleServiceURL(vehicleServiceHash('history'))}`, - `原始记录:${vehicleServiceURL(vehicleServiceHash('history-query', { tab: 'raw', includeFields: 'true' }))}`, + `历史数据:${vehicleServiceURL(vehicleServiceHash('history-query', { tab: 'raw', includeFields: 'true' }))}`, `里程统计:${vehicleServiceURL(vehicleServiceHash('mileage'))}` ]; - copyText(lines.join('\n'), '多证据归并矩阵'); + copyText(lines.join('\n'), '数据通道归并矩阵'); }; const qualityIssueAction = (count: number) => { if (count <= 0) { @@ -488,7 +497,7 @@ export function VehicleDetail({ actions.push({ key: `missing-${item}`, label: `补齐 ${item} 证据`, - description: `${item} 当前没有形成有效证据来源,会影响这辆车的统一服务可信度。`, + description: `${item} 当前没有形成有效数据通道,会影响这辆车的统一服务可信度。`, color: 'orange', onClick: () => onOpenVehicles?.({ serviceStatus: 'degraded', missingProtocol: item }) }); @@ -537,7 +546,7 @@ export function VehicleDetail({ color: (overview?.historyCount ?? detail?.history?.total ?? 0) > 0 ? 'blue' as const : 'grey' as const }, { - label: '原始记录', + label: '历史数据', value: formatCompactNumber(overview?.rawCount ?? detail?.raw?.total ?? 0), detail: latestRaw?.frameType || '解析字段', color: (overview?.rawCount ?? detail?.raw?.total ?? 0) > 0 ? 'blue' as const : 'grey' as const @@ -564,12 +573,12 @@ export function VehicleDetail({ }, { label: '服务状态', - value: serviceStatus?.title ?? serviceConclusion.status, + value: customerStatusTitle(serviceStatus?.title) || serviceConclusion.status, detail: serviceStatus?.detail ?? serviceConclusion.risk, color: serviceConclusion.color }, { - label: '实时主证据来源', + label: '实时主数据通道', value: overview?.primaryProtocol || summary?.primaryProtocol || '-', detail: evidenceSourceCount > 0 ? `${evidenceOnlineSourceCount}/${evidenceSourceCount} 在线` : '暂无来源', color: evidenceOnlineSourceCount > 0 ? 'green' as const : 'orange' as const @@ -592,17 +601,17 @@ export function VehicleDetail({ `车辆:${vehicleName}`, `当前范围:${scopeText}`, `车辆身份:${hasResolvedVIN ? displayVIN : '待绑定'}`, - `服务状态:${serviceStatus?.title ?? serviceConclusion.status}`, + `服务状态:${customerStatusTitle(serviceStatus?.title) || serviceConclusion.status}`, `服务说明:${serviceStatus?.detail ?? serviceConclusion.risk}`, - `实时主证据来源:${overview?.primaryProtocol || summary?.primaryProtocol || '-'}`, - `证据在线:${evidenceSourceCount > 0 ? `${evidenceOnlineSourceCount}/${evidenceSourceCount}` : '-'}`, + `实时主数据通道:${overview?.primaryProtocol || summary?.primaryProtocol || '-'}`, + `通道在线:${evidenceSourceCount > 0 ? `${evidenceOnlineSourceCount}/${evidenceSourceCount}` : '-'}`, `业务可用性:${online ? '在线可用' : hasResolvedVIN ? '离线待查' : '待绑定'}`, - `服务数据:实时 ${overview?.realtimeCount ?? detail.realtime.length} / 轨迹 ${overview?.historyCount ?? detail.history.total ?? 0} / 原始记录 ${overview?.rawCount ?? detail.raw.total ?? 0} / 里程 ${overview?.mileageCount ?? detail.mileage.total ?? 0} / 告警 ${overview?.qualityIssueCount ?? qualityCount}`, + `服务数据:实时 ${overview?.realtimeCount ?? detail.realtime.length} / 轨迹 ${overview?.historyCount ?? detail.history.total ?? 0} / 历史数据 ${overview?.rawCount ?? detail.raw.total ?? 0} / 里程 ${overview?.mileageCount ?? detail.mileage.total ?? 0} / 告警 ${overview?.qualityIssueCount ?? qualityCount}`, `下一步:${serviceActions[0]?.label ?? serviceConclusion.nextStep}`, `车辆服务:${vehicleServiceURL(vehicleServiceHash('detail'))}`, `实时监控:${vehicleServiceURL(vehicleServiceHash('realtime'))}`, `轨迹回放:${vehicleServiceURL(vehicleServiceHash('history'))}`, - `数据导出:${vehicleServiceURL(vehicleServiceHash('history-query', { tab: 'raw', includeFields: 'true' }))}`, + `历史数据:${vehicleServiceURL(vehicleServiceHash('history-query', { tab: 'raw', includeFields: 'true' }))}`, `里程统计:${vehicleServiceURL(vehicleServiceHash('mileage'))}` ]; copyText(lines.join('\n'), '统一车辆服务摘要'); @@ -619,17 +628,17 @@ export function VehicleDetail({ `查询关键词:${displayLookupKey}`, `当前范围:${scopeText}`, `档案完整度:${archiveCompleteness}${archiveMissingLabels.length > 0 ? `(${archiveMissingLabels.join('、')})` : ''}`, - `主证据来源:${overview?.primaryProtocol || summary?.primaryProtocol || '-'}`, - `证据在线:${evidenceSourceCount > 0 ? `${evidenceOnlineSourceCount}/${evidenceSourceCount}` : '-'}`, - `服务状态:${serviceStatus?.title ?? serviceConclusion.status}`, + `主数据通道:${overview?.primaryProtocol || summary?.primaryProtocol || '-'}`, + `通道在线:${evidenceSourceCount > 0 ? `${evidenceOnlineSourceCount}/${evidenceSourceCount}` : '-'}`, + `服务状态:${customerStatusTitle(serviceStatus?.title) || serviceConclusion.status}`, `服务说明:${serviceStatus?.detail ?? serviceConclusion.risk}`, `资产概览:${serviceAssetItems.map((item) => `${item.label} ${item.value}`).join(' / ')}`, - `一致性:${consistencyTitle}`, + `一致性:${customerStatusTitle(consistencyTitle) || consistencyTitle}`, `下一步:${serviceActions[0]?.label ?? serviceConclusion.nextStep}`, `车辆服务:${vehicleServiceURL(vehicleServiceHash('detail'))}`, `实时监控:${vehicleServiceURL(vehicleServiceHash('realtime'))}`, `轨迹回放:${vehicleServiceURL(vehicleServiceHash('history'))}`, - `原始记录:${vehicleServiceURL(vehicleServiceHash('history-query', { tab: 'raw', includeFields: 'true' }))}`, + `历史数据:${vehicleServiceURL(vehicleServiceHash('history-query', { tab: 'raw', includeFields: 'true' }))}`, `里程统计:${vehicleServiceURL(vehicleServiceHash('mileage'))}` ]; copyText(lines.join('\n'), '车辆服务档案'); @@ -645,19 +654,19 @@ export function VehicleDetail({ '【车辆服务运营摘要】', `车辆:${vehicleName}`, `当前范围:${scopeText}`, - `服务状态:${serviceStatus?.title ?? serviceConclusion.status}`, + `服务状态:${customerStatusTitle(serviceStatus?.title) || serviceConclusion.status}`, `业务影响:${serviceConclusion.risk}`, `建议动作:${serviceConclusion.nextStep}`, - `在线证据:${evidenceSourceCount > 0 ? `${evidenceOnlineSourceCount}/${evidenceSourceCount}` : '-'}`, - `定位证据:${evidenceLocatedSourceCount > 0 ? `${evidenceLocatedSourceCount}` : '0'}`, + `在线数据通道:${evidenceSourceCount > 0 ? `${evidenceOnlineSourceCount}/${evidenceSourceCount}` : '-'}`, + `定位数据:${evidenceLocatedSourceCount > 0 ? `${evidenceLocatedSourceCount}` : '0'}`, `质量问题:${qualityCount.toLocaleString()} 项`, - `服务数据:轨迹 ${overview?.historyCount ?? detail.history.total ?? 0} / 原始记录 ${overview?.rawCount ?? detail.raw.total ?? 0} / 里程 ${overview?.mileageCount ?? detail.mileage.total ?? 0}`, + `服务数据:轨迹 ${overview?.historyCount ?? detail.history.total ?? 0} / 历史数据 ${overview?.rawCount ?? detail.raw.total ?? 0} / 里程 ${overview?.mileageCount ?? detail.mileage.total ?? 0}`, `下一步清单:${actionLines.length > 0 ? '' : '暂无明确处置项,持续观察实时状态。'}`, ...actionLines, `车辆服务:${vehicleServiceURL(vehicleServiceHash('detail'))}`, `实时监控:${vehicleServiceURL(vehicleServiceHash('realtime'))}`, `轨迹回放:${vehicleServiceURL(vehicleServiceHash('history'))}`, - `原始记录:${vehicleServiceURL(vehicleServiceHash('history-query', { tab: 'raw', includeFields: 'true' }))}`, + `历史数据:${vehicleServiceURL(vehicleServiceHash('history-query', { tab: 'raw', includeFields: 'true' }))}`, `里程统计:${vehicleServiceURL(vehicleServiceHash('mileage'))}` ]; copyText(lines.join('\n'), '运营摘要'); @@ -668,7 +677,7 @@ export function VehicleDetail({ value: lastSeen, meta: evidenceSourceCount > 0 ? `${evidenceOnlineSourceCount}/${evidenceSourceCount} 在线` : '暂无来源', color: online ? 'green' as const : 'orange' as const, - detail: '查看该车最新位置、速度、SOC、总里程和多证据实时字段。', + detail: '查看该车最新位置、速度、SOC、总里程和多通道实时字段。', disabled: !hasResolvedVIN, actions: [ { label: '查看实时', onClick: () => onOpenRealtime(resolvedVIN, activeProtocol) } @@ -686,14 +695,14 @@ export function VehicleDetail({ ] }, { - title: '原始记录', + title: '历史数据', value: `${formatCompactNumber(overview?.rawCount ?? detail?.raw?.total ?? 0)} 帧`, meta: latestRaw?.frameType || '字段明细', color: (overview?.rawCount ?? detail?.raw?.total ?? 0) > 0 ? 'blue' as const : 'grey' as const, - detail: '查看原始记录和字段明细,用于复核定位、里程和告警依据。', + detail: '查看历史明细和字段明细,用于复核定位、里程和告警依据。', disabled: !hasResolvedVIN, actions: [ - { label: '查看原始记录', onClick: () => onOpenRaw(resolvedVIN, activeProtocol) } + { label: '查看历史数据', onClick: () => onOpenRaw(resolvedVIN, activeProtocol) } ] }, { @@ -712,7 +721,7 @@ export function VehicleDetail({ value: `${formatCompactNumber(overview?.mileageCount ?? detail?.mileage?.total ?? 0)} 条统计`, meta: formatCompactNumber(evidenceMileageDelta, ' km 差异'), color: isFiniteNumber(evidenceMileageDelta) && Math.abs(evidenceMileageDelta) > 1 ? 'orange' as const : 'green' as const, - detail: '核对统计口径,确认区间总值、每日里程与证据来源是否闭合。', + detail: '核对统计口径,确认区间总值、每日里程与数据通道是否闭合。', disabled: !hasResolvedVIN, actions: [ { label: '查看里程', onClick: () => onOpenMileage(resolvedVIN, activeProtocol) } @@ -722,7 +731,7 @@ export function VehicleDetail({ const serviceRunbookSteps = [ { title: '确认实时状态', - evidence: evidenceSourceCount > 0 ? `${evidenceOnlineSourceCount}/${evidenceSourceCount} 在线,最后上报 ${lastSeen}` : '暂无在线证据', + evidence: evidenceSourceCount > 0 ? `${evidenceOnlineSourceCount}/${evidenceSourceCount} 在线,最后上报 ${lastSeen}` : '暂无在线数据', acceptance: '车辆在线状态、最新时间、有效坐标均可解释。', action: '打开实时', disabled: !hasResolvedVIN, @@ -730,17 +739,17 @@ export function VehicleDetail({ }, { title: '回放轨迹断点', - evidence: evidenceLocatedSourceCount > 0 ? `${evidenceLocatedSourceCount} 个证据有位置,时间差 ${formatSeconds(evidenceTimeDeltaSeconds)}` : '暂无可回放位置', + evidence: evidenceLocatedSourceCount > 0 ? `${evidenceLocatedSourceCount} 个通道有位置,时间差 ${formatSeconds(evidenceTimeDeltaSeconds)}` : '暂无可回放位置', acceptance: '轨迹点、速度和总里程变化连续,异常点可定位到时间段。', action: '轨迹回放', disabled: !hasResolvedVIN, onClick: () => onOpenHistory(resolvedVIN, activeProtocol) }, { - title: '核对原始记录', - evidence: `${formatCompactNumber(overview?.rawCount ?? detail?.raw?.total ?? 0)} 帧,最新类型 ${latestRaw?.frameType || '-'}`, + title: '查询历史数据', + evidence: `${formatCompactNumber(overview?.rawCount ?? detail?.raw?.total ?? 0)} 条历史数据,最新类型 ${latestRaw?.frameType || '-'}`, acceptance: '关键字段来自解析字段而不是页面二次推断。', - action: '原始记录', + action: '历史数据', disabled: !hasResolvedVIN, onClick: () => onOpenRaw(resolvedVIN, activeProtocol) }, @@ -755,7 +764,7 @@ export function VehicleDetail({ { title: '复核统计口径', evidence: `${formatCompactNumber(overview?.mileageCount ?? detail?.mileage?.total ?? 0)} 条统计,证据里程差 ${formatCompactNumber(evidenceMileageDelta, ' km')}`, - acceptance: '区间里程、每日里程和轨迹总里程证据可以互相解释。', + acceptance: '区间里程、每日里程和轨迹总里程可以互相解释。', action: '里程统计', disabled: !hasResolvedVIN, onClick: () => onOpenMileage(resolvedVIN, activeProtocol) @@ -771,8 +780,8 @@ export function VehicleDetail({ '【单车服务处理清单】', `车辆:${vehicleName}`, `当前范围:${scopeText}`, - `服务状态:${serviceStatus?.title ?? serviceConclusion.status}`, - `一致性:${consistencyTitle}`, + `服务状态:${customerStatusTitle(serviceStatus?.title) || serviceConclusion.status}`, + `一致性:${customerStatusTitle(consistencyTitle) || consistencyTitle}`, ...serviceRunbookSteps.map((item, index) => [ `${index + 1}. ${item.title}`, ` 证据:${item.evidence}`, @@ -780,7 +789,7 @@ export function VehicleDetail({ ].join('\n')), `实时监控:${vehicleServiceURL(vehicleServiceHash('realtime'))}`, `轨迹回放:${vehicleServiceURL(vehicleServiceHash('history'))}`, - `原始记录:${vehicleServiceURL(vehicleServiceHash('history-query', { tab: 'raw', includeFields: 'true' }))}`, + `历史数据:${vehicleServiceURL(vehicleServiceHash('history-query', { tab: 'raw', includeFields: 'true' }))}`, `告警事件:${vehicleServiceURL(buildAppHash({ page: 'alert-events', keyword: resolvedVIN, protocol: activeProtocol }))}`, `里程统计:${vehicleServiceURL(vehicleServiceHash('mileage'))}`, `车辆服务:${vehicleServiceURL(vehicleServiceHash('detail'))}` @@ -791,7 +800,7 @@ export function VehicleDetail({ { title: '看实时位置', value: lastSeen, - detail: evidenceSourceCount > 0 ? `${evidenceOnlineSourceCount}/${evidenceSourceCount} 在线,先确认车辆当前是否仍在上报。` : '暂无在线证据,优先确认数据接入。', + detail: evidenceSourceCount > 0 ? `${evidenceOnlineSourceCount}/${evidenceSourceCount} 在线,先确认车辆当前是否仍在上报。` : '暂无在线数据,优先确认数据接入。', color: online ? 'green' as const : 'orange' as const, primaryAction: '实时监控', secondaryAction: '复制摘要', @@ -811,11 +820,11 @@ export function VehicleDetail({ onSecondary: () => copyVehicleRunbook() }, { - title: '导出原始证据', + title: '查询历史数据', value: `${formatCompactNumber(overview?.rawCount ?? detail?.raw?.total ?? 0)} 帧`, - detail: latestRaw?.frameType ? `最新原始记录类型 ${latestRaw.frameType},用于核对解析字段。` : '按车辆和来源导出 RAW 与解析字段。', + detail: latestRaw?.frameType ? `最新历史数据类型 ${latestRaw.frameType},用于核对解析字段。` : '按车辆和数据通道查询历史明细与解析字段。', color: (overview?.rawCount ?? detail?.raw?.total ?? 0) > 0 ? 'blue' as const : 'grey' as const, - primaryAction: '数据导出', + primaryAction: '历史数据', secondaryAction: '导出档案', disabled: !hasResolvedVIN, onPrimary: () => onOpenRaw(resolvedVIN, activeProtocol), @@ -857,7 +866,7 @@ export function VehicleDetail({ { title: '轨迹回放', value: `${formatCompactNumber(overview?.historyCount ?? detail?.history?.total ?? 0)} 条`, - detail: evidenceLocatedSourceCount > 0 ? `${evidenceLocatedSourceCount} 个证据可定位` : '暂无可用定位证据', + detail: evidenceLocatedSourceCount > 0 ? `${evidenceLocatedSourceCount} 个通道可定位` : '暂无可用定位数据', color: evidenceLocatedSourceCount > 0 ? 'blue' as const : 'grey' as const, action: '回放轨迹', disabled: !hasResolvedVIN, @@ -902,12 +911,12 @@ export function VehicleDetail({ `车辆:${vehicleName}`, `当前范围:${scopeText}`, `平台能力:实时监控 / 轨迹回放 / 历史数据查询 / 告警通知 / 统计查询`, - `服务状态:${serviceStatus?.title ?? serviceConclusion.status}`, + `服务状态:${customerStatusTitle(serviceStatus?.title) || serviceConclusion.status}`, `服务说明:${serviceStatus?.detail ?? serviceConclusion.risk}`, - `在线证据:${evidenceSourceCount > 0 ? `${evidenceOnlineSourceCount}/${evidenceSourceCount}` : '-'}`, + `在线数据通道:${evidenceSourceCount > 0 ? `${evidenceOnlineSourceCount}/${evidenceSourceCount}` : '-'}`, `最新上报:${lastSeen}`, `轨迹证据:${overview?.historyCount ?? detail.history.total ?? 0} 条`, - `原始证据:${overview?.rawCount ?? detail.raw.total ?? 0} 帧`, + `历史数据:${overview?.rawCount ?? detail.raw.total ?? 0} 条`, `里程统计:${overview?.mileageCount ?? detail.mileage.total ?? 0} 条,证据差异 ${formatCompactNumber(evidenceMileageDelta, ' km')}`, `告警事件:${qualityCount.toLocaleString()} 项`, `车辆服务:${vehicleServiceURL(vehicleServiceHash('detail'))}`, @@ -950,22 +959,22 @@ export function VehicleDetail({ { section: '服务结论', item: '服务判断', value: serviceConclusion.status }, { section: '服务结论', item: '主要风险', value: serviceConclusion.risk }, { section: '服务结论', item: '下一步', value: serviceConclusion.nextStep }, - { section: '证据链', item: '证据来源', value: evidenceSourceCount > 0 ? `${evidenceOnlineSourceCount}/${evidenceSourceCount} 在线` : '-' }, - { section: '证据链', item: '定位证据', value: evidenceLocatedSourceCount > 0 ? `${evidenceLocatedSourceCount} 个证据有位置` : '暂无位置' }, - { section: '证据链', item: '里程差异', value: formatCompactNumber(evidenceMileageDelta, ' km') }, - { section: '证据链', item: '时间差异', value: formatSeconds(evidenceTimeDeltaSeconds) }, - { section: '证据链', item: '一致性结论', value: consistencyTitle }, - { section: '证据链', item: '一致性说明', value: consistencyDetail }, + { section: '车辆服务', item: '数据通道', value: evidenceSourceCount > 0 ? `${evidenceOnlineSourceCount}/${evidenceSourceCount} 在线` : '-' }, + { section: '车辆服务', item: '定位数据', value: evidenceLocatedSourceCount > 0 ? `${evidenceLocatedSourceCount} 个通道有位置` : '暂无位置' }, + { section: '车辆服务', item: '里程差异', value: formatCompactNumber(evidenceMileageDelta, ' km') }, + { section: '车辆服务', item: '时间差异', value: formatSeconds(evidenceTimeDeltaSeconds) }, + { section: '车辆服务', item: '一致性结论', value: customerStatusTitle(consistencyTitle) || consistencyTitle }, + { section: '车辆服务', item: '一致性说明', value: consistencyDetail }, { section: '数据规模', item: '历史记录', value: String(overview?.historyCount ?? detail.history.total ?? 0) }, - { section: '数据规模', item: '原始记录', value: String(overview?.rawCount ?? detail.raw.total ?? 0) }, + { section: '数据规模', item: '历史数据', value: String(overview?.rawCount ?? detail.raw.total ?? 0) }, { section: '数据规模', item: '里程记录', value: String(overview?.mileageCount ?? detail.mileage.total ?? 0) }, { section: '数据规模', item: '质量问题', value: String(qualityCount) } ]; for (const source of detail.sourceStatus ?? []) { rows.push( - { section: `证据来源 ${source.protocol}`, item: '在线', value: source.online ? '在线' : '离线' }, - { section: `证据来源 ${source.protocol}`, item: '最后时间', value: reportText(source.lastSeen) }, - { section: `证据来源 ${source.protocol}`, item: '能力', value: sourceCapabilities.filter((item) => source[item.key]).map((item) => item.label).join('|') || '-' } + { section: `数据通道 ${source.protocol}`, item: '在线', value: source.online ? '在线' : '离线' }, + { section: `数据通道 ${source.protocol}`, item: '最后时间', value: reportText(source.lastSeen) }, + { section: `数据通道 ${source.protocol}`, item: '能力', value: sourceCapabilities.filter((item) => source[item.key]).map((item) => item.label).join('|') || '-' } ); } downloadCsv(vehicleReportFileName(displayVIN, activeProtocol), buildCsv(vehicleReportColumns, rows)); @@ -974,7 +983,7 @@ export function VehicleDetail({ return (
- +
{ const nextQuery = { keyword: String(values.keyword ?? ''), protocol: String(values.protocol ?? '') }; @@ -983,7 +992,7 @@ export function VehicleDetail({ load(nextQuery); }}> - + GB32960 JT808 YUTONG_MQTT @@ -997,7 +1006,7 @@ export function VehicleDetail({ - + @@ -1007,7 +1016,7 @@ export function VehicleDetail({ 当前查看范围 {scopeText} - {activeProtocol ? '下方实时、轨迹、原始记录和里程仅展示该证据来源返回的数据。' : '下方数据按车辆聚合展示全部可用证据来源。'} + {activeProtocol ? '下方实时、轨迹、历史数据和里程仅展示该数据通道返回的数据。' : '下方数据按车辆聚合展示全部可用数据通道。'}
@@ -1015,7 +1024,7 @@ export function VehicleDetail({
车辆服务状态 - {serviceStatus.title} + {customerStatusTitle(serviceStatus.title)} {serviceStatus.detail}
@@ -1030,7 +1039,7 @@ export function VehicleDetail({
{scopeText} - {serviceStatus?.title ?? serviceConclusion.status} + {customerStatusTitle(serviceStatus?.title) || serviceConclusion.status} {[archivePlate, displayVIN].filter((item) => item && item !== '-').join(' / ') || displayLookupKey} @@ -1041,7 +1050,7 @@ export function VehicleDetail({
- +
@@ -1067,7 +1076,7 @@ export function VehicleDetail({ {hasResolvedVIN ? '已解析 VIN' : '待维护身份'} {scopeText} - {serviceStatus?.title ?? serviceConclusion.status} + {customerStatusTitle(serviceStatus?.title) || serviceConclusion.status} {[archivePlate, displayVIN].filter((item) => item && item !== '-').join(' / ') || displayLookupKey} @@ -1076,7 +1085,7 @@ export function VehicleDetail({ {[ { label: '手机号', value: archivePhone || '-' }, { label: 'OEM', value: archiveOEM || '-' }, - { label: '主证据来源', value: overview?.primaryProtocol || summary?.primaryProtocol || '-' }, + { label: '主数据通道', value: overview?.primaryProtocol || summary?.primaryProtocol || '-' }, { label: '最后上报', value: lastSeen }, { label: '档案完整度', value: archiveCompleteness }, { label: '下一步', value: serviceActions[0]?.label ?? serviceConclusion.nextStep } @@ -1115,7 +1124,7 @@ export function VehicleDetail({
- {serviceStatus?.title ?? serviceConclusion.status} + {customerStatusTitle(serviceStatus?.title) || serviceConclusion.status} {scopeText} {hasResolvedVIN ? 'VIN 已解析' : '身份待绑定'} @@ -1159,11 +1168,11 @@ export function VehicleDetail({ row data={[ { key: '服务车辆', value: [overview.plate, overview.vin].filter(Boolean).join(' / ') || '-' }, - { key: '在线证据', value: `${overview.onlineSourceCount}/${overview.sourceCount}` }, + { key: '在线数据通道', value: `${overview.onlineSourceCount}/${overview.sourceCount}` }, { key: '覆盖状态', value: {coverageStatusText[overview.coverageStatus] ?? overview.coverageStatus} }, - { key: '主证据来源', value: overview.primaryProtocol || '-' }, + { key: '主数据通道', value: overview.primaryProtocol || '-' }, { key: '最后上报', value: overview.lastSeen || '-' }, - { key: '服务数据', value: `轨迹 ${overview.historyCount} / 原始记录 ${overview.rawCount} / 里程 ${overview.mileageCount}` }, + { key: '服务数据', value: `轨迹 ${overview.historyCount} / 历史数据 ${overview.rawCount} / 里程 ${overview.mileageCount}` }, { key: '实时来源', value: overview.realtimeCount }, { key: '质量问题', value: qualityIssueAction(overview.qualityIssueCount) } ]} @@ -1282,15 +1291,15 @@ export function VehicleDetail({ {overview || consistency ? ( - +
{[ - { label: '证据来源', value: evidenceSourceCount > 0 ? `${evidenceOnlineSourceCount}/${evidenceSourceCount} 在线` : '-' }, - { label: '定位证据', value: evidenceLocatedSourceCount > 0 ? `${evidenceLocatedSourceCount} 个证据有位置` : '暂无位置' }, + { label: '数据通道', value: evidenceSourceCount > 0 ? `${evidenceOnlineSourceCount}/${evidenceSourceCount} 在线` : '-' }, + { label: '定位数据', value: evidenceLocatedSourceCount > 0 ? `${evidenceLocatedSourceCount} 个通道有位置` : '暂无位置' }, { label: '里程差异', value: formatCompactNumber(evidenceMileageDelta, ' km') }, { label: '时间差异', value: formatSeconds(evidenceTimeDeltaSeconds) }, - { label: '主证据来源', value: overview?.primaryProtocol || summary?.primaryProtocol || '-' }, - { label: '服务结论', value: consistencyTitle } + { label: '主数据通道', value: overview?.primaryProtocol || summary?.primaryProtocol || '-' }, + { label: '服务结论', value: customerStatusTitle(consistencyTitle) || consistencyTitle } ].map((item) => (
{item.label}
@@ -1305,7 +1314,7 @@ export function VehicleDetail({ {hasResolvedVIN && sourceFusionRows.length > 0 ? ( 多证据归并矩阵} + title={数据通道归并矩阵} style={{ marginTop: 16 }} >
@@ -1327,7 +1336,7 @@ export function VehicleDetail({ {[ { label: '实时', value: row.realtimeText }, { label: '定位', value: row.locationText }, - { label: '原始记录', value: row.rawText }, + { label: '历史数据', value: row.rawText }, { label: '里程', value: row.mileageText } ].map((item) => (
@@ -1349,7 +1358,7 @@ export function VehicleDetail({ - +
@@ -1424,7 +1433,7 @@ export function VehicleDetail({ {qualityIssueVehicleLabel(priorityQualityIssue, resolvedVIN)} - {priorityQualityIssue.detail || '该车存在告警事件,需要结合轨迹和原始记录确认影响范围。'} + {priorityQualityIssue.detail || '该车存在告警事件,需要结合轨迹和历史数据确认影响范围。'}
@@ -1445,13 +1454,13 @@ export function VehicleDetail({ 轨迹证据
@@ -1484,14 +1493,14 @@ export function VehicleDetail({ { key: '归并来源数', value: archiveSourceCount }, { key: '在线', value: }, { - key: '证据来源', + key: '数据通道', value: protocols.length > 0 ? ( ) : '-' }, - { key: '在线证据', value: summary ? `${summary.onlineSourceCount}/${summary.sourceCount}` : '-' }, + { key: '在线数据通道', value: summary ? `${summary.onlineSourceCount}/${summary.sourceCount}` : '-' }, { key: '最后位置时间', value: lastSeen }, - { key: '最新原始记录时间', value: latestRaw?.serverTime ?? '-' }, + { key: '最新历史数据时间', value: latestRaw?.serverTime ?? '-' }, { key: '质量问题', value: 0 ? 'orange' : 'green'}>{qualityCount > 0 ? `${qualityCount} 项` : '无'} } ]} /> @@ -1509,14 +1518,14 @@ export function VehicleDetail({ {hasResolvedVIN ? ( <> - + {detail?.sourceStatus?.length ? ( <>
- {activeProtocol ? `当前 ${activeProtocol}` : '当前 全部证据来源'} + {activeProtocol ? `当前 ${activeProtocol}` : '当前 全部数据通道'} - +
( @@ -1598,7 +1607,7 @@ export function VehicleDetail({ disabled={!row.hasRaw} onClick={() => onOpenRaw(resolvedVIN, row.protocol)} > - 查看 {row.protocol} 原始记录 + 查看 {row.protocol} 历史数据