动态更新面包屑导航
This commit is contained in:
@ -2,11 +2,13 @@
|
||||
import React from 'react';
|
||||
import ProLayout, { PageContainer } from '@ant-design/pro-layout';
|
||||
import { Link, useLocation, useIntl, useHistory } from 'umi';
|
||||
import { connect } from 'dva';
|
||||
import defaultSettings from '../../config/defaultSettings';
|
||||
import routes from '../../config/router.config'; // 引入你的自定义路由结构
|
||||
import { ConfigProvider, Breadcrumb } from 'antd';
|
||||
import HeaderComponent from './Header';
|
||||
import IconFont from '@/components/IconFont/IconFont';
|
||||
import { BreadcrumbState } from '@/models/breadcrumb';
|
||||
|
||||
|
||||
const MenuRender = (item: any, isSubMenu: boolean) => {
|
||||
@ -32,22 +34,29 @@ const MenuRender = (item: any, isSubMenu: boolean) => {
|
||||
);
|
||||
};
|
||||
|
||||
const BreadcrumbRender = (breadcrumb: any, intl: any, history: any) => {
|
||||
const breadcrumbRoutes = breadcrumb?.routes;
|
||||
const BreadcrumbRender = (routeBreadcrumb: any, intl: any, history: any, dynamicBreadcrumbName: string | null) => {
|
||||
const breadcrumbRoutes = routeBreadcrumb?.routes;
|
||||
return (
|
||||
<Breadcrumb>
|
||||
<Breadcrumb.Item
|
||||
onClick={() => {
|
||||
history.push('/');
|
||||
}}
|
||||
style={{ cursor: 'pointer' }}
|
||||
>
|
||||
{intl.formatMessage({ id: 'menu.首页' })}
|
||||
<span style={{ cursor: 'pointer' }}>
|
||||
{intl.formatMessage({ id: 'menu.首页' })}
|
||||
</span>
|
||||
</Breadcrumb.Item>
|
||||
{breadcrumbRoutes?.map((item: any) => {
|
||||
{breadcrumbRoutes?.map((item: any, index: number) => {
|
||||
// 判断是否是最后一个面包屑项且存在动态名称
|
||||
const isLastItem = index === (breadcrumbRoutes.length - 1);
|
||||
const displayName = (isLastItem && dynamicBreadcrumbName)
|
||||
? dynamicBreadcrumbName
|
||||
: intl.formatMessage({ id: `menu.${item.breadcrumbName}` || '' });
|
||||
|
||||
return (
|
||||
<Breadcrumb.Item key={item.path}>
|
||||
{intl.formatMessage({ id: `menu.${item.breadcrumbName}` || '' })}
|
||||
{displayName}
|
||||
</Breadcrumb.Item>
|
||||
);
|
||||
})}
|
||||
@ -55,7 +64,13 @@ const BreadcrumbRender = (breadcrumb: any, intl: any, history: any) => {
|
||||
);
|
||||
};
|
||||
|
||||
const BasicLayout: React.FC = (props) => {
|
||||
interface BasicLayoutProps {
|
||||
children: React.ReactNode;
|
||||
breadcrumb: BreadcrumbState;
|
||||
}
|
||||
|
||||
const BasicLayout: React.FC<BasicLayoutProps> = (props) => {
|
||||
const { children, breadcrumb } = props;
|
||||
const location = useLocation();
|
||||
const intl = useIntl();
|
||||
const history = useHistory();
|
||||
@ -81,14 +96,18 @@ const BasicLayout: React.FC = (props) => {
|
||||
ghost={true}
|
||||
header={{
|
||||
title: false,
|
||||
breadcrumbRender: ({ breadcrumb }) => BreadcrumbRender(breadcrumb, intl, history),
|
||||
breadcrumbRender: ({ breadcrumb: routeBreadcrumb }) =>
|
||||
BreadcrumbRender(routeBreadcrumb, intl, history, breadcrumb.breadcrumbName),
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
{children}
|
||||
</PageContainer>
|
||||
</ProLayout>
|
||||
</ConfigProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default BasicLayout;
|
||||
export default connect(({ breadcrumb }: { breadcrumb: BreadcrumbState }) => ({
|
||||
breadcrumb
|
||||
}))(BasicLayout);
|
||||
|
||||
|
@ -1,64 +0,0 @@
|
||||
import React, { useEffect } from 'react';
|
||||
// import Header from './Header';
|
||||
import { Layout, Breadcrumb } from 'antd';
|
||||
import { useLocation, useIntl, Link, connect } from 'umi';
|
||||
|
||||
import type { ConnectProps, Dispatch } from 'umi';
|
||||
const { Header, Sider, Content } = Layout;
|
||||
//导入logo图片
|
||||
import HeaderComponent from './Header';
|
||||
import SiderMenu from './SiderMenu';
|
||||
import './layout.less';
|
||||
import type { BreadcrumbModelState } from '@/models/breadcrumb';
|
||||
|
||||
interface LayoutIndexProps extends ConnectProps {
|
||||
breadcrumb: BreadcrumbModelState;
|
||||
dispatch: Dispatch;
|
||||
}
|
||||
|
||||
const LayoutIndex: React.FC<LayoutIndexProps> = (props) => {
|
||||
const { children, breadcrumb, dispatch } = props;
|
||||
const location = useLocation();
|
||||
const intl = useIntl();
|
||||
// 当路由变化时更新面包屑
|
||||
useEffect(() => {
|
||||
console.log(location)
|
||||
dispatch({
|
||||
type: 'breadcrumb/updateBreadcrumbs',
|
||||
payload: { pathname: location.pathname, intl },
|
||||
});
|
||||
}, [location.pathname, intl, dispatch]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Layout>
|
||||
<Header className="header">
|
||||
<HeaderComponent />
|
||||
</Header>
|
||||
<Layout>
|
||||
<Sider width={200} theme="light">
|
||||
<SiderMenu />
|
||||
</Sider>
|
||||
<Layout className="layout-content">
|
||||
<Breadcrumb style={{ margin: '10px 0' }}>
|
||||
{breadcrumb.breadcrumbs.map((breadcrumbItem, index) => (
|
||||
<Breadcrumb.Item key={breadcrumbItem.path}>
|
||||
{index < breadcrumb.breadcrumbs.length - 1 ? (
|
||||
<Link to={breadcrumbItem.path}>{breadcrumbItem.breadcrumbName}</Link>
|
||||
) : (
|
||||
breadcrumbItem.breadcrumbName
|
||||
)}
|
||||
</Breadcrumb.Item>
|
||||
))}
|
||||
</Breadcrumb>
|
||||
<Content>{children}</Content>
|
||||
</Layout>
|
||||
</Layout>
|
||||
</Layout>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default connect(({ breadcrumb }: { breadcrumb: BreadcrumbModelState }) => ({
|
||||
breadcrumb,
|
||||
}))(LayoutIndex);
|
@ -1,127 +0,0 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Menu, Tooltip } from 'antd';
|
||||
import { useIntl, Link, useHistory } from 'umi';
|
||||
import IconFont from '@/components/IconFont/IconFont';
|
||||
import routerConfig from '../../config/router.config';
|
||||
|
||||
// 路由接口定义
|
||||
interface IRouteItem {
|
||||
path: string;
|
||||
component?: string;
|
||||
name?: string;
|
||||
meta?: {
|
||||
title?: string;
|
||||
hide?: boolean;
|
||||
icon?: string;
|
||||
};
|
||||
routes?: IRouteItem[];
|
||||
children?: IRouteItem[];
|
||||
redirect?: string;
|
||||
}
|
||||
|
||||
interface IMenuItem {
|
||||
label: string;
|
||||
key: string;
|
||||
path: string;
|
||||
icon: string;
|
||||
children?: IMenuItem[];
|
||||
}
|
||||
|
||||
// 获取路由配置转换为菜单项
|
||||
const generateMenuItems = (): IMenuItem[] => {
|
||||
// 找到主布局下的路由
|
||||
const mainLayoutRoute = routerConfig.find((route: IRouteItem) => route.path === '/');
|
||||
if (!mainLayoutRoute || !mainLayoutRoute.routes) return [];
|
||||
|
||||
// 递归处理路由,生成菜单项
|
||||
const processRoutes = (routes: IRouteItem[], parentPath: string = ''): IMenuItem[] => {
|
||||
return routes
|
||||
.filter((route) => !route.redirect && route.name && !route.meta?.hide)
|
||||
.map((route) => {
|
||||
// 构建完整路径
|
||||
const routePath = route.path.startsWith('/')
|
||||
? route.path
|
||||
: `${parentPath}/${route.path}`.replace(/\/+/g, '/');
|
||||
|
||||
// 创建菜单项
|
||||
const menuItem: IMenuItem = {
|
||||
label: route.meta?.title ? `menu.${route.meta.title}` : `menu.${route.name || ''}`,
|
||||
key: route.path || '',
|
||||
path: routePath,
|
||||
icon: route.meta?.icon || 'icon-liebiaomoshi',
|
||||
};
|
||||
|
||||
// 如果有子路由,递归处理
|
||||
if (route.routes && route.routes.length > 0) {
|
||||
const children = processRoutes(route.routes, routePath);
|
||||
if (children.length > 0) {
|
||||
menuItem.children = children;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果有children,递归处理
|
||||
if (route.children && route.children.length > 0) {
|
||||
const children = processRoutes(route.children, routePath);
|
||||
if (children.length > 0) {
|
||||
menuItem.children = children;
|
||||
}
|
||||
}
|
||||
|
||||
return menuItem;
|
||||
});
|
||||
};
|
||||
|
||||
return processRoutes(mainLayoutRoute.routes);
|
||||
};
|
||||
|
||||
const items: IMenuItem[] = generateMenuItems();
|
||||
|
||||
const SiderMenu: React.FC = (props: any) => {
|
||||
//当前激活菜单
|
||||
const [current, setCurrent] = useState('index');
|
||||
const intl = useIntl();
|
||||
const history = useHistory();
|
||||
useEffect(() => {
|
||||
// 获取当前激活菜单
|
||||
const path = history.location.pathname;
|
||||
setCurrent(path);
|
||||
// if (path.split('/').length > 1) {
|
||||
// setCurrent(path.split('/')[path.split('/').length - 1]);
|
||||
// return;
|
||||
// }
|
||||
}, [history.location.pathname]);
|
||||
|
||||
// 递归渲染菜单项
|
||||
const renderMenuItems = (menuItems: IMenuItem[]) => {
|
||||
return menuItems.map((item: IMenuItem) =>
|
||||
item.children && item.children.length > 0 ? (
|
||||
<Menu.SubMenu
|
||||
key={item.path}
|
||||
title={
|
||||
<Tooltip title={intl.formatMessage({ id: item.label })} placement="right">
|
||||
<>{intl.formatMessage({ id: item.label })}</>
|
||||
</Tooltip>
|
||||
}
|
||||
icon={<IconFont type={item.icon} />}
|
||||
>
|
||||
{renderMenuItems(item.children)}
|
||||
</Menu.SubMenu>
|
||||
) : (
|
||||
<Menu.Item key={item.path} icon={<IconFont type={item.icon} />}>
|
||||
<Tooltip title={intl.formatMessage({ id: item.label })} placement="right">
|
||||
<Link to={item.path}>{intl.formatMessage({ id: item.label })}</Link>
|
||||
</Tooltip>
|
||||
</Menu.Item>
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="header-menu">
|
||||
<Menu selectedKeys={[current]} mode="inline">
|
||||
{renderMenuItems(items)}
|
||||
</Menu>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export default SiderMenu;
|
Reference in New Issue
Block a user