diff --git a/vehicle-data-platform/apps/web/src/pages/VehicleDetail.tsx b/vehicle-data-platform/apps/web/src/pages/VehicleDetail.tsx index 80837226..b73a5129 100644 --- a/vehicle-data-platform/apps/web/src/pages/VehicleDetail.tsx +++ b/vehicle-data-platform/apps/web/src/pages/VehicleDetail.tsx @@ -450,6 +450,34 @@ export function VehicleDetail({ } return actions; }, [activeProtocol, detail?.sourceStatus, displayLookupKey, hasResolvedVIN, missingProtocols, onOpenQuality, onOpenRealtime, onOpenVehicles, qualityCount, resolvedVIN]); + const copyVehicleOperationsSummary = () => { + if (!detail) { + Toast.warning('当前没有可复制的运营摘要'); + return; + } + const vehicleName = [archivePlate, displayVIN].filter((item) => item && item !== '-').join(' / ') || displayLookupKey; + const actionLines = serviceActions.map((item, index) => `${index + 1}. ${item.label}:${item.description}`); + const lines = [ + '【车辆服务运营摘要】', + `车辆:${vehicleName}`, + `当前范围:${scopeText}`, + `服务状态:${serviceStatus?.title ?? serviceConclusion.status}`, + `业务影响:${serviceConclusion.risk}`, + `建议动作:${serviceConclusion.nextStep}`, + `在线来源:${evidenceSourceCount > 0 ? `${evidenceOnlineSourceCount}/${evidenceSourceCount}` : '-'}`, + `定位来源:${evidenceLocatedSourceCount > 0 ? `${evidenceLocatedSourceCount}` : '0'}`, + `质量问题:${qualityCount.toLocaleString()} 项`, + `数据规模:历史 ${overview?.historyCount ?? detail.history.total ?? 0} / RAW ${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'))}`, + `RAW证据:${vehicleServiceURL(vehicleServiceHash('history-query', { tab: 'raw', includeFields: 'true' }))}`, + `里程统计:${vehicleServiceURL(vehicleServiceHash('mileage'))}` + ]; + copyText(lines.join('\n'), '运营摘要'); + }; const detailWorkbench = [ { title: '实时定位', @@ -581,6 +609,7 @@ export function VehicleDetail({ + diff --git a/vehicle-data-platform/apps/web/src/test/App.test.tsx b/vehicle-data-platform/apps/web/src/test/App.test.tsx index 87161d9f..b9652a6e 100644 --- a/vehicle-data-platform/apps/web/src/test/App.test.tsx +++ b/vehicle-data-platform/apps/web/src/test/App.test.tsx @@ -10556,6 +10556,28 @@ test('copies vehicle service diagnostic summary', async () => { expect(copied).toContain('轨迹:http://localhost:3000/#/history?keyword=VIN-SUMMARY-001&protocol=JT808'); expect(copied).toContain('RAW:http://localhost:3000/#/history-query?keyword=VIN-SUMMARY-001&protocol=JT808&tab=raw&includeFields=true'); expect(copied).toContain('里程:http://localhost:3000/#/mileage?keyword=VIN-SUMMARY-001&protocol=JT808'); + + fireEvent.click(screen.getByRole('button', { name: /复制运营摘要/ })); + await waitFor(() => { + expect(writeText.mock.calls.some((call) => String(call[0]).includes('【车辆服务运营摘要】'))).toBe(true); + }); + const operationsCopied = String(writeText.mock.lastCall?.[0] ?? ''); + expect(operationsCopied).toContain('【车辆服务运营摘要】'); + expect(operationsCopied).toContain('车辆:粤A诊断1 / VIN-SUMMARY-001'); + expect(operationsCopied).toContain('当前范围:单一来源:JT808'); + expect(operationsCopied).toContain('服务状态:来源不完整'); + expect(operationsCopied).toContain('在线来源:1/2'); + expect(operationsCopied).toContain('定位来源:2'); + expect(operationsCopied).toContain('质量问题:1 项'); + expect(operationsCopied).toContain('下一步清单:'); + expect(operationsCopied).toContain('补齐 YUTONG_MQTT 来源'); + expect(operationsCopied).toContain('排查 GB32960 离线'); + expect(operationsCopied).toContain('处理质量问题 1 项'); + expect(operationsCopied).toContain('车辆服务:http://localhost:3000/#/detail?keyword=VIN-SUMMARY-001&protocol=JT808'); + expect(operationsCopied).toContain('实时监控:http://localhost:3000/#/realtime?keyword=VIN-SUMMARY-001&protocol=JT808'); + expect(operationsCopied).toContain('轨迹回放:http://localhost:3000/#/history?keyword=VIN-SUMMARY-001&protocol=JT808'); + expect(operationsCopied).toContain('RAW证据:http://localhost:3000/#/history-query?keyword=VIN-SUMMARY-001&protocol=JT808&tab=raw&includeFields=true'); + expect(operationsCopied).toContain('里程统计:http://localhost:3000/#/mileage?keyword=VIN-SUMMARY-001&protocol=JT808'); }); test('exports vehicle detail service dossier as csv', async () => {