fix:breadcrumbs
This commit is contained in:
@@ -1,9 +1,9 @@
|
|||||||
import { type FC, useCallback, useRef } from 'react';
|
import { type FC, useRef } from 'react';
|
||||||
import { Layout, Dropdown, Space, Breadcrumb } from 'antd';
|
import { Layout, Dropdown, Space, Breadcrumb } from 'antd';
|
||||||
import type { MenuProps, BreadcrumbProps } from 'antd';
|
import type { MenuProps, BreadcrumbProps } from 'antd';
|
||||||
import { UserOutlined, LogoutOutlined, SettingOutlined } from '@ant-design/icons';
|
import { UserOutlined, LogoutOutlined, SettingOutlined } from '@ant-design/icons';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useParams, useNavigate } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
import { useUser } from '@/store/user';
|
import { useUser } from '@/store/user';
|
||||||
import { useMenu } from '@/store/menu';
|
import { useMenu } from '@/store/menu';
|
||||||
import styles from './index.module.css'
|
import styles from './index.module.css'
|
||||||
@@ -13,8 +13,7 @@ const { Header } = Layout;
|
|||||||
|
|
||||||
const AppHeader: FC<{source?: 'space' | 'manage';}> = ({source = 'manage'}) => {
|
const AppHeader: FC<{source?: 'space' | 'manage';}> = ({source = 'manage'}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const params = useParams();
|
const location = useLocation();
|
||||||
const navigate = useNavigate();
|
|
||||||
const settingModalRef = useRef<SettingModalRef>(null)
|
const settingModalRef = useRef<SettingModalRef>(null)
|
||||||
const userInfoModalRef = useRef<UserInfoModalRef>(null)
|
const userInfoModalRef = useRef<UserInfoModalRef>(null)
|
||||||
|
|
||||||
@@ -55,7 +54,7 @@ const AppHeader: FC<{source?: 'space' | 'manage';}> = ({source = 'manage'}) => {
|
|||||||
key: '1',
|
key: '1',
|
||||||
label: (<>
|
label: (<>
|
||||||
<div>{user.username}</div>
|
<div>{user.username}</div>
|
||||||
<div className="rb:text-[12px] rb:text-[#5B6167] rb:mt-2">{user.email}</div>
|
<div className="rb:text-[12px] rb:text-[#5B6167] rb:mt-[8px]">{user.email}</div>
|
||||||
</>),
|
</>),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -90,7 +89,7 @@ const AppHeader: FC<{source?: 'space' | 'manage';}> = ({source = 'manage'}) => {
|
|||||||
onClick: handleLogout,
|
onClick: handleLogout,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const formatBreadcrumbNames = useCallback(() => {
|
const formatBreadcrumbNames = () => {
|
||||||
return breadcrumbs.map((menu, index) => {
|
return breadcrumbs.map((menu, index) => {
|
||||||
const item: any = {
|
const item: any = {
|
||||||
title: menu.i18nKey ? t(menu.i18nKey) : menu.label,
|
title: menu.i18nKey ? t(menu.i18nKey) : menu.label,
|
||||||
@@ -109,23 +108,13 @@ const AppHeader: FC<{source?: 'space' | 'manage';}> = ({source = 'manage'}) => {
|
|||||||
};
|
};
|
||||||
item.href = '#';
|
item.href = '#';
|
||||||
} else if (menu.path && menu.path !== '#') {
|
} else if (menu.path && menu.path !== '#') {
|
||||||
// 对于三级面包屑的二级菜单,如果路径包含动态参数,替换为当前参数值
|
// 只有当 path 不是 '#' 时才设置 path
|
||||||
if (breadcrumbs.length === 3 && index === 1 && menu.path.includes(':id') && params.id) {
|
item.path = menu.path;
|
||||||
const dynamicPath = menu.path.replace(':id', params.id);
|
|
||||||
item.onClick = (e: React.MouseEvent) => {
|
|
||||||
e.preventDefault();
|
|
||||||
navigate(dynamicPath);
|
|
||||||
};
|
|
||||||
item.href = '#';
|
|
||||||
} else {
|
|
||||||
// 只有当 path 不是 '#' 时才设置 path
|
|
||||||
item.path = menu.path;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
});
|
});
|
||||||
}, [breadcrumbs, params.id, t, navigate])
|
}
|
||||||
return (
|
return (
|
||||||
<Header className={styles.header}>
|
<Header className={styles.header}>
|
||||||
<Breadcrumb separator=">" items={formatBreadcrumbNames() as BreadcrumbProps['items']} />
|
<Breadcrumb separator=">" items={formatBreadcrumbNames() as BreadcrumbProps['items']} />
|
||||||
|
|||||||
@@ -11,47 +11,85 @@ export const useNavigationBreadcrumbs = (source: 'space' | 'manage' = 'manage')
|
|||||||
const menus = allMenus[source] || [];
|
const menus = allMenus[source] || [];
|
||||||
|
|
||||||
// 查找匹配的菜单项并构建keyPath
|
// 查找匹配的菜单项并构建keyPath
|
||||||
const findMenuKeyPath = (menuList: any[]): string[] | null => {
|
const findMenuKeyPath = (menuList: any[], parentKeys: string[] = []): string[] | null => {
|
||||||
const checkDynamicMatch = (pattern: string, path: string) => {
|
let bestMatch: { path: string; parentId?: string; score: number } | null = null;
|
||||||
const pathPattern = pattern.replace(/:[\w-]+/g, '[^/]+');
|
|
||||||
const regex = new RegExp(`^${pathPattern}$`);
|
|
||||||
return regex.test(path);
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const menu of menuList) {
|
for (const menu of menuList) {
|
||||||
|
// 检查子菜单
|
||||||
if (menu.subs && menu.subs.length > 0) {
|
if (menu.subs && menu.subs.length > 0) {
|
||||||
|
const menuPath = menu.path ? (menu.path[0] !== '/' ? '/' + menu.path : menu.path) : '';
|
||||||
for (const sub of menu.subs) {
|
for (const sub of menu.subs) {
|
||||||
// 检查三级菜单
|
if (sub.path) {
|
||||||
if (sub.subs && sub.subs.length > 0) {
|
const subPath = sub.path[0] !== '/' ? '/' + sub.path : sub.path;
|
||||||
for (const subSub of sub.subs) {
|
|
||||||
if (subSub.path) {
|
// 精确匹配优先
|
||||||
const subSubPath = subSub.path[0] !== '/' ? '/' + subSub.path : subSub.path;
|
if (subPath === currentPath) {
|
||||||
if (subSubPath === currentPath || (subSubPath.includes(':') && checkDynamicMatch(subSubPath, currentPath))) {
|
return [sub.path, `${menu.id}`];
|
||||||
return [subSub.path, `${sub.id}`, `${menu.id}`];
|
}
|
||||||
|
console.log('menuPath', menuPath)
|
||||||
|
// 动态路由匹配
|
||||||
|
if (subPath.includes(':')) {
|
||||||
|
// 检查是否在父菜单下
|
||||||
|
if (menuPath && currentPath.startsWith(menuPath + '/')) {
|
||||||
|
const relativePath = currentPath.replace(menuPath, '');
|
||||||
|
const pathSegments = subPath.split('/');
|
||||||
|
const relativeSegments = relativePath.split('/');
|
||||||
|
if (pathSegments.length === relativeSegments.length) {
|
||||||
|
const pathPattern = subPath.replace(/:[\w-]+/g, '[^/]+').replace(/\[[\w-]+\]/g, '[^/]+');
|
||||||
|
const regex = new RegExp(`^${pathPattern}$`);
|
||||||
|
if (regex.test(relativePath)) {
|
||||||
|
return [sub.path, `${menu.id}`];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 直接匹配子菜单路径
|
||||||
|
const pathSegments = subPath.split('/');
|
||||||
|
const currentSegments = currentPath.split('/');
|
||||||
|
if (pathSegments.length === currentSegments.length) {
|
||||||
|
const pathPattern = subPath.replace(/:[\w-]+/g, '[^/]+').replace(/\[[\w-]+\]/g, '[^/]+');
|
||||||
|
const regex = new RegExp(`^${pathPattern}$`);
|
||||||
|
if (regex.test(currentPath)) {
|
||||||
|
return [sub.path, `${menu.id}`];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查二级菜单
|
|
||||||
if (sub.path) {
|
|
||||||
const subPath = sub.path[0] !== '/' ? '/' + sub.path : sub.path;
|
|
||||||
if (subPath === currentPath || (subPath.includes(':') && checkDynamicMatch(subPath, currentPath))) {
|
|
||||||
return [sub.path, `${menu.id}`];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查一级菜单
|
// 检查主菜单
|
||||||
if (menu.path) {
|
if (menu.path) {
|
||||||
const menuPath = menu.path[0] !== '/' ? '/' + menu.path : menu.path;
|
const menuPath = menu.path[0] !== '/' ? '/' + menu.path : menu.path;
|
||||||
if (menuPath === currentPath || (menuPath.includes(':') && checkDynamicMatch(menuPath, currentPath))) {
|
// 精确匹配优先
|
||||||
return [menu.path];
|
if (menuPath === currentPath) {
|
||||||
|
return [menu.path, ...parentKeys].reverse();
|
||||||
|
}
|
||||||
|
// 动态路由匹配
|
||||||
|
if (menuPath.includes(':')) {
|
||||||
|
const pathSegments = menuPath.split('/');
|
||||||
|
const currentSegments = currentPath.split('/');
|
||||||
|
if (pathSegments.length === currentSegments.length) {
|
||||||
|
const pathPattern = menuPath.replace(/:[\w-]+/g, '[^/]+').replace(/\[[\w-]+\]/g, '[^/]+');
|
||||||
|
const regex = new RegExp(`^${pathPattern}$`);
|
||||||
|
if (regex.test(currentPath)) {
|
||||||
|
const score = menuPath.split('/').length;
|
||||||
|
if (!bestMatch || score > bestMatch.score) {
|
||||||
|
bestMatch = { path: menu.path, score };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (currentPath.startsWith(menuPath + '/')) {
|
||||||
|
const score = menuPath.split('/').length;
|
||||||
|
if (!bestMatch || score > bestMatch.score) {
|
||||||
|
bestMatch = { path: menu.path, score };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (bestMatch) {
|
||||||
|
return bestMatch.parentId ? [bestMatch.path, bestMatch.parentId] : [bestMatch.path];
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user