117 lines
4.0 KiB
TypeScript
117 lines
4.0 KiB
TypeScript
import React, {useEffect, useState} from "react";
|
|
import {Button, ConfigProvider, Layout, Menu, type MenuProps, type ThemeConfig} from "antd";
|
|
import useGlobalStore from "@/stores/global";
|
|
import HeaderRightRender from "@/layout/HeaderRightRender";
|
|
import IconFont from "@/components/IconFont";
|
|
import {useTranslation} from "react-i18next";
|
|
import MenuRender from "@/layout/MenuRender.tsx";
|
|
import {MenuFoldOutlined, MenuUnfoldOutlined} from "@ant-design/icons";
|
|
import BreadcrumbRender from "@/layout/BreadcrumbRender.tsx";
|
|
import {useLayoutContext} from "@/layout/LayoutContext";
|
|
import useMobile from "@/hooks/useMobile";
|
|
import {useNavigate} from "react-router";
|
|
import {findMenuByKey} from "@/layout/utils.ts";
|
|
|
|
const {Header} = Layout;
|
|
|
|
const HeaderRender: React.FC = () => {
|
|
const {t} = useTranslation();
|
|
const isMobile = useMobile();
|
|
const navigate = useNavigate();
|
|
const logo = useGlobalStore(state => state.logo);
|
|
const title = useGlobalStore(state => state.title);
|
|
const layout = useGlobalStore(state => state.layout);
|
|
const themeConfig = useGlobalStore(state => state.themeConfig);
|
|
const { menus, parentKey, collapsed, setCollapsed, setParentKey, setSelectKey } = useLayoutContext();
|
|
|
|
const theme: ThemeConfig = {
|
|
token: { colorTextBase: themeConfig.headerColor },
|
|
components: {
|
|
Menu: {
|
|
activeBarBorderWidth: 0,
|
|
itemBg: 'transparent',
|
|
}
|
|
}
|
|
}
|
|
const [mixMenu, setMixMenu] = useState<MenuProps['items']>([]);
|
|
|
|
useEffect(() => {
|
|
setMixMenu(menus.filter(item => item.hidden).map(item => ({
|
|
label: item.local ? t(item.local) : item.name,
|
|
icon: item.icon ? <IconFont name={item.icon}/> : false,
|
|
key: item.key!,
|
|
})));
|
|
}, [menus, t]);
|
|
|
|
const onMenuChange = (key: string) => {
|
|
const menu = findMenuByKey(menus, key);
|
|
if(!menu || !menu.key) return;
|
|
setParentKey(menu.key);
|
|
if(menu.type === 'menu') {
|
|
return;
|
|
}
|
|
setSelectKey([menu.key]);
|
|
if(menu.type === 'route' && menu.link) {
|
|
window.open(menu.path, '_blank');
|
|
return;
|
|
}
|
|
if(menu.type === 'route' && menu.path) {
|
|
navigate(menu.path);
|
|
return;
|
|
}
|
|
}
|
|
|
|
return (
|
|
<ConfigProvider theme={theme}>
|
|
{ isMobile ? (
|
|
<Header className={"flex sticky z-1 top-0 backdrop-blur-xs justify-between items-center"}>
|
|
<div className={"flex items-center"}>
|
|
<img className={"w-9 mr-5"} src={logo} alt="logo"/>
|
|
<span className={"font-semibold text-[20px] mr-2"}>{title}</span>
|
|
</div>
|
|
<HeaderRightRender/>
|
|
</Header>
|
|
) : (
|
|
<Header
|
|
className={"flex sticky z-1 top-0 backdrop-blur-xs"}
|
|
style={{borderBottom: "1px solid " + themeConfig.colorBorder}}
|
|
>
|
|
<div className={"flex items-center relative"}>
|
|
<img className={"h-8"} src={logo} alt="logo"/>
|
|
<span className={"font-semibold text-[20px] ml-5 mr-5"}>{title}</span>
|
|
{/* 侧边栏开关 */}
|
|
{layout !== 'top' && (
|
|
<Button
|
|
type={'text'}
|
|
className={'text-[16px] mr-2.5'}
|
|
onClick={() => setCollapsed(!collapsed)}
|
|
>
|
|
{ collapsed ? <MenuUnfoldOutlined/> : <MenuFoldOutlined/> }
|
|
</Button>
|
|
)}
|
|
{/* 面包屑 */}
|
|
{['columns', 'side'].includes(layout) && <BreadcrumbRender/> }
|
|
</div>
|
|
<div className={"overflow-hidden flex-1"}>
|
|
{/* 顶部菜单 */}
|
|
{ layout == 'top' && <MenuRender /> }
|
|
{/* 混合布局模式下的顶部菜单 */}
|
|
{ layout == 'mix' && (
|
|
<Menu
|
|
className={"border-b-0 w-full"}
|
|
mode="horizontal"
|
|
items={mixMenu}
|
|
onSelect={(info) => onMenuChange(info.key)}
|
|
selectedKeys={[parentKey!]}
|
|
/>
|
|
)}
|
|
</div>
|
|
<HeaderRightRender/>
|
|
</Header>
|
|
)}
|
|
</ConfigProvider>
|
|
)
|
|
}
|
|
|
|
export default HeaderRender;
|