From f8baaf53ba26ae9287ad93fc30d0fac8e9ea2fa0 Mon Sep 17 00:00:00 2001 From: lingniu Date: Sun, 19 Jul 2026 21:40:31 +0800 Subject: [PATCH] refactor(ui): refine semi mobile workspace launcher --- .../apps/web/src/v2/layout/AppShell.test.tsx | 11 +- .../apps/web/src/v2/layout/AppShell.tsx | 25 +++- .../apps/web/src/v2/styles/workspace.css | 127 ++++++++++++++++++ vehicle-data-platform/design-qa.md | 49 +++++++ 4 files changed, 201 insertions(+), 11 deletions(-) diff --git a/vehicle-data-platform/apps/web/src/v2/layout/AppShell.test.tsx b/vehicle-data-platform/apps/web/src/v2/layout/AppShell.test.tsx index 75ce78ee..d4fcff0b 100644 --- a/vehicle-data-platform/apps/web/src/v2/layout/AppShell.test.tsx +++ b/vehicle-data-platform/apps/web/src/v2/layout/AppShell.test.tsx @@ -197,11 +197,12 @@ test('uses the compact mobile navigation and opens secondary modules in a Semi S expect(screen.getByRole('navigation', { name: '平台治理' })).toBeInTheDocument(); expect(moreSheet).toHaveTextContent('历史证据与业务告警'); expect(moreSheet).toHaveTextContent('接入、账号与运维质量'); - expect(screen.getByRole('link', { name: '历史数据' })).toBeInTheDocument(); - expect(screen.getByRole('link', { name: '告警中心' })).toBeInTheDocument(); - expect(screen.getByRole('link', { name: '接入管理' })).toBeInTheDocument(); - expect(screen.getByRole('link', { name: '账号管理' })).toBeInTheDocument(); - expect(screen.getByRole('link', { name: '运维质量' })).toBeInTheDocument(); + expect(screen.getByRole('link', { name: '历史数据' })).toHaveTextContent('按时间回溯原始上报'); + expect(screen.getByRole('link', { name: '告警中心' })).toHaveTextContent('集中查看并闭环风险'); + expect(screen.getByRole('link', { name: '接入管理' })).toHaveTextContent('核对身份、协议与差异'); + expect(screen.getByRole('link', { name: '账号管理' })).toHaveTextContent('管理客户与车辆权限'); + expect(screen.getByRole('link', { name: '运维质量' })).toHaveTextContent('诊断链路与数据质量'); + expect(moreSheet.querySelectorAll('.v2-mobile-more-item-copy')).toHaveLength(5); fireEvent.click(screen.getByLabelText('关闭更多功能')); expect(screen.getByRole('button', { name: '更多功能' })).toHaveAttribute('aria-expanded', 'false'); diff --git a/vehicle-data-platform/apps/web/src/v2/layout/AppShell.tsx b/vehicle-data-platform/apps/web/src/v2/layout/AppShell.tsx index 75c9797b..65ea24c4 100644 --- a/vehicle-data-platform/apps/web/src/v2/layout/AppShell.tsx +++ b/vehicle-data-platform/apps/web/src/v2/layout/AppShell.tsx @@ -4,6 +4,7 @@ import { IconBox, IconChevronDown, IconChevronLeft, + IconChevronRight, IconHelpCircle, IconHome, IconLock, @@ -283,13 +284,20 @@ const mobileMoreGroups = [ key: 'insight', title: '分析与处置', description: '历史证据与业务告警', - items: [navigation[3], navigation[5]] + items: [ + { ...navigation[3], description: '按时间回溯原始上报' }, + { ...navigation[5], description: '集中查看并闭环风险' } + ] }, { key: 'governance', title: '平台治理', description: '接入、账号与运维质量', - items: [navigation[6], navigation[7], operationsNavigation] + items: [ + { ...navigation[6], description: '核对身份、协议与差异' }, + { ...navigation[7], description: '管理客户与车辆权限' }, + { ...operationsNavigation, description: '诊断链路与数据质量' } + ] } ]; const mobileMoreNavigation = mobileMoreGroups.flatMap((group) => group.items); @@ -307,14 +315,19 @@ function MobileNavigation() { const itemCount = primary.length + (more.length ? 1 : 0); const warmRoute = (path: string) => { if (shouldPreloadRouteOnIntent()) void preloadRoute(path); }; useEffect(() => setMoreOpen(false), [location.pathname]); - const link = ({ to, label, icon: Icon }: (typeof mobileMoreNavigation)[number]) => warmRoute(to)} className={({ isActive }) => `v2-mobile-nav-item ${isActive ? 'is-active' : ''}`}>{label}; + const primaryLink = ({ to, label, icon: Icon }: (typeof navigation)[number]) => warmRoute(to)} className={({ isActive }) => `v2-mobile-nav-item ${isActive ? 'is-active' : ''}`}>{label}; + const moreLink = ({ to, label, description, icon: Icon }: (typeof mobileMoreNavigation)[number]) => warmRoute(to)} className={({ isActive }) => `v2-mobile-nav-item v2-mobile-more-item ${isActive ? 'is-active' : ''}`}> + + {label}{description} + ; return <> {more.length ? } title="更多功能" description="数据分析、业务处置与平台治理" @@ -329,12 +342,12 @@ function MobileNavigation() {
{group.title}{group.description}
{group.items.length} 项 - + )}
: 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 2c7d974d..2460d5ec 100644 --- a/vehicle-data-platform/apps/web/src/v2/styles/workspace.css +++ b/vehicle-data-platform/apps/web/src/v2/styles/workspace.css @@ -22476,11 +22476,138 @@ padding: 8px 9px 10px; } + .v2-mobile-more-sidesheet .v2-mobile-more-groups { + gap: 14px; + } + + .v2-mobile-more-sidesheet .v2-mobile-more-section { + overflow: visible; + border: 0; + border-radius: 0; + background: transparent; + } + + .v2-mobile-more-sidesheet .v2-mobile-more-section > header { + min-height: 38px; + border: 0; + padding: 2px 2px 7px; + } + + .v2-mobile-more-sidesheet .v2-mobile-more-section > header strong { + color: #1f3047; + font-size: 12px; + } + + .v2-mobile-more-sidesheet .v2-mobile-more-section > header span { + font-size: 9px; + } + + .v2-mobile-more-sidesheet .v2-mobile-more-section > nav, + .v2-mobile-more-sidesheet .v2-mobile-more-section:first-child > nav { + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 8px; + padding: 0; + } + + .v2-mobile-more-sidesheet .v2-mobile-more-section .v2-mobile-nav-item.v2-mobile-more-item { + display: grid; + min-height: 68px; + grid-template-columns: 34px minmax(0, 1fr) 12px; + align-items: center; + justify-content: initial; + gap: 8px; + border: 1px solid #e0e7f0; + border-radius: 12px; + background: #fff; + padding: 10px; + color: #506178; + text-align: left; + box-shadow: 0 5px 16px rgba(31, 54, 83, .045); + } + + .v2-mobile-more-sidesheet .v2-mobile-more-section .v2-mobile-nav-item.v2-mobile-more-item:hover, + .v2-mobile-more-sidesheet .v2-mobile-more-section .v2-mobile-nav-item.v2-mobile-more-item:active { + border-color: #cbdaf0; + background: #f8fbff; + } + + .v2-mobile-more-sidesheet .v2-mobile-more-section .v2-mobile-nav-item.v2-mobile-more-item.is-active { + border-color: #a9c9fb; + background: #eef5ff; + color: #1268f3; + box-shadow: 0 7px 20px rgba(18, 104, 243, .1); + } + + .v2-mobile-more-sidesheet .v2-mobile-more-item.is-active::after { + display: none; + } + + .v2-mobile-more-item-icon { + display: grid; + width: 34px; + height: 34px; + place-items: center; + border: 1px solid #dce7f6; + border-radius: 10px; + background: #f3f7fc; + color: #58708d; + font-size: 16px; + } + + .v2-mobile-more-item.is-active .v2-mobile-more-item-icon { + border-color: #c8dcfb; + background: #fff; + color: #1268f3; + } + + .v2-mobile-more-item-copy { + display: grid; + min-width: 0; + gap: 3px; + } + + .v2-mobile-more-item-copy strong, + .v2-mobile-more-item-copy small { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .v2-mobile-more-item-copy strong { + color: #263951; + font-size: 11px; + font-weight: 750; + line-height: 1.2; + } + + .v2-mobile-more-item-copy small { + color: #8794a7; + font-size: 8px; + line-height: 1.25; + } + + .v2-mobile-more-item.is-active .v2-mobile-more-item-copy strong { + color: #1268f3; + } + + .v2-mobile-more-item-chevron { + color: #a5b0bf; + font-size: 11px; + } + .v2-vehicle-mobile-actions-sidesheet .v2-workspace-config-content { padding: 9px 11px; } } +@media (max-width: 600px) { + :root .v2-mobile-navigation .v2-mobile-nav-item > span, + :root .v2-mobile-navigation > .semi-button.v2-mobile-nav-item .semi-button-content { + font-size: 10px; + letter-spacing: .01em; + } +} + @media (max-width: 900px) and (max-height: 480px) and (orientation: landscape) { .v2-workspace-action-sidesheet.v2-vehicle-mobile-actions-sidesheet.semi-sidesheet-bottom .semi-sidesheet-inner { height: min(100dvh, 380px) !important; diff --git a/vehicle-data-platform/design-qa.md b/vehicle-data-platform/design-qa.md index 7a0c2f9f..b3d8aae8 100644 --- a/vehicle-data-platform/design-qa.md +++ b/vehicle-data-platform/design-qa.md @@ -47,4 +47,53 @@ - 同视口并排对比确认地图在首屏获得约 `83px` 的额外可见空间,车型和里程信息完整。 - 未发现 P0、P1 或 P2 视觉与交互问题。 +## 全站移动导航 Semi UI Design QA + +- source visual truth: ECS 生产端 `/monitor?mode=list` +- source screenshots: + - shell baseline: `/Users/lingniu/project/ai-coding/lingniu-vehicle-ingest/.codex-audit/semi-ui-shell/before-monitor-mobile.png` + - more baseline: `/Users/lingniu/project/ai-coding/lingniu-vehicle-ingest/.codex-audit/semi-ui-shell/before-more-sheet.png` + - help baseline: `/Users/lingniu/project/ai-coding/lingniu-vehicle-ingest/.codex-audit/semi-ui-shell/before-help-sheet.png` + - statistics filter baseline: `/Users/lingniu/project/ai-coding/lingniu-vehicle-ingest/.codex-audit/semi-ui-shell/before-statistics-filter.png` +- implementation screenshots: + - more launcher: `/Users/lingniu/project/ai-coding/lingniu-vehicle-ingest/.codex-audit/semi-ui-shell/after-more-sheet-local.png` + - active module: `/Users/lingniu/project/ai-coding/lingniu-vehicle-ingest/.codex-audit/semi-ui-shell/after-more-active-fixed-local.png` +- comparison evidence: + - before/after: `/Users/lingniu/project/ai-coding/lingniu-vehicle-ingest/.codex-audit/semi-ui-shell/compare-more-before-after.png` +- viewport and state: + - mobile `390 × 844` + - 管理员权限、全局监控列表模式 + +### Fidelity Ledger + +| Surface | Required fidelity | Implementation evidence | Result | +| --- | --- | --- | --- | +| Permission model | 客户与管理员仍只能看到已授权菜单。 | 继续使用 `hasMenu()` 过滤主导航和更多功能分组,未新增绕过入口。 | Preserved. | +| Navigation hierarchy | 高频任务保留底栏,低频任务必须有清晰业务分组。 | 全局监控、车辆查询、轨迹回放、里程查询仍在底栏;历史/告警与平台治理维持两组。 | Passed. | +| Module recognition | 更多功能不能只靠小图标和短标签辨认。 | 每个模块增加真实任务说明、独立 Semi 图标容器和进入指示。 | Improved. | +| Current state | 位于二级模块时,重新打开更多功能必须识别当前位置。 | 当前模块使用蓝色边框、浅蓝底色和图标强调,未叠加重复装饰线。 | Passed. | +| Touch density | 入口需要适合移动端点击,同时单屏展示完整模块集合。 | 模块卡片最小高度 `68px`,两列布局在 `390 × 844` 下完整显示五个入口。 | Passed. | +| Shared shell | 帮助、筛选与更多功能应属于同一 Semi UI 工作台体系。 | 继续复用 `WorkspaceSideSheet` 的标题、徽标、遮罩、圆角和滚动边界。 | Preserved. | +| Asset fidelity | 不使用临时图形或自制 SVG。 | 全部使用 `@douyinfe/semi-icons` 与 Semi UI `SideSheet`、`Tag`、`Button`。 | Preserved. | + +### Interaction And Accessibility Checks + +- 底栏仍为命名为“主导航”的可访问导航区域; +- 更多按钮继续暴露 `aria-expanded` 和 `aria-controls`; +- 两个业务分组均为独立命名导航; +- 五个模块入口保留链接语义与预加载行为; +- 当前模块继续由 React Router 输出 `aria-current="page"`; +- 关闭按钮仍使用“关闭更多功能”的可访问名称; +- `390 × 844` 同视口前后对照未出现横向溢出、裁切或不可达入口; +- 页面帮助和里程筛选基线检查未发现本次共享样式导致的回退。 + +### Comparison History + +#### Pass 1 — fixed + +- 旧版入口卡片依赖小图标和短标签,任务意图不够明确,分组又被大面积边框切割。 +- 新版保持原路由与权限边界,用两列 Semi UI 模块卡片展示任务说明,并减弱分组容器噪声。 +- 首次视觉复核发现当前模块仍继承底栏的底部装饰线;已在更多功能作用域内移除,保留单一高亮语义。 +- 同尺寸并排对比确认五个入口均可见,层级更清晰,未发现 P0、P1 或 P2 视觉与交互问题。 + final result: passed