Files
EveryPublish/apps/web/src/layouts/components/Menu.tsx
T

127 lines
3.6 KiB
TypeScript

import React, { memo, useState } from 'react';
import { useNavigate, useLocation, NavigateFunction } from 'react-router-dom';
import { Menu, MenuValue } from 'tdesign-react';
import router, { IRouter } from 'router';
import { resolve } from 'utils/path';
import { useAppSelector } from 'modules/store';
import { selectGlobal } from 'modules/global';
import { selectUser } from 'modules/user';
import MenuLogo from './MenuLogo';
import Style from './Menu.module.less';
const { SubMenu, MenuItem, HeadMenu } = Menu;
interface IMenuProps {
showLogo?: boolean;
showOperation?: boolean;
}
// 纯函数:递归渲染菜单(不在递归/循环内调用 Hook)
const renderMenuItems = (menu: IRouter[], navigate: NavigateFunction, isAdmin: boolean, parentPath = '') => {
return menu.map((item) => {
const { children, meta, path } = item;
if (!meta || meta?.hidden === true) {
return null;
}
if (meta.admin && !isAdmin) {
// 平台管理菜单仅 admin 角色可见
return null;
}
const { Icon, title, single } = meta;
const routerPath = resolve(parentPath, path);
if (!children || children.length === 0) {
return (
<MenuItem
key={routerPath}
value={routerPath}
icon={Icon ? <Icon /> : undefined}
onClick={() => navigate(routerPath)}
>
{title}
</MenuItem>
);
}
if (single && children?.length > 0) {
const firstChild = children[0];
if (firstChild?.meta && !firstChild?.meta?.hidden) {
const { Icon, title } = meta;
const singlePath = resolve(resolve(parentPath, path), firstChild.path);
return (
<MenuItem
key={singlePath}
value={singlePath}
icon={Icon ? <Icon /> : undefined}
onClick={() => navigate(singlePath)}
>
{title}
</MenuItem>
);
}
}
return (
<SubMenu key={routerPath} value={routerPath} title={title} icon={Icon ? <Icon /> : undefined}>
{renderMenuItems(children, navigate, isAdmin, routerPath)}
</SubMenu>
);
});
};
/**
* 顶部菜单
*/
export const HeaderMenu = memo(() => {
const globalState = useAppSelector(selectGlobal);
const location = useLocation();
const navigate = useNavigate();
const userState = useAppSelector(selectUser);
const isAdmin = userState.userInfo?.role === 'admin';
const [active, setActive] = useState<MenuValue>(location.pathname); // todo
return (
<HeadMenu
expandType='popup'
style={{ marginBottom: 20 }}
value={active}
theme={globalState.theme}
onChange={(v) => setActive(v)}
>
{renderMenuItems(router, navigate, isAdmin)}
</HeadMenu>
);
});
/**
* 左侧菜单
*/
export default memo((props: IMenuProps) => {
const location = useLocation();
const navigate = useNavigate();
const globalState = useAppSelector(selectGlobal);
const userState = useAppSelector(selectUser);
const isAdmin = userState.userInfo?.role === 'admin';
const { version } = globalState;
const bottomText = globalState.collapsed ? version : 'EveryPublish ' + version;
return (
<Menu
width='232px'
style={{ flexShrink: 0, height: '100%' }}
className={Style.menuPanel2}
value={location.pathname}
theme={globalState.theme}
collapsed={globalState.collapsed}
operations={props.showOperation ? <div className={Style.menuTip}>{bottomText}</div> : undefined}
logo={props.showLogo ? <MenuLogo collapsed={globalState.collapsed} /> : undefined}
>
{renderMenuItems(router, navigate, isAdmin)}
</Menu>
);
});