动态更新面包屑导航

This commit is contained in:
linxd
2025-07-03 14:40:22 +08:00
parent c8b28a4a29
commit 7114466b26
10 changed files with 586 additions and 511 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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;