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 (
+ {eyebrow != null ? {eyebrow} : null} {title} {description ? {description} : null}
diff --git a/vehicle-data-platform/apps/web/src/v2/styles/workspace.css b/vehicle-data-platform/apps/web/src/v2/styles/workspace.css index 553ded4a..d432e234 100644 --- a/vehicle-data-platform/apps/web/src/v2/styles/workspace.css +++ b/vehicle-data-platform/apps/web/src/v2/styles/workspace.css @@ -7,6 +7,10 @@ */ .v2-workspace-panel-header { + --v2-panel-accent: #708198; + --v2-panel-accent-soft: rgba(112, 129, 152, .08); + --v2-panel-chip-border: rgba(112, 129, 152, .16); + --v2-panel-chip-background: #f5f7fa; display: flex; min-height: 52px; flex: 0 0 auto; @@ -18,6 +22,47 @@ padding: 8px 14px; } +.v2-workspace-panel-header.tone-primary { + --v2-panel-accent: #1268df; + --v2-panel-accent-soft: rgba(18, 104, 223, .08); + --v2-panel-chip-border: rgba(18, 104, 223, .16); + --v2-panel-chip-background: #edf5ff; +} + +.v2-workspace-panel-header.tone-warning { + --v2-panel-accent: #d97706; + --v2-panel-accent-soft: rgba(217, 119, 6, .09); + --v2-panel-chip-border: rgba(217, 119, 6, .17); + --v2-panel-chip-background: #fff6e8; +} + +.v2-workspace-panel-header.tone-authority { + --v2-panel-accent: #6656c8; + --v2-panel-accent-soft: rgba(102, 86, 200, .08); + --v2-panel-chip-border: rgba(102, 86, 200, .16); + --v2-panel-chip-background: #f4f1ff; +} + +.v2-workspace-panel-header.tone-health { + --v2-panel-accent: #0f8f68; + --v2-panel-accent-soft: rgba(15, 143, 104, .08); + --v2-panel-chip-border: rgba(15, 143, 104, .16); + --v2-panel-chip-background: #eaf8f3; +} + +.v2-workspace-panel-header.tone-danger { + --v2-panel-accent: #d9473f; + --v2-panel-accent-soft: rgba(217, 71, 63, .08); + --v2-panel-chip-border: rgba(217, 71, 63, .16); + --v2-panel-chip-background: #fff2f1; +} + +.v2-workspace-panel-header.has-eyebrow { + background: + linear-gradient(90deg, var(--v2-panel-accent-soft) 0%, rgba(255, 255, 255, .94) 28%, #fff 100%); + box-shadow: inset 3px 0 var(--v2-panel-accent); +} + /* * Shared Semi UI mobile filter sheet. * Keep the same title hierarchy, scroll surface, condition card and action @@ -2534,6 +2579,43 @@ gap: 3px; } +.v2-workspace-panel-header.has-eyebrow > .v2-workspace-panel-copy { + display: grid; + grid-template-columns: auto minmax(0, 1fr); + grid-template-rows: auto auto; + align-items: center; + column-gap: 7px; + row-gap: 1px; +} + +.v2-workspace-panel-eyebrow.semi-typography { + display: inline-flex; + min-height: 18px; + align-items: center; + grid-column: 1; + grid-row: 1; + border: 1px solid var(--v2-panel-chip-border); + border-radius: 999px; + background: var(--v2-panel-chip-background); + padding-inline: 6px; + color: var(--v2-panel-accent); + font-size: 8px; + font-weight: 750; + letter-spacing: .04em; + line-height: 16px; + white-space: nowrap; +} + +.v2-workspace-panel-header.has-eyebrow > .v2-workspace-panel-copy > h5.semi-typography { + grid-column: 2; + grid-row: 1; +} + +.v2-workspace-panel-header.has-eyebrow > .v2-workspace-panel-copy > .semi-typography:last-child:not(.v2-workspace-panel-eyebrow) { + grid-column: 1 / -1; + grid-row: 2; +} + .v2-workspace-panel-header > .v2-workspace-panel-copy { align-items: flex-start; justify-content: center; @@ -15550,12 +15632,34 @@ } .v2-mileage-results .v2-workspace-panel-copy { - width: 82px; - min-width: 82px; - max-width: 82px; + width: 108px; + min-width: 108px; + max-width: 108px; flex: none; } + .v2-mileage-results .v2-workspace-panel-header.has-eyebrow > .v2-workspace-panel-copy { + grid-template-columns: minmax(0, 1fr); + grid-template-rows: auto auto; + align-content: center; + justify-items: start; + row-gap: 2px; + } + + .v2-mileage-results .v2-workspace-panel-header.has-eyebrow .v2-workspace-panel-eyebrow.semi-typography { + grid-column: 1; + grid-row: 1; + min-height: 16px; + padding-inline: 5px; + font-size: 7px; + line-height: 14px; + } + + .v2-mileage-results .v2-workspace-panel-header.has-eyebrow > .v2-workspace-panel-copy > h5.semi-typography { + grid-column: 1; + grid-row: 2; + } + .v2-mileage-results .v2-workspace-panel-copy > h5.semi-typography { width: auto; overflow: visible; diff --git a/vehicle-data-platform/design-qa.md b/vehicle-data-platform/design-qa.md index 4ad0c6c2..27cce4d3 100644 --- a/vehicle-data-platform/design-qa.md +++ b/vehicle-data-platform/design-qa.md @@ -151,4 +151,51 @@ - 首次截图在 SideSheet 进入动画期间截到不完整底栏;等待动画稳定后复核,重置与应用按钮完整可见。 - 同视口对比确认新增摘要没有压缩关键输入、日期范围和来源选择,未发现 P0、P1 或 P2 问题。 +## 共享数据面板标题 Semi UI Design QA + +- source visual truth: ECS 生产端里程查询、告警中心、接入管理与运维质量结果面板 +- source screenshots: + - statistics baseline: `/Users/lingniu/project/ai-coding/lingniu-vehicle-ingest/.codex-audit/semi-panel-hierarchy-20260720/desktop-statistics-after.png` + - mobile statistics baseline: `/Users/lingniu/project/ai-coding/lingniu-vehicle-ingest/.codex-audit/semi-panel-hierarchy-20260720/mobile-statistics-after.png` +- implementation screenshots: + - statistics desktop: `/Users/lingniu/project/ai-coding/lingniu-vehicle-ingest/.codex-audit/semi-panel-hierarchy-20260720/desktop-statistics-fixed.png` + - statistics mobile: `/Users/lingniu/project/ai-coding/lingniu-vehicle-ingest/.codex-audit/semi-panel-hierarchy-20260720/mobile-statistics-fixed.png` + - alert center desktop: `/Users/lingniu/project/ai-coding/lingniu-vehicle-ingest/.codex-audit/semi-panel-hierarchy-20260720/desktop-alerts-fixed.png` + - access center desktop: `/Users/lingniu/project/ai-coding/lingniu-vehicle-ingest/.codex-audit/semi-panel-hierarchy-20260720/desktop-access-fixed.png` + - operations desktop: `/Users/lingniu/project/ai-coding/lingniu-vehicle-ingest/.codex-audit/semi-panel-hierarchy-20260720/desktop-operations-fixed.png` +- viewport and state: + - mobile `390 × 844` + - desktop `1440 × 900` + - 管理员权限、生产数据、默认查询状态 + +### Fidelity Ledger + +| Surface | Required fidelity | Implementation evidence | Result | +| --- | --- | --- | --- | +| Semantic hierarchy | 高频结果区必须先说明数据语境,再给出当前任务标题。 | 共享 `WorkspacePanelHeader` 新增真实眉题层;里程、告警、接入、历史、对账和运维分别使用“每日统计”“实时队列”“接入差异”等业务语义。 | Improved. | +| Tone consistency | 不同业务状态需要可辨认,但不能形成高饱和装饰色块。 | 使用共享 primary、warning、health 等低对比度语义变量,只在顶部细线、眉题与轻微渐变中表达状态。 | Passed. | +| Data density | 标题升级不能挤压表格、统计条和分页主体。 | `1440 × 900` 下四个生产结果页保留原表格行数、筛选条件、分页和滚动边界。 | Preserved. | +| Mobile hierarchy | 眉题、结果标题、车辆数量和操作不能在窄屏互相遮挡。 | 里程结果头在 `390px` 下缩短描述并收敛文本轨道;车辆数量、刷新和导出仍完整可见。 | Passed. | +| Action continuity | 刷新、导出和结果计数仍需保持原按钮语义与点击范围。 | 共享组件只调整标题结构,原 Semi Button、Tag、事件处理器和可访问名称均未替换。 | Preserved. | +| Shared implementation | 页面不能各自复制标题视觉规则。 | 六个页面复用同一 `WorkspacePanelHeader` 与统一 tone 类型,样式集中在 `workspace.css`。 | Passed. | +| Desktop stability | 共享视觉层不得产生横向溢出、截断或固定列错位。 | 里程、告警、接入和运维生产截图均无标题裁切,列表滚动条、固定列及分页位置正常。 | Passed. | + +### Interaction And Accessibility Checks + +- 结果标题继续输出正确的 Heading 层级,眉题使用 Semi Typography 文本而不是模拟标题; +- 里程查询的“刷新里程数据”和“导出 Excel”保留完整可访问名称; +- 告警、接入和运维结果列表保留原筛选、刷新、分页与纵向滚动交互; +- `390 × 844` 下里程矩阵车牌列与区间总里程列继续固定,日期列可横向浏览; +- `1440 × 900` 下四个重点页面没有出现标题折断、操作区覆盖或列表主体缩水; +- 生产发布版本 `semi-panel-hierarchy-20260720031800` 完成资源烟测与登录态复核。 + +### Comparison History + +#### Pass 1 — fixed + +- 首轮移动端复核发现新增眉题、结果标题和车辆数量在同一窄轨道内显得拥挤。 +- 通过共享标题的移动端两级文本布局和里程结果区专用宽度约束,减少无效描述占位,同时保留结果计数与操作。 +- 第二轮同视口截图确认“每日统计—车辆每日里程—20 / 1024 辆”层级可辨,刷新、导出和首屏表格均可达。 +- 桌面端里程、告警、接入与运维四个结果面板语义色克制、密度稳定,未发现 P0、P1 或 P2 视觉与交互问题。 + final result: passed