import React, {useState, useMemo, useRef, useEffect} from 'react'; import {Switch, Route, Redirect, useHistory} from 'react-router-dom'; import {Layout, Menu, Breadcrumb, Spin} from '@arco-design/web-react'; import cs from 'classnames'; import { IconMenuFold, IconMenuUnfold, IconSettings, IconList, IconDashboard, } from '@arco-design/web-react/icon'; import qs from 'query-string'; import NProgress from 'nprogress'; import Navbar from './components/NavBar'; import Footer from './components/Footer'; import useRoute, {IRoute} from '@/routes'; import {isArray} from './utils/is'; import useLocale from './utils/useLocale'; import getUrlParams from './utils/getUrlParams'; import lazyload from './utils/lazyload'; import styles from './style/layout.module.less'; import {useStore} from '@/store'; const MenuItem = Menu.Item; const SubMenu = Menu.SubMenu; const Sider = Layout.Sider; const Content = Layout.Content; function getFlattenRoutes(routes) { const mod = import.meta.glob('./pages/**/[a-z[]*.tsx'); const res = []; function travel(_routes) { _routes.forEach((route) => { if (route.key && !route.children) { try { route.component = lazyload(mod[`./pages/${route.key}/index.tsx`]); res.push(route); } catch (error) { console.error(`cannot found /pages/${route.key}/index.tsx`) } } else if (isArray(route.children) && route.children.length) { travel(route.children); } }); } travel(routes); return res; } function PageLayout() { const urlParams = getUrlParams(); const history = useHistory(); const pathname = history.location.pathname; const currentComponent = qs.parseUrl(pathname).url.slice(1); const locale = useLocale(); const store = useStore() const {settings, userInfo, userLoading} = store const [routes, defaultRoute] = useRoute(userInfo?.menus); const defaultSelectedKeys = [currentComponent || defaultRoute]; const paths = (currentComponent || defaultRoute).split('/'); const defaultOpenKeys = paths.slice(0, paths.length - 1); const [breadcrumb, setBreadCrumb] = useState([]); const [collapsed, setCollapsed] = useState(false); const [selectedKeys, setSelectedKeys] = useState(defaultSelectedKeys); const [openKeys, setOpenKeys] = useState(defaultOpenKeys); const routeMap = useRef>(new Map()); const menuMap = useRef< Map >(new Map()); 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 flattenRoutes = useMemo(() => getFlattenRoutes(routes) || [], [routes]); function onClickMenuItem(key) { const currentRoute = flattenRoutes.find((r) => r.key === key); const component = currentRoute.component; const preload = component.preload(); NProgress.start(); preload.then(() => { history.push(currentRoute.path ? currentRoute.path : `/${key}`); NProgress.done(); }); } function toggleCollapse() { setCollapsed((collapsed) => !collapsed); } const paddingLeft = showMenu ? {paddingLeft: menuWidth} : {}; const paddingTop = showNavbar ? {paddingTop: navbarHeight} : {}; const paddingStyle = {...paddingLeft, ...paddingTop}; function renderRoutes(locale) { routeMap.current.clear(); return function travel(_routes: IRoute[], level, parentNode = []) { return _routes.map((route) => { const {breadcrumb = true, ignore} = route; const iconDom = getIconFromKey(route.key); const titleDom = ( <> {iconDom} {locale[route.name] || route.name} ); 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) { 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); } function getIconFromKey(key) { switch (key) { case 'role': return ; case 'user': return ; default: return ; } } useEffect(() => { const routeConfig = routeMap.current.get(pathname); const _breadcrumb = routeConfig || [] setBreadCrumb([getIconFromKey(paths[0]), ..._breadcrumb]); updateMenuStatus(); }, [pathname, userLoading]); return (
{userLoading ? ( ) : ( {showMenu && (
{ setOpenKeys(openKeys); }} > {renderRoutes(locale)(routes, 1)}
{collapsed ? : }
)}
{!!breadcrumb.length && (
{breadcrumb.map((node, index) => ( {typeof node === 'string' ? locale[node] || node : node} ))}
)} {flattenRoutes.map((route, index) => { return ( ); })} import('./pages/exception/403'))} />
{showFooter &&