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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章