假设myPage002是没有权限的 react-project/src/route/index.jsx 定义路由
import { createBrowserRouter, Navigate } from "react-router-dom";
import { lazy } from "react";
import RequireAuth from "./RequireAuth";
// 动态导入组件
const MyPage001 = lazy(() => import("../views/MyPage001"));
const MyPage002 = lazy(() => import("../views/MyPage002"));
const MyPage003 = lazy(() => import("../views/MyPage003"));
const NotFound = lazy(() => import("../components/NotFound"));
const Layout = lazy(() => import("../views/Layout"));
const Login = lazy(() => import("../views/Login"));
const Home = lazy(() => import("../views/Home"));
const staticRoutes = [
{
path: "/login",
element: <Login />,
meta: {
label: "登录",
},
},
];
const routeConfig = [
{
path: "/",
element: <Layout />,
meta: {
label: "主页",
},
children: [
{
index: true,
element: <Navigate replace to="/home" />,
meta: {
replace: true,
},
},
{
path: "home",
element: <Home />,
meta: {
label: "主页",
first: true,
},
},
],
},
{
path: "/fenxi",
element: <Layout />,
meta: {
label: "分析",
},
children: [
{
index: true,
element: <Navigate replace to="/fenxi/myPage001" />,
meta: {
replace: true,
},
},
{
path: "myPage001",
element: <MyPage001 />,
meta: {
label: "分析一",
},
},
{
path: "myPage002",
element: <RequireAuth><MyPage002 /></RequireAuth>,
meta: {
label: "分析二",
},
},
{
path: "myPage003",
element: <RequireAuth><MyPage003 /></RequireAuth>,
meta: {
label: "分析三",
},
},
],
},
];
const routerConfig = createBrowserRouter(
[
...staticRoutes,
...routeConfig,
{
path: "*",
element: <NotFound />,
},
],
{
basename: "/",
}
);
export { routeConfig ,staticRoutes};
export default routerConfig;
react-project/src/route/RequireAuth.jsx 定义权限,包裹路由页面,如上面的myPage002。
// RequireAuth.js
import { Navigate, useLocation } from "react-router-dom";
function RequireAuth({ children }) {
const location = useLocation();
if (location.pathname === "/fenxi/myPage002") {
// 直接去404
return <Navigate to="/404" />;
// 权限不足则显示无权限页面
// return <div>无权限访问</div>;
}
return children;
}
export default RequireAuth;
/react-project/src/App.jsx 路由出口
import { Suspense, useEffect } from "react";
import { RouterProvider } from "react-router-dom";
import routerConfig from "./route";
function App() {
// const [routers, setRouters] = useState(routerConfig);
return (
<>
<Suspense fallback={<div>加载中...</div>}>
<RouterProvider router={routerConfig} />
</Suspense>
</>
);
}
export default App;
react-project/src/views/Layout.jsx 左侧菜单
通过getItems将只有一层的子菜单提到主菜单,比如:/home
过滤无权限菜单项,比如filterMenuItems移除myPage002
import React, { useState } from "react";
import { Outlet, useNavigate } from "react-router-dom";
import { routeConfig } from "../route/index";
import {
MenuFoldOutlined,
MenuUnfoldOutlined,
PieChartOutlined,
} from "@ant-design/icons";
import { Button, Menu } from "antd";
// function getItems(_routeConfig, items = []) {
// _routeConfig.forEach((item) => {
// if(item?.meta?.replace){
// return
// }
// const _item = {
// key: item.path,
// label: item?.meta?.title,
// icon: <PieChartOutlined />,
// };
// items.push(_item);
// if (item?.children) {
// _item.children = getItems(item?.children);
// }
// });
// return items;
// }
// [...routeConfig].forEach(item=>{
// if(item?.meta?.first){
// item.path = item.path.split('/').pop()
// }
// })
function getItems(_routes) {
const items = [];
_routes.forEach((item) => {
if (
item.children &&
item?.children?.filter((v) => !v.meta?.replace).length === 1
) {
const _item = item.children[1];
_item.path = item.path + _item.path;
_item.label = item.meta.label;
_item.key = _item.path;
_item.icon = <PieChartOutlined />;
items.push(_item);
} else if (
item?.children &&
item?.children?.filter((v) => !v.meta?.replace).length > 1
) {
const _child = item.children.filter((v) => !v.meta?.replace);
item.label = item.meta.label;
item.key = item.path;
item.icon = <PieChartOutlined />;
item.children = _child.map((v) => {
return {
...v,
label: v.meta.label,
path: item.path + "/" + v.path,
key: item.path + "/" + v.path,
icon: <PieChartOutlined />,
};
});
items.push(item);
}
});
return items;
}
/**
* 过滤菜单项,移除指定路径的项
* @param {Array} items - 菜单项数组
* @param {string|Array} excludePaths - 要排除的路径(字符串或数组)
* @returns {Array} 过滤后的菜单项
*/
function filterMenuItems(items, excludePaths = '/fenxi/myPage002') {
// 统一处理排除路径,支持字符串或数组
const excludeList = Array.isArray(excludePaths) ? excludePaths : [excludePaths];
return items
.filter(item => !excludeList.includes(item.path)) // 过滤掉指定路径
.map(item => {
// 如果有子菜单,递归过滤
if (item.children && item.children.length > 0) {
const filteredChildren = filterMenuItems(item.children, excludePaths);
// 如果过滤后还有子菜单,保留children
if (filteredChildren.length > 0) {
return {
...item,
children: filteredChildren
};
} else {
// 如果过滤后没有子菜单,删除children属性
// eslint-disable-next-line no-unused-vars
const { children, ...itemWithoutChildren } = item;
return itemWithoutChildren;
}
}
return item;
});
}
const items = getItems(routeConfig);
// 使用示例:
// 1. 过滤单个路径
const filteredItems = filterMenuItems(items, '/fenxi/myPage002');
// 2. 过滤多个路径
// const filteredItems = filterMenuItems(items, ['/fenxi/myPage002', '/fenxi/myPage003']);
// 3. 不过滤任何路径(显示所有菜单)
// const filteredItems = filterMenuItems(items, []);
console.log("原始菜单项:", items);
console.log("过滤后的菜单项:", filteredItems);
// const items = [
// { key: "1", icon: <PieChartOutlined />, label: "Option 1" },
// { key: "2", icon: <DesktopOutlined />, label: "Option 2" },
// { key: "3", icon: <ContainerOutlined />, label: "Option 3" },
// {
// key: "sub1",
// label: "Navigation One",
// icon: <MailOutlined />,
// children: [
// { key: "5", label: "Option 5" },
// { key: "6", label: "Option 6" },
// { key: "7", label: "Option 7" },
// { key: "8", label: "Option 8" },
// ],
// },
// {
// key: "sub2",
// label: "Navigation Two",
// icon: <AppstoreOutlined />,
// children: [
// { key: "9", label: "Option 9" },
// { key: "10", label: "Option 10" },
// {
// key: "sub3",
// label: "Submenu",
// children: [
// { key: "11", label: "Option 11" },
// { key: "12", label: "Option 12" },
// ],
// },
// ],
// },
// ];
const Layout = () => {
const navigate = useNavigate();
const [collapsed, setCollapsed] = useState(false);
const toggleCollapsed = () => {
setCollapsed(!collapsed);
};
return (
<div style={{ width: 256, display: "flex" }}>
<div>
<Button
type="primary"
onClick={toggleCollapsed}
style={{ marginBottom: 16 }}
>
{collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
</Button>
<Menu
defaultSelectedKeys={["/home"]}
defaultOpenKeys={["/home"]}
mode="inline"
theme="dark"
inlineCollapsed={collapsed}
onClick={(item) => {
console.log(item);
//跳转路由
navigate(item.key);
}}
items={filteredItems}
/>
</div>
<div>
<Outlet />
</div>
</div>
);
};
export default Layout;