|
|
@@ -1,256 +0,0 @@
|
|
|
-import React, { PureComponent } from 'react';
|
|
|
-import { Layout, Menu, Icon } from 'antd';
|
|
|
-import pathToRegexp from 'path-to-regexp';
|
|
|
-import Link from 'umi/link';
|
|
|
-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') {
|
|
|
- if (icon.indexOf('http') === 0) {
|
|
|
- return <img src={icon} alt="icon" className={`${styles.icon} sider-menu-item-img`} />;
|
|
|
- }
|
|
|
- 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 => {
|
|
|
- const { menuData } = this.props;
|
|
|
- return 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, menuData, 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(menuData)}
|
|
|
- </Menu>
|
|
|
- </Sider>
|
|
|
- );
|
|
|
- }
|
|
|
-}
|