diff --git a/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx b/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx
index 28561811..c30549e4 100644
--- a/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx
+++ b/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx
@@ -774,6 +774,60 @@ export function Dashboard({
const range = scope.dateFrom || scope.dateTo ? `${scope.dateFrom || '-'} 至 ${scope.dateTo || '-'}` : '全部时间';
return `${vehicle} / ${source} / ${range}`;
})();
+ const timeMonitorReviewItems = [
+ {
+ label: '车辆范围',
+ value: timeMonitorScope().keyword || '全部车辆',
+ detail: timeMonitorScope().protocol || '全部数据通道',
+ color: timeMonitorScope().keyword ? 'green' as const : 'blue' as const
+ },
+ {
+ label: '复盘时间',
+ value: timeMonitorScope().dateFrom || timeMonitorScope().dateTo ? `${timeMonitorScope().dateFrom || '-'} 至 ${timeMonitorScope().dateTo || '-'}` : '全部时间',
+ detail: '同步带入轨迹、里程、历史证据和告警',
+ color: 'blue' as const
+ },
+ {
+ label: '轨迹证据',
+ value: `${formatCount(summary?.activeToday)} 活跃`,
+ detail: '位置、速度、里程断点同窗复核',
+ color: 'blue' as const
+ },
+ {
+ label: '统计证据',
+ value: `${formatCount(serviceSummary?.totalVehicles)} 车辆`,
+ detail: '区间里程、日统计和异常点闭合',
+ color: 'green' as const
+ },
+ {
+ label: '风险事件',
+ value: `${formatCount(summary?.issueVehicles)} 告警`,
+ detail: highPriorityIssue ? qualityIssueLabel(highPriorityIssue.issueType) : '暂无高优先级',
+ 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}`,
+ '平台能力:实时地图 / 轨迹回放 / 里程统计 / 历史数据查询 / 告警通知',
+ `车辆范围:${scope.keyword || '全部车辆'}`,
+ `数据通道:${scope.protocol || '全部数据通道'}`,
+ `时间范围:${scope.dateFrom || '-'} 至 ${scope.dateTo || '-'}`,
+ `轨迹证据:今日活跃 ${formatCount(summary?.activeToday)},用于回放位置、速度、里程断点`,
+ `统计证据:车辆口径 ${formatCount(serviceSummary?.totalVehicles)},用于区间里程和日统计闭合`,
+ `历史证据:今日数据 ${formatCount(summary?.frameToday)},用于 RAW 与解析字段复核`,
+ `告警事件:${formatCount(summary?.issueVehicles)} 辆车存在告警`,
+ `轨迹回放:${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 }))}`,
+ `告警通知:${appURL(buildAppHash({ page: 'alert-events', keyword: scope.keyword, protocol: scope.protocol, filters: scope }))}`,
+ `实时地图:${appURL(buildAppHash({ page: 'map', keyword: scope.keyword, protocol: scope.protocol, filters: scope }))}`
+ ];
+ copyText(lines.join('\n'), '时间窗复盘包');
+ };
const timeMonitorWorkbench = [
{
label: '轨迹回放',
@@ -1308,6 +1362,34 @@ export function Dashboard({
同一时间窗会带入轨迹回放、里程统计和历史数据导出,避免跨页面重复输入。
+
+
+
+ 客户时间窗复盘包
+ {timeMonitorScope().keyword ? '单车复盘' : '全域复盘'}
+
+ {timeMonitorSummary}
+
+ 客户问某辆车、某段时间发生了什么时,直接按这个时间窗串起轨迹、里程、历史数据、告警通知和地图定位。
+
+
+
+
+
+
+
+
+
+
+ {timeMonitorReviewItems.map((item) => (
+
+ {item.label}
+ {item.value}
+ {item.detail}
+
+ ))}
+
+
diff --git a/vehicle-data-platform/apps/web/src/styles/global.css b/vehicle-data-platform/apps/web/src/styles/global.css
index 68df4dd5..9ad687cc 100644
--- a/vehicle-data-platform/apps/web/src/styles/global.css
+++ b/vehicle-data-platform/apps/web/src/styles/global.css
@@ -527,6 +527,59 @@ body {
outline: none;
}
+.vp-time-review-package {
+ margin-top: 14px;
+ display: grid;
+ grid-template-columns: minmax(300px, 0.86fr) minmax(0, 1.14fr);
+ gap: 12px;
+}
+
+.vp-time-review-summary {
+ min-height: 180px;
+ padding: 14px;
+ border: 1px solid rgba(0, 164, 184, 0.28);
+ border-radius: 8px;
+ background: #f3fbfc;
+ display: grid;
+ gap: 12px;
+ align-content: start;
+}
+
+.vp-time-review-summary .semi-typography {
+ line-height: 21px;
+}
+
+.vp-time-review-grid {
+ display: grid;
+ grid-template-columns: repeat(5, minmax(0, 1fr));
+ gap: 12px;
+}
+
+.vp-time-review-item {
+ min-height: 180px;
+ padding: 14px;
+ border: 1px solid var(--vp-border);
+ border-radius: 8px;
+ background: #fbfcff;
+ display: grid;
+ gap: 10px;
+ align-content: start;
+}
+
+.vp-time-review-item strong {
+ color: var(--vp-text);
+ font-size: 20px;
+ font-weight: 700;
+ line-height: 26px;
+ word-break: break-word;
+}
+
+.vp-time-review-item span {
+ color: var(--vp-text-muted);
+ font-size: 13px;
+ line-height: 20px;
+}
+
.vp-time-monitor-workbench {
margin-top: 14px;
display: grid;
@@ -3809,6 +3862,8 @@ button.vp-realtime-command-item:focus-visible {
.vp-vehicle-service-desk,
.vp-vehicle-service-grid,
.vp-vehicle-decision-grid,
+ .vp-time-review-package,
+ .vp-time-review-grid,
.vp-realtime-command-board,
.vp-realtime-command-grid,
.vp-realtime-impact-board,
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 3d5719f4..813b7810 100644
--- a/vehicle-data-platform/apps/web/src/test/App.test.tsx
+++ b/vehicle-data-platform/apps/web/src/test/App.test.tsx
@@ -813,6 +813,12 @@ test('dashboard exposes vehicle data center capability matrix', async () => {
expect(screen.getByText('应用时间窗')).toBeInTheDocument();
expect(screen.getByText('重置最近一天')).toBeInTheDocument();
expect(screen.getByText('当前时间窗')).toBeInTheDocument();
+ expect(screen.getByText('客户时间窗复盘包')).toBeInTheDocument();
+ expect(screen.getByText('客户问某辆车、某段时间发生了什么时,直接按这个时间窗串起轨迹、里程、历史数据、告警通知和地图定位。')).toBeInTheDocument();
+ expect(screen.getByText('车辆范围')).toBeInTheDocument();
+ expect(screen.getByText('复盘时间')).toBeInTheDocument();
+ expect(screen.getByText('统计证据')).toBeInTheDocument();
+ expect(screen.getByRole('button', { name: '复制复盘包' })).toBeInTheDocument();
expect(screen.getByText('这个范围会同步带入轨迹、里程、历史证据和告警复盘,适合客户问询、BI 核对和异常解释。')).toBeInTheDocument();
expect(screen.getByText('查看轨迹')).toBeInTheDocument();
expect(screen.getByText('统计里程')).toBeInTheDocument();
@@ -826,7 +832,7 @@ test('dashboard exposes vehicle data center capability matrix', async () => {
expect(screen.getByText('今天车辆轨迹')).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();
@@ -918,6 +924,12 @@ test('dashboard exposes vehicle data center capability matrix', async () => {
expect(new URLSearchParams(window.location.hash.split('?')[1] ?? '').get('dateTo')).toBeTruthy();
cleanup();
+ await renderDashboard();
+ fireEvent.click(screen.getByRole('button', { name: '复制复盘包' }));
+ expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【客户时间窗复盘包】'));
+ expect(writeText).toHaveBeenCalledWith(expect.stringContaining('平台能力'));
+ cleanup();
+
await renderDashboard();
fireEvent.click(screen.getByRole('button', { name: '导出原始记录' }));
expect(window.location.hash.startsWith('#/history-query')).toBe(true);