新增顶部面包屑导航组件

main
yupeng 1 month ago
parent eefeab35e8
commit d8c754d3da

@ -0,0 +1,233 @@
import React, { useState, useEffect } from 'react';
import { Breadcrumb } from 'antd';
import { CloseOutlined } from '@ant-design/icons';
import { history, useLocation, matchRoutes } from '@umijs/max';
import './breadcrumb.less';
import routes from '../../../config/routes';
const CustomBreadcrumb = () => {
// sessionStorageVue
const [breadcrumbList, setBreadcrumbList] = useState(() => {
return JSON.parse(sessionStorage.getItem('breadcrumb')) || [];
});
const [currentRouterName, setCurrentRouterName] = useState('');
const location = useLocation();
//
const getCurrentRoute = () => {
const pathname = location.pathname || '/';
const href = window.location.href;
console.log('当前路径:', pathname);
// 使matchRoutes
const matchedRoutes = matchRoutes(routes, pathname);
console.log('匹配的路由:', matchedRoutes);
//
if (matchedRoutes && matchedRoutes.length > 0) {
const matchedRoute = matchedRoutes[matchedRoutes.length - 1]; //
const routeConfig = matchedRoute.route;
console.log('匹配的路由配置:', routeConfig);
// 使routeConfig.name
const routeTitle = routeConfig.name || '未知页面';
return {
name: routeConfig.name || pathname.replace(/\//g, '_').slice(1) || 'home',
path: pathname,
href,
fullPath: href,
meta: {
title: routeTitle,
affix: pathname === '/'
}
};
}
// 使matchRoutes
const pathSegments = pathname.split('/').filter(Boolean);
const lastSegment = pathSegments[pathSegments.length - 1] || 'home';
//
return {
name: lastSegment,
path: pathname,
href,
fullPath: href,
meta: {
title: lastSegment === 'home' ? '首页' : lastSegment,
affix: pathname === '/'
}
};
};
// - path
const generateBreadcrumb = () => {
//
const currentRoute = getCurrentRoute();
const { meta: { title }, name, path } = currentRoute;
// titlebreadcrumbList
if (title) {
const newBreadcrumbList = [...breadcrumbList];
newBreadcrumbList.push({
name,
path: path, // 使path
title
});
// 使reducebreadcrumbListVue
const uniqueList = newBreadcrumbList.reduce((accumulator, current) => {
const x = accumulator.find(item => item.name === current.name);
if (!x) {
return accumulator.concat(current);
} else {
accumulator.forEach((val, i) => {
if (val.name == current.name) {
val.title = current.title;
val.path = current.path;
}
});
return accumulator;
}
}, []);
// sessionStorage
setBreadcrumbList(uniqueList);
sessionStorage.setItem('breadcrumb', JSON.stringify(uniqueList));
}
};
//
const toPath = (item) => {
const currentRoute = getCurrentRoute();
//
if (item.name === currentRoute.name) {
return '';
}
//
if (item.meta?.affix) {
return '';
} else {
//
return item.path;
}
};
//
const cloneCurrentPage = (item, index) => {
//
if (!breadcrumbList || breadcrumbList.length <= 1) {
// 使window.location.href
window.location.href = '/';
return;
}
const updatedList = [...breadcrumbList];
updatedList.splice(index, 1);
let length = updatedList.length;
//
if (currentRouterName === item.name && length > 0) {
// 使history
history.push(updatedList[length - 1].path);
}
//
setBreadcrumbList(updatedList);
sessionStorage.setItem('breadcrumb', JSON.stringify(updatedList));
};
//
const handleItemClick = (item) => {
const path = toPath(item);
if (path) {
console.log(currentRouterName, 'yp')
// 使history
history.push(path);
}
};
//
useEffect(() => {
const currentRoute = getCurrentRoute();
setCurrentRouterName(currentRoute.name);
generateBreadcrumb();
}, []);
// generateBreadcrumb
useEffect(() => {
const handleRouteChange = () => {
const currentRoute = getCurrentRoute();
setCurrentRouterName(currentRoute.name);
generateBreadcrumb();
};
// 退
window.addEventListener('popstate', handleRouteChange);
// pushStatereplaceState
const originalPushState = window.history.pushState;
const originalReplaceState = window.history.replaceState;
window.history.pushState = function (...args) {
originalPushState.apply(this, args);
handleRouteChange();
};
window.history.replaceState = function (...args) {
originalReplaceState.apply(this, args);
handleRouteChange();
};
// hashhash
window.addEventListener('hashchange', handleRouteChange);
return () => {
window.removeEventListener('popstate', handleRouteChange);
window.removeEventListener('hashchange', handleRouteChange);
//
window.history.pushState = originalPushState;
window.history.replaceState = originalReplaceState;
};
}, [breadcrumbList, location]);
// location.pathnamecurrentRouterName
useEffect(() => {
const currentRoute = getCurrentRoute();
setCurrentRouterName(currentRoute.name);
}, [location.pathname]);
return (
<div className="bread-crumb" style={{ maxHeight: '32px', flexWrap: 'nowrap' }}>
<Breadcrumb separator="" >
{breadcrumbList.map((item, index) => (
<Breadcrumb.Item
key={index}
onClick={() => handleItemClick(item)}
className={`breadcrumb-item ${item.name === currentRouterName ? 'breadcrumb-item-active' : ''}`}
>
<span className="breadcrumb-item-content">
<span className="breadcrumb-item-text">{item.title}</span>
<CloseOutlined
className="breadcrumb-close-icon"
onClick={(e) => {
e.stopPropagation();
cloneCurrentPage(item, index);
}}
/>
</span>
</Breadcrumb.Item>
))}
</Breadcrumb>
</div>
);
};
export default CustomBreadcrumb;

@ -0,0 +1,151 @@
// 面包屑容器样式
.bread-crumb {
// display: flex;
align-items: center;
overflow-x: auto;
overflow-y: hidden;
height: auto;
max-width: 100%;
margin: 8px 0;
padding: 0 12px;
background-color: transparent;
flex-wrap: nowrap;
justify-content: flex-start;
box-sizing: border-box;
}
// 面包屑容器滚动条样式
.bread-crumb::-webkit-scrollbar {
height: 4px;
}
.bread-crumb::-webkit-scrollbar-track {
background-color: #f1f1f1;
border-radius: 2px;
}
.bread-crumb::-webkit-scrollbar-thumb {
background-color: #c1c1c1;
border-radius: 2px;
}
.bread-crumb::-webkit-scrollbar-thumb:hover {
background-color: #a1a1a1;
}
// 面包屑导航容器
.ant-breadcrumb {
display: flex !important;
flex-wrap: nowrap !important;
align-items: center;
gap: 8px;
margin: 0;
padding: 0;
}
// 面包屑项基础样式
.breadcrumb-item {
height: 32px;
padding: 0 12px;
border-radius: 16px;
background-color: #fff;
border: 1px solid #E8E8E8;
margin-right: 8px;
display: flex;
align-items: center;
transition: all 0.3s ease;
cursor: pointer;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 180px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.04);
box-sizing: border-box;
// 移除默认的分隔符
.ant-breadcrumb-separator {
display: none !important;
}
// 链接样式
.ant-breadcrumb-link {
display: flex;
align-items: center;
height: 100%;
color: #808080 !important;
background: transparent !important;
padding: 0;
margin: 0;
overflow: hidden;
text-overflow: ellipsis;
width: 100%;
}
// 悬停效果
&:hover {
border-color: #0056FF;
// transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(0, 86, 255, 0.1);
}
}
// 面包屑项选中状态样式
.breadcrumb-item-active {
background-color: #ECF2FF;
border-color: #0056FF;
.ant-breadcrumb-link {
color: #0056FF !important;
font-weight: 500;
}
}
// 面包屑项内容容器
.breadcrumb-item-content {
display: flex;
align-items: center;
height: 100%;
overflow: hidden;
text-overflow: ellipsis;
width: 100%;
}
// 面包屑项文本样式
.breadcrumb-item-text {
font-size: 14px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex: 1;
}
// 关闭图标样式
.breadcrumb-close-icon {
font-size: 14px;
color: #7D9CD8;
margin-left: 8px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: color 0.3s ease;
flex-shrink: 0;
&:hover {
color: #0056FF;
}
}
// 确保所有面包屑链接样式一致
.ant-breadcrumb-link {
color: #808080 !important;
transition: color 0.3s ease;
}
.ant-breadcrumb-link:hover {
color: #0056FF !important;
}
.ant-breadcrumb-item-active .ant-breadcrumb-link {
color: #0056FF !important;
}

@ -1,5 +1,6 @@
import { PureComponent } from 'react'
import * as plugins from 'antd'
import React, { PureComponent } from 'react';
import * as plugins from 'antd';
import CustomBreadcrumb from './Breadcrumb';
class GlobalComponent extends PureComponent {
constructor(props) {
@ -69,7 +70,11 @@ class GlobalComponent extends PureComponent {
this.Typography = plugins.Typography
this.Upload = plugins.Upload
this.message = plugins.message
// 自定义组件
this.CustomBreadcrumb = CustomBreadcrumb;
}
}
export default GlobalComponent
export default GlobalComponent;
// 同时导出自定义面包屑组件,方便直接使用
export { CustomBreadcrumb };

@ -10,6 +10,7 @@ import { HomeOutlined, SettingOutlined, LogoutOutlined } from '@ant-design/icons
import { getPageQuery } from '@/utils/utils'
import menuTitle from '@/assets/img/memuTitle.png'
import menuTitle1 from '@/assets/img/memuTitle1.png'
import { CustomBreadcrumb } from '@/components/GlobalComponent'
const SystemContentList = (props) => {
const dynamicRoute = window.dynamicRoute
@ -181,7 +182,10 @@ const SystemContentList = (props) => {
<Menu mode='horizontal' className='leftMenu' selectedKeys={[activeKey]} items={menuItems} onClick={value => setRouteActive(value)} />
</Col> */}
<Col xs={8} sm={8} md={8} lg={8} xl={8} className='tabBarRight'>
<Col xs={20} sm={20} md={20} lg={20} xl={20} className='tabBarLeft'>
<CustomBreadcrumb />
</Col>
<Col xs={4} sm={4} md={4} lg={4} xl={4} className='tabBarRight'>
<Avatar className='tabBarRightAvaTor' src='https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201707%2F31%2F20170731021444_2YUfe.jpeg&refer=http%3A%2F%2Fb-ssl.duitang.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1669779871&t=ec025ed48a1668dee9cfa0e803b6f787' />
<span className='tabBarRightName'>{userInfo?.user_name_cn ? userInfo.user_name_cn : (userInfo?.user_name || '')}</span>

@ -34,6 +34,14 @@
}
}
.tabBarLeft {
text-align: right;
display: flex;
// justify-content: flex-end;s
flex-wrap: nowrap;
align-items: center;
}
.tabBarRight {
text-align: right;
display: flex;
@ -111,4 +119,4 @@
// height: 100%;
overflow: auto;
}
}
}
Loading…
Cancel
Save