diff --git a/vehicle-data-platform/apps/web/src/v2/pages/AccessPage.tsx b/vehicle-data-platform/apps/web/src/v2/pages/AccessPage.tsx
index c67325aa..ee1f12f3 100644
--- a/vehicle-data-platform/apps/web/src/v2/pages/AccessPage.tsx
+++ b/vehicle-data-platform/apps/web/src/v2/pages/AccessPage.tsx
@@ -511,6 +511,8 @@ export default function AccessPage() {
setTrendExpanded((value) => !value)} />
{visibleMetrics.length} / {allMetrics.length} 字段{resultSummary ? 查询 {resultSummary.queryDurationMs} ms : null}}
diff --git a/vehicle-data-platform/apps/web/src/v2/pages/OperationsPage.tsx b/vehicle-data-platform/apps/web/src/v2/pages/OperationsPage.tsx
index 76cfa25b..6cfd65ec 100644
--- a/vehicle-data-platform/apps/web/src/v2/pages/OperationsPage.tsx
+++ b/vehicle-data-platform/apps/web/src/v2/pages/OperationsPage.tsx
@@ -570,6 +570,8 @@ export default function OperationsPage() {
{readiness.isError ? readiness.refetch()} /> : null}
{!data ? '正在读取' : healthIssueCount > 0 ? `${healthIssueCount} 项需关注` : '全部正常'}15 秒自动刷新}
diff --git a/vehicle-data-platform/apps/web/src/v2/pages/ReconciliationCenter.tsx b/vehicle-data-platform/apps/web/src/v2/pages/ReconciliationCenter.tsx
index a7ba271b..f52f035d 100644
--- a/vehicle-data-platform/apps/web/src/v2/pages/ReconciliationCenter.tsx
+++ b/vehicle-data-platform/apps/web/src/v2/pages/ReconciliationCenter.tsx
@@ -333,6 +333,8 @@ export default function ReconciliationCenter() {
return <>
{ statistics.refetch(); mileage.refetch(); if (!hasVehicles) fleetVehicles.refetch(); }} /> : null}
{hasVehicles ? `${totalVehicles} 辆` : `${displayVehicles.length} / ${totalVehicles} 辆`}{dates.length} 个自然日}
diff --git a/vehicle-data-platform/apps/web/src/v2/productionEntry.test.ts b/vehicle-data-platform/apps/web/src/v2/productionEntry.test.ts
index c320f6dd..0aa635eb 100644
--- a/vehicle-data-platform/apps/web/src/v2/productionEntry.test.ts
+++ b/vehicle-data-platform/apps/web/src/v2/productionEntry.test.ts
@@ -73,6 +73,21 @@ describe('V2 production entry', () => {
expect(corePageSources[name]).toContain("from '../shared/WorkspaceCommandBar'");
expect(corePageSources[name]).toContain(' .v2-workspace-panel-copy');
expect(corePageSources.VehiclePage).toContain("lazy(() => import('./VehicleSearchWorkspace'))");
expect(vehicleSearchSource).toContain("from '../shared/WorkspaceCommandBar'");
expect(vehicleSearchSource).toContain(' {
rerender();
expect(screen.getByRole('heading', { name: '粤A12345', level: 5 }).closest('header')).toHaveClass('is-inverted');
});
+
+ test('adds one compact semantic context marker for primary data panels', () => {
+ render();
+
+ const header = screen.getByRole('heading', { name: '当前事件队列', level: 5 }).closest('header');
+ expect(header).toHaveClass('has-eyebrow', 'tone-warning');
+ expect(screen.getByText('实时队列')).toHaveClass('v2-workspace-panel-eyebrow', 'semi-typography');
+ });
});
diff --git a/vehicle-data-platform/apps/web/src/v2/shared/WorkspacePanelHeader.tsx b/vehicle-data-platform/apps/web/src/v2/shared/WorkspacePanelHeader.tsx
index 180b71d8..0a122505 100644
--- a/vehicle-data-platform/apps/web/src/v2/shared/WorkspacePanelHeader.tsx
+++ b/vehicle-data-platform/apps/web/src/v2/shared/WorkspacePanelHeader.tsx
@@ -3,29 +3,42 @@ import type { ReactNode } from 'react';
const { Text, Title } = Typography;
+export type WorkspacePanelTone = 'neutral' | 'primary' | 'warning' | 'authority' | 'health' | 'danger';
+
export function WorkspacePanelHeader({
title,
description,
+ eyebrow,
meta,
actions,
variant = 'default',
+ tone = 'neutral',
className = '',
actionsClassName = ''
}: {
title: ReactNode;
description?: ReactNode;
+ eyebrow?: ReactNode;
meta?: ReactNode;
actions?: ReactNode;
variant?: 'default' | 'compact' | 'inverted';
+ tone?: WorkspacePanelTone;
className?: string;
actionsClassName?: string;
}) {
- const headerClassName = ['v2-workspace-panel-header', `is-${variant}`, className].filter(Boolean).join(' ');
+ const headerClassName = [
+ 'v2-workspace-panel-header',
+ `is-${variant}`,
+ `tone-${tone}`,
+ eyebrow != null ? 'has-eyebrow' : '',
+ className
+ ].filter(Boolean).join(' ');
const trailingClassName = ['v2-workspace-panel-actions', actionsClassName].filter(Boolean).join(' ');
return (