揭秘如何使用react 18 + antd (v5.3.0) 动态生成左侧菜单栏?

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会报错

揭秘如何使用react 18 + antd (v5.3.0) 动态生成左侧菜单栏?_第1张图片

解释

  • useEffect中的第一个回调参数返回的是一个clean-up函数,所以不能返回promise对象,更不能直接使用async/await,否则会报错;
  • 可以在回调参数中使用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();
  }, []);

第三步:利用antd提供的菜单menu渲染


  
{siteBaseConfig.siteName}

小细节

使用icon – iconList

这里,定义了一组导航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]);

当前展开的 SubMenu 菜单项

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}
); }

展示结果

揭秘如何使用react 18 + antd (v5.3.0) 动态生成左侧菜单栏?_第2张图片

你可能感兴趣的:(前端杂货铺,react.js,javascript,前端)