feat: refine global shell for mobile landscape

This commit is contained in:
lingniu
2026-07-19 04:29:58 +08:00
parent 712721bc73
commit d3eb77e2c7
4 changed files with 420 additions and 5 deletions

View File

@@ -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]) => <NavLink key={to} to={to} aria-label={label} onPointerDown={() => warmRoute(to)} className={({ isActive }) => `v2-mobile-nav-item ${isActive ? 'is-active' : ''}`}><Icon size="large" /><span>{label}</span></NavLink>;
const link = ({ to, label, icon: Icon }: (typeof mobileMoreNavigation)[number]) => <NavLink key={to} to={to} aria-label={label} onPointerDown={() => warmRoute(to)} className={({ isActive }) => `v2-mobile-nav-item ${isActive ? 'is-active' : ''}`}><Icon size="large" /><span>{label}</span></NavLink>;
return <>
{more.length ? <SideSheet className="v2-mobile-more-sidesheet" visible={moreOpen} placement="bottom" height="auto" title={<div><strong></strong><span></span></div>} aria-label="更多功能" footer={null} onCancel={() => setMoreOpen(false)}><nav>{more.map(link)}</nav></SideSheet> : null}
{more.length ? <SideSheet
className="v2-mobile-more-sidesheet"
visible={moreOpen}
placement="bottom"
height="auto"
title={<div><strong></strong><span></span></div>}
aria-label="更多功能"
footer={null}
onCancel={() => setMoreOpen(false)}
>
<div className="v2-mobile-more-groups">
{moreGroups.map((group) => <section className="v2-mobile-more-section" key={group.key} aria-labelledby={`v2-mobile-more-${group.key}`}>
<header>
<div><strong id={`v2-mobile-more-${group.key}`}>{group.title}</strong><span>{group.description}</span></div>
<Tag size="small" color="blue">{group.items.length} </Tag>
</header>
<nav aria-label={group.title}>{group.items.map(link)}</nav>
</section>)}
</div>
</SideSheet> : null}
<nav className="v2-mobile-navigation" aria-label="主导航" style={{ '--v2-mobile-nav-count': Math.max(itemCount, 1) } as CSSProperties}>
{primary.map(link)}
{more.length ? <Button theme="borderless" className={`v2-mobile-nav-item${moreActive ? ' is-active' : ''}`} aria-label="更多功能" aria-expanded={moreOpen} aria-controls="v2-mobile-more" icon={<IconMore size="large" />} onClick={() => setMoreOpen((value) => !value)}><span></span></Button> : null}