BaseMenu.js 4.47 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
  constructor(props) {
    super(props);
    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
42
    menus.forEach(item => {
jim's avatar
jim committed
43 44 45 46 47 48 49 50 51 52 53
      if (item.children) {
        keys = keys.concat(this.getFlatMenuKeys(item.children));
      }
      keys.push(item.path);
    });
    return keys;
  }
  /**
   * θŽ·εΎ—θœε•ε­θŠ‚η‚Ή
   * @memberof SiderMenu
   */
jim's avatar
jim committed
54
  getNavMenuItems = menusData => {
jim's avatar
jim committed
55 56 57 58 59
    if (!menusData) {
      return [];
    }
    return menusData
      .filter(item => item.name && !item.hideInMenu)
jim's avatar
jim committed
60
      .map(item => {
jim's avatar
jim committed
61 62 63 64 65 66 67 68
        // 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
69 70 71
    const {
      location: { pathname },
    } = this.props;
Erwin Zhang's avatar
Erwin Zhang committed
72
    return urlToList(pathname).map(itemPath => getMenuMatches(this.flatMenuKeys, itemPath).pop());
jim's avatar
jim committed
73 74 75 76
  };
  /**
   * get SubMenu or Item
   */
jim's avatar
jim committed
77
  getSubMenuOrItem = item => {
jim's avatar
jim committed
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
    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
97
      return <Menu.Item key={item.path}>{this.getMenuItemPath(item)}</Menu.Item>;
jim's avatar
jim committed
98 99 100 101 102 103 104
    }
  };
  /**
   * εˆ€ζ–­ζ˜―ε¦ζ˜―httpι“ΎζŽ₯.θΏ”ε›ž Link ζˆ– a
   * Judge whether it is http link.return a or Link
   * @memberof SiderMenu
   */
jim's avatar
jim committed
105
  getMenuItemPath = item => {
jim's avatar
jim committed
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 143
    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
144
  conversionPath = path => {
jim's avatar
jim committed
145 146 147 148 149 150 151
    if (path && path.indexOf('http') === 0) {
      return path;
    } else {
      return `/${path || ''}`.replace(/\/+/g, '/');
    }
  };
  render() {
jim's avatar
jim committed
152
    const { openKeys, theme, mode } = this.props;
jim's avatar
jim committed
153 154 155 156 157
    // 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
158 159 160 161 162 163
    let props = {};
    if (openKeys) {
      props = {
        openKeys,
      };
    }
jim's avatar
jim committed
164 165 166
    return (
      <Menu
        key="Menu"
jim's avatar
jim committed
167 168
        mode={mode}
        theme={theme}
jim's avatar
jim committed
169 170 171
        onOpenChange={this.props.handleOpenChange}
        selectedKeys={selectedKeys}
        style={this.props.style}
jim's avatar
jim committed
172
        {...props}
jim's avatar
jim committed
173
      >
ι™ˆεΈ…'s avatar
ι™ˆεΈ… committed
174
        {this.getNavMenuItems(this.props.menuData)}
jim's avatar
jim committed
175 176 177 178
      </Menu>
    );
  }
}