diff --git a/vehicle-data-platform/apps/web/src/pages/History.tsx b/vehicle-data-platform/apps/web/src/pages/History.tsx index 48122838..3a2cb47e 100644 --- a/vehicle-data-platform/apps/web/src/pages/History.tsx +++ b/vehicle-data-platform/apps/web/src/pages/History.tsx @@ -1036,6 +1036,84 @@ export function History({ ) : null} + +
+ {[ + { + title: '轨迹回放', + tag: validLocations.length > 0 ? '可回放' : '待查询', + tagColor: validLocations.length > 0 ? 'green' as const : 'orange' as const, + value: `${validLocations.length.toLocaleString()} 个有效点`, + detail: currentVehicleKeyword + ? '围绕当前车辆生成可播放轨迹,用于定位复盘和客户问询。' + : '先输入 VIN、车牌或手机号,再生成单车轨迹。', + primaryText: '播放轨迹', + secondaryText: '复制摘要', + disabled: validLocations.length === 0, + onPrimary: () => openQueryTab('location'), + onSecondary: copyTrajectorySummary + }, + { + title: '位置导出', + tag: locations.total > 0 ? '可交付' : '无数据', + tagColor: locations.total > 0 ? 'green' as const : 'grey' as const, + value: `${locations.total.toLocaleString()} 条位置`, + detail: '导出当前筛选范围的位置历史,支撑客户定位、BI 核对和离线复盘。', + primaryText: '导出位置', + secondaryText: '车辆服务', + disabled: locations.items.length === 0, + secondaryDisabled: !currentVehicleKeyword, + onPrimary: exportLocations, + onSecondary: () => currentVehicleKeyword && onOpenVehicle(currentVehicleKeyword, currentProtocol) + }, + { + title: '原始证据', + tag: (rawFrames.total ?? 0) > 0 ? '有证据' : '待加载', + tagColor: (rawFrames.total ?? 0) > 0 ? 'blue' as const : 'orange' as const, + value: `${(rawFrames.total ?? 0).toLocaleString()} 帧`, + detail: '保留接入侧原始记录,用于解释位置、里程和字段值的来源。', + primaryText: '看原始', + secondaryText: '导出RAW', + disabled: false, + secondaryDisabled: rawFrames.items.length === 0, + onPrimary: () => openQueryTab('raw'), + onSecondary: exportRawFrames + }, + { + title: '字段裁剪', + tag: selectedFieldCount > 0 ? `${selectedFieldCount} 个字段` : '全量字段', + tagColor: rawFieldRows.length > 0 ? 'green' as const : 'blue' as const, + value: `${rawFieldRows.length.toLocaleString()} 个字段`, + detail: selectedFieldCount > 0 + ? '按配置字段返回明细,减少接口体积并加快复核。' + : '需要字段级核对时,切到字段明细或填写字段裁剪。', + primaryText: '字段明细', + secondaryText: '导出字段', + disabled: false, + secondaryDisabled: rawFieldRows.length === 0, + onPrimary: () => openQueryTab('fields'), + onSecondary: exportRawFields + } + ].map((task) => ( +
+
+ {task.title} + {task.tag} +
+ {task.value} + {task.detail} + + + + +
+ ))} +
+
diff --git a/vehicle-data-platform/apps/web/src/styles/global.css b/vehicle-data-platform/apps/web/src/styles/global.css index 3ccc7ea8..33bf4890 100644 --- a/vehicle-data-platform/apps/web/src/styles/global.css +++ b/vehicle-data-platform/apps/web/src/styles/global.css @@ -2328,6 +2328,38 @@ button.vp-realtime-command-item:focus-visible { word-break: break-word; } +.vp-history-delivery-grid { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 12px; +} + +.vp-history-delivery-item { + min-height: 184px; + padding: 12px; + border: 1px solid var(--vp-border); + border-radius: var(--vp-radius); + background: #fbfcff; + display: grid; + gap: 10px; + align-content: start; +} + +.vp-history-delivery-head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; +} + +.vp-history-delivery-item strong { + color: var(--vp-text); + font-size: 20px; + line-height: 26px; + font-weight: 700; + word-break: break-word; +} + .vp-playback-layout { display: grid; grid-template-columns: minmax(0, 1fr) 260px; @@ -3338,6 +3370,7 @@ button.vp-realtime-command-item:focus-visible { .vp-trajectory-impact-grid, .vp-history-query-workbench, .vp-history-query-grid, + .vp-history-delivery-grid, .vp-source-fusion-grid, .vp-source-fusion-facts, .vp-vehicle-map-layout, 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 53ee2d0f..d7f7d569 100644 --- a/vehicle-data-platform/apps/web/src/test/App.test.tsx +++ b/vehicle-data-platform/apps/web/src/test/App.test.tsx @@ -6488,6 +6488,12 @@ test('shows trajectory playback workspace from history locations', async () => { render(); expect(await screen.findByRole('heading', { name: '轨迹回放' })).toBeInTheDocument(); + expect(screen.getByText('历史数据交付任务板')).toBeInTheDocument(); + expect(screen.getByText('2 个有效点')).toBeInTheDocument(); + expect(screen.getByText('围绕当前车辆生成可播放轨迹,用于定位复盘和客户问询。')).toBeInTheDocument(); + expect(screen.getByText('位置导出')).toBeInTheDocument(); + expect(screen.getByText('原始证据')).toBeInTheDocument(); + expect(screen.getAllByText('字段裁剪').length).toBeGreaterThanOrEqual(1); expect(screen.getByText('轨迹运营影响')).toBeInTheDocument(); expect(screen.getByText('轨迹范围')).toBeInTheDocument(); expect(screen.getByText('定位覆盖')).toBeInTheDocument();