From d3eb77e2c7cc71ecbb5915f66064d3024c17ff1e Mon Sep 17 00:00:00 2001 From: lingniu Date: Sun, 19 Jul 2026 04:29:58 +0800 Subject: [PATCH] feat: refine global shell for mobile landscape --- .../apps/web/src/v2/layout/AppShell.test.tsx | 4 + .../apps/web/src/v2/layout/AppShell.tsx | 45 ++- .../apps/web/src/v2/productionEntry.test.ts | 8 +- .../apps/web/src/v2/styles/workspace.css | 368 ++++++++++++++++++ 4 files changed, 420 insertions(+), 5 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 a1c4feb7..bf37eaec 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 @@ -187,6 +187,10 @@ test('uses the compact mobile navigation and opens secondary modules in a Semi S const moreSheet = screen.getByRole('dialog', { name: '更多功能' }); expect(moreSheet).toHaveTextContent('数据分析与系统管理'); + expect(screen.getByRole('navigation', { name: '分析与处置' })).toBeInTheDocument(); + 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(); 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 fba01eb1..da1b79b7 100644 --- a/vehicle-data-platform/apps/web/src/v2/layout/AppShell.tsx +++ b/vehicle-data-platform/apps/web/src/v2/layout/AppShell.tsx @@ -251,23 +251,60 @@ function PasswordDialog({ onClose, onChanged }: { onClose: () => void; onChanged } const mobilePrimaryNavigation = [navigation[0], navigation[1], navigation[2], navigation[4]]; -const mobileMoreNavigation = [navigation[3], navigation[5], navigation[6], navigation[7], { to: '/operations', menu: 'operations', label: '运维质量', icon: IconSetting }]; +const operationsNavigation = { to: '/operations', menu: 'operations', label: '运维质量', icon: IconSetting }; +const mobileMoreGroups = [ + { + key: 'insight', + title: '分析与处置', + description: '历史证据与业务告警', + items: [navigation[3], navigation[5]] + }, + { + key: 'governance', + title: '平台治理', + description: '接入、账号与运维质量', + items: [navigation[6], navigation[7], operationsNavigation] + } +]; +const mobileMoreNavigation = mobileMoreGroups.flatMap((group) => group.items); function MobileNavigation() { const location = useLocation(); const { session } = usePlatformSession(); const [moreOpen, setMoreOpen] = useState(false); const primary = mobilePrimaryNavigation.filter((item) => hasMenu(session, item.menu)); - const more = mobileMoreNavigation.filter((item) => hasMenu(session, item.menu)); + const moreGroups = mobileMoreGroups + .map((group) => ({ ...group, items: group.items.filter((item) => hasMenu(session, item.menu)) })) + .filter((group) => group.items.length > 0); + const more = moreGroups.flatMap((group) => group.items); 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', '更多功能', '关闭更多功能'); - const link = ({ to, label, icon: Icon }: (typeof navigation)[number]) => warmRoute(to)} className={({ isActive }) => `v2-mobile-nav-item ${isActive ? 'is-active' : ''}`}>{label}; + const link = ({ to, label, icon: Icon }: (typeof mobileMoreNavigation)[number]) => warmRoute(to)} className={({ isActive }) => `v2-mobile-nav-item ${isActive ? 'is-active' : ''}`}>{label}; return <> - {more.length ? 更多功能数据分析与系统管理} aria-label="更多功能" footer={null} onCancel={() => setMoreOpen(false)}> : null} + {more.length ? 更多功能数据分析与系统管理} + aria-label="更多功能" + footer={null} + onCancel={() => setMoreOpen(false)} + > +
+ {moreGroups.map((group) =>
+
+
{group.title}{group.description}
+ {group.items.length} 项 +
+ +
)} +
+
: null}