Преглед изворни кода

修复侧边栏菜单不能动态显示的问题

杨伟荣 пре 7 година
родитељ
комит
a5b5f9c8da
1 измењених фајлова са 254 додато и 0 уклоњено
  1. 254 0
      src/components/SiderMenu/SiderMenu.js

+ 254 - 0
src/components/SiderMenu/SiderMenu.js

@@ -0,0 +1,254 @@
+import React, { PureComponent } from 'react';
+import { Layout, Menu, Icon } from 'antd';
+import pathToRegexp from 'path-to-regexp';
+import { Link } from 'dva/router';
+import styles from './index.less';
+import { urlToList } from '../_utils/pathTools';
+
+const { Sider } = Layout;
+const { SubMenu } = Menu;
+
+// Allow menu.js config icon as string or ReactNode
+//   icon: 'setting',
+//   icon: 'http://demo.com/icon.png',
+//   icon: <Icon type="setting" />,
+const getIcon = icon => {
+  if (typeof icon === 'string' && icon.indexOf('http') === 0) {
+    return <img src={icon} alt="icon" className={`${styles.icon} sider-menu-item-img`} />;
+  }
+  if (typeof icon === 'string') {
+    return <Icon type={icon} />;
+  }
+  return icon;
+};
+
+/**
+ * Recursively flatten the data
+ * [{path:string},{path:string}] => {path,path2}
+ * @param  menu
+ */
+export const getFlatMenuKeys = menu =>
+  menu.reduce((keys, item) => {
+    keys.push(item.path);
+    if (item.children) {
+      return keys.concat(getFlatMenuKeys(item.children));
+    }
+    return keys;
+  }, []);
+
+/**
+ * Find all matched menu keys based on paths
+ * @param  flatMenuKeys: [/abc, /abc/:id, /abc/:id/info]
+ * @param  paths: [/abc, /abc/11, /abc/11/info]
+ */
+export const getMenuMatchKeys = (flatMenuKeys, paths) =>
+  paths.reduce(
+    (matchKeys, path) =>
+      matchKeys.concat(flatMenuKeys.filter(item => pathToRegexp(item).test(path))),
+    []
+  );
+
+export default class SiderMenu extends PureComponent {
+  constructor(props) {
+    super(props);
+    this.flatMenuKeys = getFlatMenuKeys(props.menuData);
+    this.state = {
+      openKeys: this.getDefaultCollapsedSubMenus(props),
+    };
+  }
+
+  componentWillReceiveProps(nextProps) {
+    const { location } = this.props;
+    if (nextProps.location.pathname !== location.pathname) {
+      this.setState({
+        openKeys: this.getDefaultCollapsedSubMenus(nextProps),
+      });
+    }
+  }
+
+  /**
+   * Convert pathname to openKeys
+   * /list/search/articles = > ['list','/list/search']
+   * @param  props
+   */
+  getDefaultCollapsedSubMenus(props) {
+    const {
+      location: { pathname },
+    } =
+      props || this.props;
+    return getMenuMatchKeys(this.flatMenuKeys, urlToList(pathname));
+  }
+
+  /**
+   * 判断是否是http链接.返回 Link 或 a
+   * Judge whether it is http link.return a or Link
+   * @memberof SiderMenu
+   */
+  getMenuItemPath = item => {
+    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>
+      );
+    }
+    const { location, isMobile, onCollapse } = this.props;
+    return (
+      <Link
+        to={itemPath}
+        target={target}
+        replace={itemPath === location.pathname}
+        onClick={
+          isMobile
+            ? () => {
+                onCollapse(true);
+              }
+            : undefined
+        }
+      >
+        {icon}
+        <span>{name}</span>
+      </Link>
+    );
+  };
+
+  /**
+   * get SubMenu or Item
+   */
+  getSubMenuOrItem = item => {
+    if (item.children && item.children.some(child => child.name)) {
+      const childrenItems = this.getNavMenuItems(item.children);
+      // 当无子菜单时就不展示菜单
+      if (childrenItems && childrenItems.length > 0) {
+        return (
+          <SubMenu
+            title={
+              item.icon ? (
+                <span>
+                  {getIcon(item.icon)}
+                  <span>{item.name}</span>
+                </span>
+              ) : (
+                item.name
+              )
+            }
+            key={item.path}
+          >
+            {childrenItems}
+          </SubMenu>
+        );
+      }
+      return null;
+    } else {
+      return <Menu.Item key={item.path}>{this.getMenuItemPath(item)}</Menu.Item>;
+    }
+  };
+
+  /**
+   * 获得菜单子节点
+   * @memberof SiderMenu
+   */
+  getNavMenuItems = menusData => {
+    if (!menusData) {
+      return [];
+    }
+    return menusData
+      .filter(item => item.name && !item.hideInMenu)
+      .map(item => {
+        // 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;
+    return getMenuMatchKeys(this.flatMenuKeys, urlToList(pathname));
+  };
+
+  // conversion Path
+  // 转化路径
+  conversionPath = path => {
+    if (path && path.indexOf('http') === 0) {
+      return path;
+    } else {
+      return `/${path || ''}`.replace(/\/+/g, '/');
+    }
+  };
+
+  // permission to check
+  checkPermissionItem = (authority, ItemDom) => {
+    const { Authorized } = this.props;
+    if (Authorized && Authorized.check) {
+      const { check } = Authorized;
+      return check(authority, ItemDom);
+    }
+    return ItemDom;
+  };
+
+  isMainMenu = key => {
+    return this.props.menuData.some(item => key && (item.key === key || item.path === key));
+  };
+
+  handleOpenChange = openKeys => {
+    const lastOpenKey = openKeys[openKeys.length - 1];
+    const moreThanOne = openKeys.filter(openKey => this.isMainMenu(openKey)).length > 1;
+    this.setState({
+      openKeys: moreThanOne ? [lastOpenKey] : [...openKeys],
+    });
+  };
+
+  render() {
+    const { logo, collapsed, onCollapse } = this.props;
+    const { openKeys } = this.state;
+    // Don't show popup menu when it is been collapsed
+    const menuProps = collapsed
+      ? {}
+      : {
+          openKeys,
+        };
+    // if pathname can't match, use the nearest parent's key
+    let selectedKeys = this.getSelectedMenuKeys();
+    if (!selectedKeys.length) {
+      selectedKeys = [openKeys[openKeys.length - 1]];
+    }
+    return (
+      <Sider
+        trigger={null}
+        collapsible
+        collapsed={collapsed}
+        breakpoint="lg"
+        onCollapse={onCollapse}
+        width={256}
+        className={styles.sider}
+      >
+        <div className={styles.logo} key="logo">
+          <Link to="/">
+            <img src={logo} alt="logo" />
+            <h1>Ant Design Pro</h1>
+          </Link>
+        </div>
+        <Menu
+          key="Menu"
+          theme="dark"
+          mode="inline"
+          {...menuProps}
+          onOpenChange={this.handleOpenChange}
+          selectedKeys={selectedKeys}
+          style={{ padding: '16px 0', width: '100%' }}
+        >
+          {this.getNavMenuItems(this.props.menuData)}
+        </Menu>
+      </Sider>
+    );
+  }
+}