import React, { useState, ReactNode, useRef, useEffect } from 'react'; import { Layout, Menu, Breadcrumb, Spin } from '@arco-design/web-react'; import cs from 'classnames'; import { IconApps, IconMenuFold, IconMenuUnfold, IconArchive, IconUnorderedList, IconMindMapping, IconCommon, IconCode, IconHome, IconStorage } from '@arco-design/web-react/icon'; import { useSelector } from 'react-redux'; import { useRouter } from 'next/router'; import Link from 'next/link'; import qs from 'query-string'; import Navbar from '../components/NavBar'; import Footer from '../components/Footer'; import useRoute, { IRoute } from '@/routes'; import useLocale from '@/utils/useLocale'; import { GlobalState } from '@/store'; import getUrlParams from '@/utils/getUrlParams'; import styles from '@/style/layout.module.less'; import NoAccess from '@/pages/exception/403'; const MenuItem = Menu.Item; const SubMenu = Menu.SubMenu; const Sider = Layout.Sider; const Content = Layout.Content; function getIconFromKey(key) { switch (key) { case 'dashboard/workplace': return ; case 'scene': return ; case 'application': return ; case 'instance': return ; case 'componentDevelopment': return ; case 'componentLibrary': return ; case 'compositeCompLibrary': return ; case 'componentMarket': return ; default: return
; } } function PageLayout({ children }: { children: ReactNode }) { const urlParams = getUrlParams(); const router = useRouter(); const pathname = router.pathname; const currentComponent = qs.parseUrl(pathname).url.slice(1); const locale = useLocale(); const { userInfo, settings, userLoading } = useSelector( (state: GlobalState) => state ); const [collapsed, setCollapsed] = useState(false); const [routes, defaultRoute] = useRoute(userInfo?.permissions); const defaultSelectedKeys = [currentComponent || defaultRoute]; const paths = (currentComponent || defaultRoute).split('/'); const defaultOpenKeys = paths.slice(0, paths.length - 1); const [selectedKeys, setSelectedKeys] = useState(defaultSelectedKeys); const [openKeys, setOpenKeys] = useState(defaultOpenKeys); const navbarHeight = 60; const menuWidth = collapsed ? 48 : settings?.menuWidth; const showNavbar = settings?.navbar && urlParams.navbar !== false; const showMenu = settings?.menu && urlParams.menu !== false; const showFooter = settings?.footer && urlParams.footer !== false; const routeMap = useRef>(new Map()); const menuMap = useRef< Map >(new Map()); const [breadcrumb, setBreadCrumb] = useState([]); function onClickMenuItem(key) { setSelectedKeys([key]); } function toggleCollapse() { setCollapsed((collapsed) => !collapsed); } const paddingLeft = showMenu ? { paddingLeft: menuWidth } : {}; const paddingTop = showNavbar ? { paddingTop: navbarHeight } : {}; const paddingStyle = { ...paddingLeft, ...paddingTop }; // 初始化时创建并实例路由映射 useEffect(() => { // 仅构建 routeMap,不渲染菜单 function buildRouteMap(_routes: IRoute[], parentNode = []) { _routes.forEach((route) => { const { breadcrumb = true, ignore } = route; routeMap.current.set( `/${route.key}`, breadcrumb ? [...parentNode, route.name] : [] ); const visibleChildren = (route.children || []).filter((child) => { const { ignore, breadcrumb = true } = child; if (ignore || route.ignore) { routeMap.current.set( `/${child.key}`, breadcrumb ? [...parentNode, route.name, child.name] : [] ); } return !ignore; }); if (!ignore && visibleChildren.length) { buildRouteMap(visibleChildren, [...parentNode, route.name]); } }); } buildRouteMap(routes); }, [routes]); function renderRoutes(locale) { return function travel(_routes: IRoute[], level, parentNode = []) { return _routes.map((route) => { const { ignore } = route; const iconDom = getIconFromKey(route.key); const titleDom = ( <> {iconDom} {locale[route.name] || route.name} ); const visibleChildren = (route.children || []).filter((child) => { return !child.ignore; }); if (ignore) { return ''; } if (visibleChildren.length) { menuMap.current.set(route.key, { subMenu: true }); return ( {travel(visibleChildren, level + 1, [...parentNode, route.name])} ); } menuMap.current.set(route.key, { menuItem: true }); return ( {titleDom} ); }); }; } function updateMenuStatus() { const pathKeys = pathname.split('/'); const newSelectedKeys: string[] = []; const newOpenKeys: string[] = [...openKeys]; while (pathKeys.length > 0) { const currentRouteKey = pathKeys.join('/'); const menuKey = currentRouteKey.replace(/^\//, ''); const menuType = menuMap.current.get(menuKey); if (menuType && menuType.menuItem) { newSelectedKeys.push(menuKey); } if (menuType && menuType.subMenu && !openKeys.includes(menuKey)) { newOpenKeys.push(menuKey); } pathKeys.pop(); } setSelectedKeys(newSelectedKeys); setOpenKeys(newOpenKeys); } useEffect(() => { const routeConfig = routeMap.current.get(pathname); setBreadCrumb(routeConfig || []); updateMenuStatus(); }, [pathname]); return (
{userLoading ? ( ) : ( {showMenu && (
{ setOpenKeys(openKeys); }} > {renderRoutes(locale)(routes, 1)}
{collapsed ? : }
)}
{!!breadcrumb.length && (
{breadcrumb.map((node, index) => ( {typeof node === 'string' ? locale[node] || node : node} ))}
)} {routeMap.current.has(pathname) ? children : }
{/*{showFooter &&