From 018ec202568c2bcc32246b496324f1ef0f47fc4f Mon Sep 17 00:00:00 2001 From: lingniu Date: Sat, 18 Jul 2026 05:50:04 +0800 Subject: [PATCH] refine Semi UI responsive application shell --- .../apps/web/src/v2/layout/AppShell.test.tsx | 25 ++++++++ .../apps/web/src/v2/layout/AppShell.tsx | 7 ++- .../apps/web/src/v2/styles/v2.css | 2 +- .../apps/web/src/v2/styles/workspace.css | 60 ++++++++++++++++++- 4 files changed, 88 insertions(+), 6 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 a37f474d..165fa6b3 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 @@ -172,6 +172,8 @@ test('uses the compact mobile navigation and opens secondary modules in a Semi S }>页面内容} /> ); + expect(screen.getByRole('heading', { name: '全局监控' })).toHaveClass('v2-topbar-page-title'); + expect(screen.getByRole('navigation', { name: '主导航' })).toHaveStyle({ '--v2-mobile-nav-count': '5' }); expect(screen.getByRole('link', { name: '全局监控' })).toBeInTheDocument(); expect(screen.getByRole('link', { name: '车辆查询' })).toBeInTheDocument(); expect(screen.getByRole('link', { name: '轨迹回放' })).toBeInTheDocument(); @@ -193,6 +195,29 @@ test('uses the compact mobile navigation and opens secondary modules in a Semi S expect(document.body.style.overflow).not.toBe('hidden'); }); +test('lets customer mobile navigation use the full width when no more menu is needed', () => { + auth.session = { name: '客户甲', role: 'customer', userType: 'customer', menuKeys: ['monitor', 'vehicles', 'tracks', 'statistics'] }; + vi.spyOn(window, 'matchMedia').mockReturnValue({ + matches: true, + media: '(max-width: 680px)', + onchange: null, + addListener: vi.fn(), + removeListener: vi.fn(), + addEventListener: vi.fn(), + removeEventListener: vi.fn(), + dispatchEvent: vi.fn() + }); + + render( + }>页面内容} /> + ); + + expect(screen.getByRole('heading', { name: '里程查询' })).toHaveClass('v2-topbar-page-title'); + expect(screen.getByRole('navigation', { name: '主导航' })).toHaveStyle({ '--v2-mobile-nav-count': '4' }); + expect(screen.queryByRole('button', { name: '更多功能' })).not.toBeInTheDocument(); + expect(screen.getByRole('link', { name: '里程查询' })).toHaveAttribute('aria-current', 'page'); +}); + test('automatically collapses the Semi sidebar at tablet width', () => { vi.spyOn(window, 'matchMedia').mockImplementation((query) => ({ matches: query === '(max-width: 900px)', 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 93fb010e..b2ffae35 100644 --- a/vehicle-data-platform/apps/web/src/v2/layout/AppShell.tsx +++ b/vehicle-data-platform/apps/web/src/v2/layout/AppShell.tsx @@ -16,7 +16,7 @@ import { IconExit } from '@douyinfe/semi-icons'; import { Avatar, Banner, Button, Card, Dropdown, Input, Layout, Modal, Nav, SideSheet, Tag, Typography } from '@douyinfe/semi-ui'; -import { FormEvent, type MouseEvent, useEffect, useLayoutEffect, useState } from 'react'; +import { type CSSProperties, FormEvent, type MouseEvent, useEffect, useLayoutEffect, useState } from 'react'; import { NavLink, Outlet, useLocation, useNavigate } from 'react-router-dom'; import { api } from '../../api/client'; import { usePlatformSession } from '../auth/AuthGate'; @@ -150,7 +150,7 @@ export function AppShell() { {mobileLayout ? : }
-
羚牛车辆数据中台{pageNames[section] ?? '车辆数据中台'}
+
羚牛车辆数据中台{pageNames[section] ?? '车辆数据中台'}
hasMenu(session, item.menu)); const more = mobileMoreNavigation.filter((item) => hasMenu(session, item.menu)); const moreActive = more.some((item) => location.pathname.startsWith(item.to)); + const itemCount = primary.length + (more.length ? 1 : 0); const warmRoute = (path: string) => { if (shouldPreloadRouteOnIntent()) void preloadRoute(path); }; useEffect(() => setMoreOpen(false), [location.pathname]); useSideSheetA11y(moreOpen, '.v2-mobile-more-sidesheet', 'v2-mobile-more', '更多功能', '关闭更多功能'); @@ -261,7 +262,7 @@ function MobileNavigation() { const link = ({ to, label, icon: Icon }: (typeof navigation)[number]) => warmRoute(to)} className={({ isActive }) => `v2-mobile-nav-item ${isActive ? 'is-active' : ''}`}>{label}; return <> {more.length ? 更多功能数据分析与系统管理
} aria-label="更多功能" footer={null} onCancel={() => setMoreOpen(false)}> : null} -