天天看點

react+mobx實作側邊欄菜單

菜單配置檔案

container > sideMenu > configMenu.js

/**
 * @description 左側菜單配置
 * @param(leafMenuModels) 子節點菜單
 * @param(active) boolean 目前菜單是否被選中
 * @param(id) string 菜單ID,唯一,不能重複
 * @param(name) string  菜單名稱
 * @param(router)  菜單對應的路由
 * @param(activeRouter)  目前菜單下包含的路由
 */

module.exports = {
  id: "0",
  name: "票務平台",
  leafMenuModels: [
    {
      id: "1",
      active: true,
      name: "平台首頁",
      router: "/",
      activeRouter: ["/", "/ticket/create/:key/:conferenceId"]
    },
    {
      id: "2",
      active: true,
      name: "建立會場",
      router: "/meeting/create",
      activeRouter: ["/meeting/create", "/meeting/create/:id"]
    },
    {
      id: "3",
      active: true,
      name: "資料檢視",
      router: "/data/view",
      activeRouter: ["/data/view", "/data/view/:id"]
    },
    {
      id: "4",
      active: true,
      name: "訂單清單",
      router: "/order",
      activeRouter: ["/order", "/order/:searchParams", "/orderDetail/:orderId"]
    }
  ]
};
           

菜單主檔案

container > sideMenu > index.js

import React, { Component } from "react";
import { Link, NavLink, withRouter } from "react-router-dom";
import { toJS } from "mobx";
import { inject, observer } from "mobx-react";
import { Menu, Icon, Button } from "antd";
const SubMenu = Menu.SubMenu;

@inject("MenuStore")
@observer
class SideMenu extends Component {
  componentDidMount() {
  	// 設定menu
    this.props.MenuStore.initMenu(this.props);
  }

  // react路由發生變化時會觸發該函數
  componentWillReceiveProps(nextProps) {
  	// 設定menu
    this.props.MenuStore.initMenu(nextProps);
  }

  // 菜單切換時觸發
  toSkip = path => {
    this.props.history.push(path);
  };

  render() {
    const { menus, selectedKeys } = this.props.MenuStore;
    return (
      <div className="container-side-menu">
        <Menu selectedKeys={toJS(selectedKeys)} mode="inline" theme="dark">
          {menus.map(item => {
            return (
              <Menu.Item
                key={item.id}
                onClick={this.toSkip.bind(this, item.router)}
              >
                <span>{item.name}</span>
              </Menu.Item>
            );
          })}
        </Menu>
      </div>
    );
  }
}

export default withRouter(SideMenu);
           

菜單store檔案

stores > MenuStore.js :

import { action, observable } from "mobx";
import menuConfig from "../containers/SideMenu/configMenu";
// 方法在下面?
import { isActiveMenu } from "../utils/menu";
const uuid = require("uuid/v1");

class MenuStore {
  @observable menus = [];
  @observable selectedKeys = []; // 目前選中的菜單

  // 初始化進來的時候,判斷路由,設定菜單的active屬性以及被選中的key的集合
  @action
  initMenu(props) {
    let { leafMenuModels: menus } = menuConfig;
    let pathname = props.location.pathname;

    for (let index = 0; index < menus.length; index++) {
      let menu = menus[index];
      if (isActiveMenu(menu, pathname) === true) {
        this.selectedKeys = [menu.id];
      }
      menu.active = isActiveMenu(menu, pathname);
    }
    this.menus = menus;
  }
}
const menuStore = new MenuStore();
export default menuStore;
           

根據路由判斷菜單是否應該被選中

utils > menu.js :

import { forEach } from "lodash";
// 将路徑字元串轉換為正規表達式  需要使用**安裝**
import pathToRegexp from "path-to-regexp";

// 根據路由判斷菜單是否應該被選中
function isActiveMenu(menu, pathname) {
  let router = menu.activeRouter || null,
    active = false;
  if (router) {
    if (Array.isArray(router) && router.length) {
      forEach(router, item => {
        // item: /orderDetail/:orderId ; pathname: /orderDetail/1
        if (!active && pathToRegexp(item).test(pathname)) {
          active = true;
        }
      });
    }
    if (
      typeof router === "string" &&
      !active &&
      pathToRegexp(router).test(pathname)
    ) {
      active = true;
    }
  }
  return active;
}

export { isActiveMenu };