antd专门为react定制的中后台组件库,提供了大量的组件供开发者使用,
官网地址 点击跳转
在中后台中,菜单项是必不可少的,今天就使用react结合antd V5.0 配置一个菜单栏目.
由于antdV5.0的做了升级,Menu导航菜单组件使用有了比较大的变化。
下面就开始使用antd v5构建菜单栏目。
从后台得到的数据结构(根据不同角色返回的不同菜单数据),比如:
const menusListData = [
{
"id": 1,
"title": "首页",
"key": "/home",
"pagepermisson": 1,
"grade": 1,
"children": []
},
{
"id": 2,
"title": "用户管理",
"key": "/user-manage",
"pagepermisson": 1,
"grade": 1,
"children": [
{
"id": 3,
"title": "添加用户",
"rightId": 2,
"key": "/user-manage/add",
"grade": 2
}
]
},
{
"id": 7,
"title": "权限管理",
"key": "/right-manage",
"pagepermisson": 1,
"grade": 1,
"children": [
{
"id": 8,
"title": "角色列表",
"rightId": 7,
"key": "/right-manage/role/list",
"pagepermisson": 1,
"grade": 2
}
]
},
{
"id": 14,
"title": "新闻管理",
"key": "/news-manage",
"pagepermisson": 1,
"grade": 1,
"children": [
{
"id": 15,
"title": "新闻列表",
"rightId": 14,
"key": "/news-manage/list",
"grade": 2
}
]
},
{
"id": 21,
"title": "审核管理",
"key": "/audit-manage",
"pagepermisson": 1,
"grade": 1,
"children": [
{
"id": 22,
"title": "审核新闻",
"rightId": 21,
"key": "/audit-manage/audit",
"pagepermisson": 1,
"grade": 2
}
]
},
{
"id": 24,
"title": "发布管理",
"key": "/publish-manage",
"pagepermisson": 1,
"grade": 1,
"children": [
{
"id": 25,
"title": "待发布",
"rightId": 24,
"key": "/publish-manage/unpublished",
"pagepermisson": 1,
"grade": 2
}
]
}
]
特别注意:useEffect中直接使用async/await会报错
解释
useEffect(()=>{
const fn=async ()=>{
// do something
await otherFn()
}
fn()
},[])
处理获取到的路由数据处理函数
useEffect(() => {
async function fetchData() {
const menusListData = await fetchGetMenus();
let tempItems = [],
rootSubmenuKeys = []; // submenu keys of first level
// 注意此处
menusListData.forEach((item) => {
item.key !== "/login" && rootSubmenuKeys.push(item.key)
item.key !== "/login" &&
item.pagepermisson === 1 &&
tempItems.push({
label: item.title,
key: item.key,
icon: iconList[item.key],
children:
item.children &&
item.children.length > 0 &&
item.children.map((child) => {
if (child.pagepermisson === 1) {
return {
label: child.title,
key: child.key,
icon: iconList[item.key],
children:
child.children &&
child.children.length > 0 &&
child.children.map((sun) => {
if (child.pagepermisson === 1) {
return {
label: sun.title,
key: sun.key,
icon: iconList[item.key],
};
}
}),
};
}
}),
});
});
setitems(tempItems);
}
fetchData();
}, []);
{siteBaseConfig.siteName}
这里,定义了一组导航icon数据,根据路径,使用不同的icon
“./iconList”
// "./iconList"
import {
UserOutlined,
LaptopOutlined,
NotificationOutlined,
} from "@ant-design/icons";
export const iconList = {
"/home": ,
"/user-manage": ,
// 权限管理
"/right-manage": ,
// 新闻管理
"/news-manage":
SideMenu.js
// SideMenu.js
import { iconList } from "./iconList";
// 使用
icon: iconList[item.key],
defaultSelectedKeys --初始选中的菜单项 key 数组 string[]
defaultOpenKeys={defaultOpenKeys} // 初始展开的 SubMenu 菜单项 key 数组
// defaultSelectedKeys 初始选中的菜单项 key 数组
const [defaultSelectedKeys, setDefaultSelectedKeys] = useState([]);
// defaultSelectedKeys 初始选中的菜单项 key 数组
const [defaultSelectedKeys, setDefaultSelectedKeys] = useState([]);
useEffect(() => {
const routes = matchRoutes(routers, location.pathname); // 返回匹配到的路由数组对象,每一个对象都是一个路由对象
const pathArr = [];
if (routes !== null) {
routes.forEach((item) => {
const path = item.pathname;
if (path) {
pathArr.push(path);
}
});
}
setDefaultSelectedKeys(pathArr);
setDefaultOpenKeys(pathArr);
}, [location.pathname]);
openKeys 当前展开的 SubMenu 菜单项 key 数组
onOpenChange SubMenu 展开/关闭的回调
// submenu keys of first level
const [rootSubmenuKeys , setrootSubmenuKeys ] = useState([])
// openKeys 当前展开的 SubMenu 菜单项 key 数组
const [openKeys, setOpenKeys] = useState([]);
const onOpenChange = (keys) => {
const latestOpenKey = keys.find((key) => openKeys.indexOf(key) === -1);
if (rootSubmenuKeys.indexOf(latestOpenKey) === -1) {
setOpenKeys(keys);
} else {
setOpenKeys(latestOpenKey ? [latestOpenKey] : []);
}
};
import { useEffect, useState } from "react";
import { Layout, Menu } from "antd";
import { matchRoutes, useLocation, useNavigate } from "react-router-dom";
import { routers } from "../../router";
import styles from "./SideMenu.module.css";
import siteBaseConfig from "../../config";
// 接口请求
import { fetchGetMenus } from "../../utils/api";
import { iconList } from "./iconList";
const { Sider } = Layout;
export default function AppLayout() {
const location = useLocation();
const navigate = useNavigate();
const [isInit, setIsInit] = useState(false);
const [collapsed, setCollapsed] = useState(false);
// items 菜单内容 ItemType[]
const [items, setitems] = useState([]);
// defaultSelectedKeys 初始选中的菜单项 key 数组
const [defaultSelectedKeys, setDefaultSelectedKeys] = useState([]);
// defaultOpenKeys 初始展开的 SubMenu 菜单项 key 数组
const [defaultOpenKeys, setDefaultOpenKeys] = useState([]);
// submenu keys of first level
const [rootSubmenuKeys , setrootSubmenuKeys ] = useState([])
// openKeys 当前展开的 SubMenu 菜单项 key 数组
const [openKeys, setOpenKeys] = useState([]);
useEffect(() => {
async function fetchData() {
const menusListData = await fetchGetMenus();
let tempItems = [],
rootSubmenuKeys = []; // submenu keys of first level
menusListData.forEach((item) => {
item.key !== "/login" && rootSubmenuKeys.push(item.key)
item.key !== "/login" &&
item.pagepermisson === 1 &&
tempItems.push({
label: item.title,
key: item.key,
icon: iconList[item.key],
children:
item.children &&
item.children.length > 0 &&
item.children.map((child) => {
if (child.pagepermisson === 1) {
return {
label: child.title,
key: child.key,
icon: iconList[item.key],
children:
child.children &&
child.children.length > 0 &&
child.children.map((sun) => {
if (child.pagepermisson === 1) {
return {
label: sun.title,
key: sun.key,
icon: iconList[item.key],
};
}
}),
};
}
}),
});
});
setitems(tempItems);
setrootSubmenuKeys(rootSubmenuKeys)
}
fetchData();
}, []);
useEffect(() => {
const routes = matchRoutes(routers, location.pathname); // 返回匹配到的路由数组对象,每一个对象都是一个路由对象
const pathArr = [];
if (routes !== null) {
routes.forEach((item) => {
const path = item.pathname;
if (path) {
pathArr.push(path);
}
});
}
setDefaultSelectedKeys(pathArr);
setDefaultOpenKeys(pathArr);
setIsInit(true);
}, [location.pathname]);
if (!isInit) {
return null;
}
const onClick = (e) => {
navigate(e.key);
};
const onOpenChange = (keys) => {
const latestOpenKey = keys.find((key) => openKeys.indexOf(key) === -1);
if (rootSubmenuKeys.indexOf(latestOpenKey) === -1) {
setOpenKeys(keys);
} else {
setOpenKeys(latestOpenKey ? [latestOpenKey] : []);
}
};
return (
<>
{siteBaseConfig.siteName}
>
);
}
展示结果