BaseMenu.js 4.49 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
        // make dom
        const ItemDom = this.getSubMenuOrItem(item);
        return this.checkPermissionItem(item.authority, ItemDom);
      })
      .filter(item => item);
  };
  // Get the currently selected menu
  getSelectedMenuKeys = () => {
jim's avatar
jim committed
70 71 72
    const {
      location: { pathname },
    } = this.props;
Erwin Zhang's avatar
Erwin Zhang committed
73
    return urlToList(pathname).map(itemPath => getMenuMatches(this.flatMenuKeys, itemPath).pop());
jim's avatar
jim committed
74 75 76 77
  };
  /**
   * get SubMenu or Item
   */
jim's avatar
jim committed
78
  getSubMenuOrItem = item => {
jim's avatar
jim committed
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
    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
98
      return <Menu.Item key={item.path}>{this.getMenuItemPath(item)}</Menu.Item>;
jim's avatar
jim committed
99 100 101 102 103 104 105
    }
  };
  /**
   * 判断是否是http链接.返回 Link 或 a
   * Judge whether it is http link.return a or Link
   * @memberof SiderMenu
   */
jim's avatar
jim committed
106
  getMenuItemPath = item => {
jim's avatar
jim committed
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 143 144
    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
145
  conversionPath = path => {
jim's avatar
jim committed
146 147 148 149 150 151 152
    if (path && path.indexOf('http') === 0) {
      return path;
    } else {
      return `/${path || ''}`.replace(/\/+/g, '/');
    }
  };
  render() {
jim's avatar
jim committed
153
    const { openKeys, theme, mode } = this.props;
jim's avatar
jim committed
154 155 156 157 158
    // if pathname can't match, use the nearest parent's key
    let selectedKeys = this.getSelectedMenuKeys();
    if (!selectedKeys.length && openKeys) {
      selectedKeys = [openKeys[openKeys.length - 1]];
    }
jim's avatar
jim committed
159 160 161 162 163 164
    let props = {};
    if (openKeys) {
      props = {
        openKeys,
      };
    }
jim's avatar
jim committed
165 166 167
    return (
      <Menu
        key="Menu"
jim's avatar
jim committed
168 169
        mode={mode}
        theme={theme}
jim's avatar
jim committed
170 171 172
        onOpenChange={this.props.handleOpenChange}
        selectedKeys={selectedKeys}
        style={this.props.style}
jim's avatar
jim committed
173
        {...props}
jim's avatar
jim committed
174 175 176 177 178 179
      >
        {this.getNavMenuItems(this.menus)}
      </Menu>
    );
  }
}