React + Ant Design 利用递归动态生成菜单栏

React + Ant Design 利用递归动态生成菜单栏

自定义菜单配置文件

// menu-list.js
// 引入菜单向需要的图标
import {
  HomeOutlined,
  AppstoreOutlined,
  UserOutlined,
  SettingOutlined,
  BarChartOutlined,
  LineChartOutlined,
  UnorderedListOutlined,
  ApartmentOutlined,
  PieChartOutlined,
  AreaChartOutlined
} from '@ant-design/icons'

const menuList = [
  {
    title: '首页',
    key: '/home',
    icon: HomeOutlined
  }, {
    title: '商品管理',
    key: '/products',
    icon: AppstoreOutlined,
    children: [{
      title: '类别管理',
      key: '/category',
      icon: ApartmentOutlined,
    }, {
      title: '增删改查',
      key: '/product',
      icon: UnorderedListOutlined
    }]
  }, {
    title: '用户管理',
    key: '/user',
    icon: UserOutlined
  }, {
    title: '权限设置',
    key: '/role',
    icon: SettingOutlined
  }, {
    title: '数据统计',
    key: '/statistics',
    icon: AreaChartOutlined,
    children: [{
      title: '柱形图',
      key: '/statistics/bar',
      icon: BarChartOutlined
    }, {
      title: '折线图',
      key: '/statistics/line',
      icon: LineChartOutlined
    }, {
      title: '饼图',
      key: '/statistics/pie',
      icon: PieChartOutlined
    }]
  }
];
export default menuList

菜单组件

// left-menu.jsx
import React, { Component } from 'react'
import menuList from './menu-list'
import { withRouter } from 'react-router-dom'
import { Menu } from 'antd'
const { SubMenu } = Menu;

class LeftMenu extends Component {
  state = {
    openKey: []
  };
   
  // 当点击菜单时切换路由,如果当前组件是路由组件,可以直接调用 props 中的 history 对象,如果当前组件非路由组件,需要调用 withRouter 函数,传入当前组件,组件中就可以访问 history 对象了,当前示例 withRouter 函数的调用在代码最后
  handleChangeMenu = ({key}) => {
    this.props.history.push(key);
  };

  // 设置了默认的 openKey 后,手动点击展开关闭菜单功能失效,需要绑定 openChange 函数,动态设置 openKey
  handleOpenChange = (v) => {
    this.setState({
      openKey: v
    })
  };

  // 组件挂载之后设置默认展开项(刷新页面或跳转路由时需要做此处理)
  componentDidMount() {
    // 组件挂载前无法设置 state,所以 setInitOpenKey 函数只是将需要展开的菜单的 key 值保留成类的静态属性 openKey,组件挂载之后再将其设置成 state
    this.setState({
      openKey: [this.openKey] // 默认展开子菜单的 openKey
    });
  }
  
  // 利用 createMenuListMap 的递归调用实现菜单的动态创建,当 menuList 值改变时,菜单也会动态改变,可以将此方法声明成单独的组件,传值 list,并返回 JSX 节点列表
  createMenuListMap = (list) => {
    return list.map((item) => {
      if(item.children) {
      	// 如果当前循环到的菜单项有 children,那就返回 SubMenu,否则返回的直接是 Menu.Item
        const path = this.props.location.pathname;
        const res = item.children.find(child => path.indexOf(child.key) >= 0);
        if(res) this.openKey = item.key;  
        return (
          <SubMenu
            key={item.key}
            title={
              <span>
                <item.icon />
                <span>{item.title}</span>
              </span>
            }
          >
            {
              // 根据当前菜单的 children 去生成其子菜单,由于菜单项 menuList 是个有终结的数据,且嵌套层数并不复杂,所以这里不用担心递归会造成栈溢出的问题
              this.createMenuListMap(item.children)
            }
          </SubMenu>
        );
      } else {
        return (
          <Menu.Item key={item.key}>
            <Link to={item.key}>
              <item.icon />
              <span>{item.title}</span>
            </Link>
          </Menu.Item>
        );
      }
    });
  };
  
  render() {
    return (
      <div className="left-nav">
     	<Menu
          mode="inline"
          theme="dark"
          onClick={this.handleChangeMenu}
          selectedKeys={[this.props.location.pathname]}
          onOpenChange={this.handleOpenChange}
          openKeys={this.state.openKey}
        >
          {
          	// 获取并渲染动态的菜单内容
            this.createMenuListMap(menuList)
          }
        </Menu>
      </div>
    )
  }
}
export default withRouter(LeftMenu)

如果对 Vue 有深入了解,该方式的原理同样适用于 Vue

你可能感兴趣的:(框架,前端开发)