diff --git a/src/components/GlobalComponent/Breadcrumb.jsx b/src/components/GlobalComponent/Breadcrumb.jsx new file mode 100644 index 0000000..229ab12 --- /dev/null +++ b/src/components/GlobalComponent/Breadcrumb.jsx @@ -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 = () => { + // 从sessionStorage初始化面包屑数据,与Vue实例保持一致 + 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; + + // 如果title存在,将路由信息添加到breadcrumbList数组 + if (title) { + const newBreadcrumbList = [...breadcrumbList]; + newBreadcrumbList.push({ + name, + path: path, // 正确使用当前路由的path属性 + title + }); + + // 使用reduce方法对breadcrumbList进行去重处理,与Vue组件完全一致 + 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); + + // 重写pushState和replaceState方法以捕获更多路由变化 + 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(); + }; + + // 监听hash变化(针对hash路由) + window.addEventListener('hashchange', handleRouteChange); + + return () => { + window.removeEventListener('popstate', handleRouteChange); + window.removeEventListener('hashchange', handleRouteChange); + // 还原原始方法 + window.history.pushState = originalPushState; + window.history.replaceState = originalReplaceState; + }; + }, [breadcrumbList, location]); + + // 当location.pathname变化时,自动更新currentRouterName + useEffect(() => { + const currentRoute = getCurrentRoute(); + setCurrentRouterName(currentRoute.name); + }, [location.pathname]); + + return ( +
+ + {breadcrumbList.map((item, index) => ( + handleItemClick(item)} + className={`breadcrumb-item ${item.name === currentRouterName ? 'breadcrumb-item-active' : ''}`} + > + + {item.title} + { + e.stopPropagation(); + cloneCurrentPage(item, index); + }} + /> + + + ))} + +
+ ); +}; + +export default CustomBreadcrumb; \ No newline at end of file diff --git a/src/components/GlobalComponent/breadcrumb.less b/src/components/GlobalComponent/breadcrumb.less new file mode 100644 index 0000000..acd336f --- /dev/null +++ b/src/components/GlobalComponent/breadcrumb.less @@ -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; +} \ No newline at end of file diff --git a/src/components/GlobalComponent/index.js b/src/components/GlobalComponent/index.js index 3a637ab..ac2716b 100644 --- a/src/components/GlobalComponent/index.js +++ b/src/components/GlobalComponent/index.js @@ -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 }; diff --git a/src/pages/nav_system_content/SystemContentList.js b/src/pages/nav_system_content/SystemContentList.js index db805a0..79acc22 100644 --- a/src/pages/nav_system_content/SystemContentList.js +++ b/src/pages/nav_system_content/SystemContentList.js @@ -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) => { setRouteActive(value)} /> */} - + + + + {userInfo?.user_name_cn ? userInfo.user_name_cn : (userInfo?.user_name || '')} diff --git a/src/pages/nav_system_content/SystemContentList.less b/src/pages/nav_system_content/SystemContentList.less index 3260f3d..5ee4c8a 100644 --- a/src/pages/nav_system_content/SystemContentList.less +++ b/src/pages/nav_system_content/SystemContentList.less @@ -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; } -} +} \ No newline at end of file