BaseMenu.js 4.36 KB
Newer Older
jim's avatar
jim committed
1 2 3 4
import React, { PureComponent } from 'react';
import { Menu, Icon } from 'antd';
import { Link } from 'dva/router';
import pathToRegexp from 'path-to-regexp';
jim's avatar
jim committed
5
import { urlToList } from '../_utils/pathTools';
jim's avatar
jim committed
6 7 8 9 10 11 12 13
import styles from './index.less';

const { SubMenu } = Menu;

// Allow menu.js config icon as string or ReactNode
//   icon: 'setting',
//   icon: 'http://demo.com/icon.png',
//   icon: <Icon type="setting" />,
jim's avatar
jim committed
14
const getIcon = icon => {
jim's avatar
jim committed
15 16 17 18 19 20 21 22 23
  if (typeof icon === 'string' && icon.indexOf('http') === 0) {
    return <img src={icon} alt="icon" className={styles.icon} />;
  }
  if (typeof icon === 'string') {
    return <Icon type={icon} />;
  }
  return icon;
};

Erwin Zhang's avatar
Erwin Zhang committed
24
export const getMenuMatches = (flatMenuKeys, path) => {
jim's avatar
jim committed
25
  return flatMenuKeys.filter(item => {
jim's avatar
jim committed
26 27 28 29
    return pathToRegexp(item).test(path);
  });
};

Erwin Zhang's avatar
Erwin Zhang committed
30
export default class BaseMenu extends PureComponent {
jim's avatar
jim committed
31 32 33 34 35 36 37 38 39 40 41 42
  constructor(props) {
    super(props);
    this.menus = props.menuData;
    this.flatMenuKeys = this.getFlatMenuKeys(props.menuData);
  }
  /**
   * Recursively flatten the data
   * [{path:string},{path:string}] => {path,path2}
   * @param  menus
   */
  getFlatMenuKeys(menus) {
    let keys = [];
jim's avatar
jim committed
43
    menus.forEach(item => {
jim's avatar
jim committed
44 45 46 47 48 49 50 51 52 53 54
      if (item.children) {
        keys = keys.concat(this.getFlatMenuKeys(item.children));
      }
      keys.push(item.path);
    });
    return keys;
  }
  /**
   * θŽ·εΎ—θœε•ε­θŠ‚η‚Ή
   * @memberof SiderMenu
   */
jim's avatar
jim committed
55
  getNavMenuItems = menusData => {
jim's avatar
jim committed
56 57 58 59 60
    if (!menusData) {
      return [];
    }
    return menusData
      .filter(item => item.name && !item.hideInMenu)
jim's avatar
jim committed
61
      .map(item => {
jim's avatar
jim committed
62 63 64 65 66 67 68 69 70
        // make dom
        const ItemDom = this.getSubMenuOrItem(item);
        return this.checkPermissionItem(item.authority, ItemDom);
      })
      .filter(item => item);
  };
  // Get the currently selected menu
  getSelectedMenuKeys = () => {
    const { location: { pathname } } = this.props;
Erwin Zhang's avatar
Erwin Zhang committed
71
    return urlToList(pathname).map(itemPath => getMenuMatches(this.flatMenuKeys, itemPath).pop());
jim's avatar
jim committed
72 73 74 75
  };
  /**
   * get SubMenu or Item
   */
jim's avatar
jim committed
76
  getSubMenuOrItem = item => {
jim's avatar
jim committed
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
    if (item.children && item.children.some(child => child.name)) {
      return (
        <SubMenu
          title={
            item.icon ? (
              <span>
                {getIcon(item.icon)}
                <span>{item.name}</span>
              </span>
            ) : (
              item.name
            )
          }
          key={item.path}
        >
          {this.getNavMenuItems(item.children)}
        </SubMenu>
      );
    } else {
jim's avatar
jim committed
96
      return <Menu.Item key={item.path}>{this.getMenuItemPath(item)}</Menu.Item>;
jim's avatar
jim committed
97 98 99 100 101 102 103
    }
  };
  /**
   * εˆ€ζ–­ζ˜―ε¦ζ˜―httpι“ΎζŽ₯.θΏ”ε›ž Link ζˆ– a
   * Judge whether it is http link.return a or Link
   * @memberof SiderMenu
   */
jim's avatar
jim committed
104
  getMenuItemPath = item => {
jim's avatar
jim committed
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
    const itemPath = this.conversionPath(item.path);
    const icon = getIcon(item.icon);
    const { target, name } = item;
    // Is it a http link
    if (/^https?:\/\//.test(itemPath)) {
      return (
        <a href={itemPath} target={target}>
          {icon}
          <span>{name}</span>
        </a>
      );
    }
    return (
      <Link
        to={itemPath}
        target={target}
        replace={itemPath === this.props.location.pathname}
        onClick={
          this.props.isMobile
            ? () => {
                this.props.onCollapse(true);
              }
            : undefined
        }
      >
        {icon}
        <span>{name}</span>
      </Link>
    );
  };
  // permission to check
  checkPermissionItem = (authority, ItemDom) => {
    if (this.props.Authorized && this.props.Authorized.check) {
      const { check } = this.props.Authorized;
      return check(authority, ItemDom);
    }
    return ItemDom;
  };
jim's avatar
jim committed
143
  conversionPath = path => {
jim's avatar
jim committed
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
    if (path && path.indexOf('http') === 0) {
      return path;
    } else {
      return `/${path || ''}`.replace(/\/+/g, '/');
    }
  };
  render() {
    const { openKeys } = this.props;
    // if pathname can't match, use the nearest parent's key
    let selectedKeys = this.getSelectedMenuKeys();
    if (!selectedKeys.length && openKeys) {
      selectedKeys = [openKeys[openKeys.length - 1]];
    }
    return (
      <Menu
        key="Menu"
        mode="inline"
        onOpenChange={this.props.handleOpenChange}
        selectedKeys={selectedKeys}
        style={this.props.style}
        {...this.props}
      >
        {this.getNavMenuItems(this.menus)}
      </Menu>
    );
  }
}