feat(platform): make mobile workflows task focused

This commit is contained in:
lingniu
2026-07-16 12:05:01 +08:00
parent 6d82a66d68
commit 069b324565
8 changed files with 227 additions and 13 deletions

View File

@@ -6,6 +6,7 @@ import {
IconHelpCircle,
IconHome,
IconMapPin,
IconMore,
IconSearch,
IconSetting,
IconUser,
@@ -71,12 +72,20 @@ export function AppShell() {
const activeRoutePath = `/${section}`;
const { session, logout } = usePlatformSession();
const [helpOpen, setHelpOpen] = useState(false);
const [mobileLayout, setMobileLayout] = useState(() => typeof window !== 'undefined' && window.matchMedia('(max-width: 680px)').matches);
const roleLabel = { viewer: '只读', operator: '处置员', admin: '管理员' }[session.role];
useEffect(() => scheduleIdleRoutePreloads({ activePathname: activeRoutePath }), [activeRoutePath]);
useEffect(() => {
const media = window.matchMedia('(max-width: 680px)');
const update = () => setMobileLayout(media.matches);
media.addEventListener('change', update);
update();
return () => media.removeEventListener('change', update);
}, []);
return (
<div className="v2-shell">
<Sidebar />
{mobileLayout ? <MobileNavigation /> : <Sidebar />}
<div className="v2-main">
<header className="v2-topbar">
<h1>{pageNames[section] ?? '车辆数据中台'}</h1>
@@ -93,6 +102,32 @@ export function AppShell() {
);
}
const mobilePrimaryNavigation = [navigation[0], navigation[1], navigation[2], navigation[4]];
const mobileMoreNavigation = [navigation[3], navigation[5], navigation[6], { to: '/operations', label: '运维质量', icon: IconSetting }];
function MobileNavigation() {
const location = useLocation();
const [moreOpen, setMoreOpen] = useState(false);
const moreActive = mobileMoreNavigation.some((item) => location.pathname.startsWith(item.to));
const warmRoute = (path: string) => { if (shouldPreloadRouteOnIntent()) void preloadRoute(path); };
useEffect(() => setMoreOpen(false), [location.pathname]);
useEffect(() => {
if (!moreOpen) return;
const closeOnEscape = (event: KeyboardEvent) => { if (event.key === 'Escape') setMoreOpen(false); };
window.addEventListener('keydown', closeOnEscape);
return () => window.removeEventListener('keydown', closeOnEscape);
}, [moreOpen]);
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>;
return <>
{moreOpen ? <div className="v2-mobile-more-backdrop" role="presentation" onMouseDown={(event) => { if (event.target === event.currentTarget) setMoreOpen(false); }}><section className="v2-mobile-more-sheet" role="dialog" aria-modal="true" aria-label="更多功能"><header><div><strong></strong><span></span></div><button type="button" aria-label="关闭更多功能" onClick={() => setMoreOpen(false)}>×</button></header><nav>{mobileMoreNavigation.map(link)}</nav></section></div> : null}
<nav className="v2-mobile-navigation" aria-label="主导航">
{mobilePrimaryNavigation.map(link)}
<button type="button" className={`v2-mobile-nav-item${moreActive ? ' is-active' : ''}`} aria-label="更多功能" aria-expanded={moreOpen} onClick={() => setMoreOpen((value) => !value)}><IconMore size="large" /><span></span></button>
</nav>
</>;
}
function Sidebar() {
const [collapsed, setCollapsed] = useState(false);
const warmRoute = (path: string) => { if (shouldPreloadRouteOnIntent()) void preloadRoute(path); };