|
|
@@ -3,6 +3,7 @@ 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;
|
|
|
@@ -21,10 +22,17 @@ const getIcon = (icon) => {
|
|
|
return icon;
|
|
|
};
|
|
|
|
|
|
+export const getMeunMatcheys = (flatMenuKeys, path) => {
|
|
|
+ return flatMenuKeys.filter((item) => {
|
|
|
+ return pathToRegexp(item).test(path);
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
export default class SiderMenu extends PureComponent {
|
|
|
constructor(props) {
|
|
|
super(props);
|
|
|
this.menus = props.menuData;
|
|
|
+ this.flatMenuKeys = this.getFlatMenuKeys(props.menuData);
|
|
|
this.state = {
|
|
|
openKeys: this.getDefaultCollapsedSubMenus(props),
|
|
|
};
|
|
|
@@ -43,30 +51,11 @@ export default class SiderMenu extends PureComponent {
|
|
|
*/
|
|
|
getDefaultCollapsedSubMenus(props) {
|
|
|
const { location: { pathname } } = props || this.props;
|
|
|
- // eg. /list/search/articles = > ['','list','search','articles']
|
|
|
- let snippets = pathname.split('/');
|
|
|
- // Delete the end
|
|
|
- // eg. delete 'articles'
|
|
|
- snippets.pop();
|
|
|
- // Delete the head
|
|
|
- // eg. delete ''
|
|
|
- snippets.shift();
|
|
|
- // eg. After the operation is completed, the array should be ['list','search']
|
|
|
- // eg. Forward the array as ['list','list/search']
|
|
|
- snippets = snippets.map((item, index) => {
|
|
|
- // If the array length > 1
|
|
|
- if (index > 0) {
|
|
|
- // eg. search => ['list','search'].join('/')
|
|
|
- return snippets.slice(0, index + 1).join('/');
|
|
|
- }
|
|
|
- // index 0 to not do anything
|
|
|
- return item;
|
|
|
- });
|
|
|
- snippets = snippets.map((item) => {
|
|
|
- return this.getSelectedMenuKeys(`/${item}`)[0];
|
|
|
- });
|
|
|
- // eg. ['list','list/search']
|
|
|
- return snippets;
|
|
|
+ return urlToList(pathname)
|
|
|
+ .map((item) => {
|
|
|
+ return getMeunMatcheys(this.flatMenuKeys, item)[0];
|
|
|
+ })
|
|
|
+ .filter(item => item);
|
|
|
}
|
|
|
/**
|
|
|
* Recursively flatten the data
|
|
|
@@ -77,29 +66,17 @@ export default class SiderMenu extends PureComponent {
|
|
|
let keys = [];
|
|
|
menus.forEach((item) => {
|
|
|
if (item.children) {
|
|
|
- keys.push(item.path);
|
|
|
keys = keys.concat(this.getFlatMenuKeys(item.children));
|
|
|
- } else {
|
|
|
- keys.push(item.path);
|
|
|
}
|
|
|
+ keys.push(item.path);
|
|
|
});
|
|
|
return keys;
|
|
|
}
|
|
|
/**
|
|
|
- * Get selected child nodes
|
|
|
- * /user/chen => ['user','/user/:id']
|
|
|
+ * 判断是否是http链接.返回 Link 或 a
|
|
|
+ * Judge whether it is http link.return a or Link
|
|
|
+ * @memberof SiderMenu
|
|
|
*/
|
|
|
- getSelectedMenuKeys = (path) => {
|
|
|
- const flatMenuKeys = this.getFlatMenuKeys(this.menus);
|
|
|
- return flatMenuKeys.filter((item) => {
|
|
|
- return pathToRegexp(`/${item}(.*)`).test(path);
|
|
|
- });
|
|
|
- }
|
|
|
- /**
|
|
|
- * 判断是否是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);
|
|
|
@@ -108,7 +85,8 @@ export default class SiderMenu extends PureComponent {
|
|
|
if (/^https?:\/\//.test(itemPath)) {
|
|
|
return (
|
|
|
<a href={itemPath} target={target}>
|
|
|
- {icon}<span>{name}</span>
|
|
|
+ {icon}
|
|
|
+ <span>{name}</span>
|
|
|
</a>
|
|
|
);
|
|
|
}
|
|
|
@@ -117,16 +95,23 @@ export default class SiderMenu extends PureComponent {
|
|
|
to={itemPath}
|
|
|
target={target}
|
|
|
replace={itemPath === this.props.location.pathname}
|
|
|
- onClick={this.props.isMobile ? () => { this.props.onCollapse(true); } : undefined}
|
|
|
+ onClick={
|
|
|
+ this.props.isMobile
|
|
|
+ ? () => {
|
|
|
+ this.props.onCollapse(true);
|
|
|
+ }
|
|
|
+ : undefined
|
|
|
+ }
|
|
|
>
|
|
|
- {icon}<span>{name}</span>
|
|
|
+ {icon}
|
|
|
+ <span>{name}</span>
|
|
|
</Link>
|
|
|
);
|
|
|
- }
|
|
|
+ };
|
|
|
/**
|
|
|
* get SubMenu or Item
|
|
|
*/
|
|
|
- getSubMenuOrItem=(item) => {
|
|
|
+ getSubMenuOrItem = (item) => {
|
|
|
if (item.children && item.children.some(child => child.name)) {
|
|
|
return (
|
|
|
<SubMenu
|
|
|
@@ -136,8 +121,10 @@ export default class SiderMenu extends PureComponent {
|
|
|
{getIcon(item.icon)}
|
|
|
<span>{item.name}</span>
|
|
|
</span>
|
|
|
- ) : item.name
|
|
|
- }
|
|
|
+ ) : (
|
|
|
+ item.name
|
|
|
+ )
|
|
|
+ }
|
|
|
key={item.path}
|
|
|
>
|
|
|
{this.getNavMenuItems(item.children)}
|
|
|
@@ -145,16 +132,14 @@ export default class SiderMenu extends PureComponent {
|
|
|
);
|
|
|
} else {
|
|
|
return (
|
|
|
- <Menu.Item key={item.path}>
|
|
|
- {this.getMenuItemPath(item)}
|
|
|
- </Menu.Item>
|
|
|
+ <Menu.Item key={item.path}>{this.getMenuItemPath(item)}</Menu.Item>
|
|
|
);
|
|
|
}
|
|
|
- }
|
|
|
+ };
|
|
|
/**
|
|
|
- * 获得菜单子节点
|
|
|
- * @memberof SiderMenu
|
|
|
- */
|
|
|
+ * 获得菜单子节点
|
|
|
+ * @memberof SiderMenu
|
|
|
+ */
|
|
|
getNavMenuItems = (menusData) => {
|
|
|
if (!menusData) {
|
|
|
return [];
|
|
|
@@ -162,49 +147,60 @@ export default class SiderMenu extends PureComponent {
|
|
|
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);
|
|
|
- }
|
|
|
+ .filter(item => item);
|
|
|
+ };
|
|
|
+ // Get the currently selected menu
|
|
|
+ getSelectedMenuKeys = () => {
|
|
|
+ const { location: { pathname } } = this.props;
|
|
|
+ return urlToList(pathname).map(itemPath =>
|
|
|
+ getMeunMatcheys(this.flatMenuKeys, itemPath).pop(),
|
|
|
+ );
|
|
|
+ };
|
|
|
// conversion Path
|
|
|
// 转化路径
|
|
|
- conversionPath=(path) => {
|
|
|
+ conversionPath = (path) => {
|
|
|
if (path && path.indexOf('http') === 0) {
|
|
|
return path;
|
|
|
} else {
|
|
|
return `/${path || ''}`.replace(/\/+/g, '/');
|
|
|
}
|
|
|
- }
|
|
|
+ };
|
|
|
// permission to check
|
|
|
checkPermissionItem = (authority, ItemDom) => {
|
|
|
if (this.props.Authorized && this.props.Authorized.check) {
|
|
|
const { check } = this.props.Authorized;
|
|
|
- return check(
|
|
|
- authority,
|
|
|
- ItemDom
|
|
|
- );
|
|
|
+ return check(authority, ItemDom);
|
|
|
}
|
|
|
return ItemDom;
|
|
|
+ };
|
|
|
+ isMainMenu = (key) => {
|
|
|
+ return this.menus.some(
|
|
|
+ item =>
|
|
|
+ key && (item.key === key || item.path === key),
|
|
|
+ );
|
|
|
}
|
|
|
handleOpenChange = (openKeys) => {
|
|
|
const lastOpenKey = openKeys[openKeys.length - 1];
|
|
|
- const isMainMenu = this.menus.some(
|
|
|
- item => lastOpenKey && (item.key === lastOpenKey || item.path === lastOpenKey)
|
|
|
- );
|
|
|
+ const moreThanOne = openKeys.filter(openKey => this.isMainMenu(openKey)).length > 1;
|
|
|
this.setState({
|
|
|
- openKeys: isMainMenu ? [lastOpenKey] : [...openKeys],
|
|
|
+ openKeys: moreThanOne ? [lastOpenKey] : [...openKeys],
|
|
|
});
|
|
|
- }
|
|
|
+ };
|
|
|
render() {
|
|
|
- const { logo, collapsed, location: { pathname }, onCollapse } = this.props;
|
|
|
+ const { logo, collapsed, onCollapse } = this.props;
|
|
|
const { openKeys } = this.state;
|
|
|
// Don't show popup menu when it is been collapsed
|
|
|
- const menuProps = collapsed ? {} : {
|
|
|
- openKeys,
|
|
|
- };
|
|
|
+ const menuProps = collapsed
|
|
|
+ ? {}
|
|
|
+ : {
|
|
|
+ openKeys,
|
|
|
+ };
|
|
|
// if pathname can't match, use the nearest parent's key
|
|
|
- let selectedKeys = this.getSelectedMenuKeys(pathname);
|
|
|
+ let selectedKeys = this.getSelectedMenuKeys();
|
|
|
if (!selectedKeys.length) {
|
|
|
selectedKeys = [openKeys[openKeys.length - 1]];
|
|
|
}
|