SiderMenu.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. import React, { PureComponent } from 'react';
  2. import { Layout, Menu, Icon } from 'antd';
  3. import { Link } from 'dva/router';
  4. import logo from '../../assets/logo.svg';
  5. import styles from './index.less';
  6. import { getMenuData } from '../../common/menu';
  7. const { Sider } = Layout;
  8. const { SubMenu } = Menu;
  9. export default class SiderMenu extends PureComponent {
  10. constructor(props) {
  11. super(props);
  12. this.menus = getMenuData();
  13. this.state = {
  14. openKeys: this.getDefaultCollapsedSubMenus(props),
  15. };
  16. }
  17. getDefaultCollapsedSubMenus(props) {
  18. const { location: { pathname } } = props || this.props;
  19. const snippets = pathname.split('/').slice(1, -1);
  20. const currentPathSnippets = snippets.map((item, index) => {
  21. const arr = snippets.filter((_, i) => i <= index);
  22. return arr.join('/');
  23. });
  24. let currentMenuSelectedKeys = [];
  25. currentPathSnippets.forEach((item) => {
  26. currentMenuSelectedKeys = currentMenuSelectedKeys.concat(this.getSelectedMenuKeys(item));
  27. });
  28. if (currentMenuSelectedKeys.length === 0) {
  29. return ['dashboard'];
  30. }
  31. return currentMenuSelectedKeys;
  32. }
  33. getFlatMenuKeys(menus) {
  34. let keys = [];
  35. menus.forEach((item) => {
  36. if (item.children) {
  37. keys.push(item.path);
  38. keys = keys.concat(this.getFlatMenuKeys(item.children));
  39. } else {
  40. keys.push(item.path);
  41. }
  42. });
  43. return keys;
  44. }
  45. getSelectedMenuKeys = (path) => {
  46. const flatMenuKeys = this.getFlatMenuKeys(this.menus);
  47. if (flatMenuKeys.indexOf(path.replace(/^\//, '')) > -1) {
  48. return [path.replace(/^\//, '')];
  49. }
  50. if (flatMenuKeys.indexOf(path.replace(/^\//, '').replace(/\/$/, '')) > -1) {
  51. return [path.replace(/^\//, '').replace(/\/$/, '')];
  52. }
  53. return flatMenuKeys.filter((item) => {
  54. const itemRegExpStr = `^${item.replace(/:[\w-]+/g, '[\\w-]+')}$`;
  55. const itemRegExp = new RegExp(itemRegExpStr);
  56. return itemRegExp.test(path.replace(/^\//, ''));
  57. });
  58. }
  59. getNavMenuItems(menusData) {
  60. if (!menusData) {
  61. return [];
  62. }
  63. return menusData.map((item) => {
  64. if (!item.name) {
  65. return null;
  66. }
  67. let itemPath;
  68. if (item.path && item.path.indexOf('http') === 0) {
  69. itemPath = item.path;
  70. } else {
  71. itemPath = `/${item.path || ''}`.replace(/\/+/g, '/');
  72. }
  73. if (item.children && item.children.some(child => child.name)) {
  74. return item.hideInMenu ? null :
  75. (
  76. <SubMenu
  77. title={
  78. item.icon ? (
  79. <span>
  80. <Icon type={item.icon} />
  81. <span>{item.name}</span>
  82. </span>
  83. ) : item.name
  84. }
  85. key={item.key || item.path}
  86. >
  87. {this.getNavMenuItems(item.children)}
  88. </SubMenu>
  89. );
  90. }
  91. const icon = item.icon && <Icon type={item.icon} />;
  92. return item.hideInMenu ? null :
  93. (
  94. <Menu.Item key={item.key || item.path}>
  95. {
  96. /^https?:\/\//.test(itemPath) ? (
  97. <a href={itemPath} target={item.target}>
  98. {icon}<span>{item.name}</span>
  99. </a>
  100. ) : (
  101. <Link
  102. to={itemPath}
  103. target={item.target}
  104. replace={itemPath === this.props.location.pathname}
  105. onClick={this.props.isMobile ? () => { this.props.onCollapse(true); } : undefined}
  106. >
  107. {icon}<span>{item.name}</span>
  108. </Link>
  109. )
  110. }
  111. </Menu.Item>
  112. );
  113. });
  114. }
  115. handleOpenChange = (openKeys) => {
  116. const lastOpenKey = openKeys[openKeys.length - 1];
  117. const isMainMenu = this.menus.some(
  118. item => lastOpenKey && (item.key === lastOpenKey || item.path === lastOpenKey)
  119. );
  120. this.setState({
  121. openKeys: isMainMenu ? [lastOpenKey] : [...openKeys],
  122. });
  123. }
  124. render() {
  125. const { collapsed, location: { pathname }, onCollapse } = this.props;
  126. // Don't show popup menu when it is been collapsed
  127. const menuProps = collapsed ? {} : {
  128. openKeys: this.state.openKeys,
  129. };
  130. return (
  131. <Sider
  132. trigger={null}
  133. collapsible
  134. collapsed={collapsed}
  135. breakpoint="md"
  136. onCollapse={onCollapse}
  137. width={256}
  138. className={styles.sider}
  139. >
  140. <div className={styles.logo}>
  141. <Link to="/">
  142. <img src={logo} alt="logo" />
  143. <h1>Ant Design Pro</h1>
  144. </Link>
  145. </div>
  146. <Menu
  147. theme="dark"
  148. mode="inline"
  149. {...menuProps}
  150. onOpenChange={this.handleOpenChange}
  151. selectedKeys={this.getSelectedMenuKeys(pathname)}
  152. style={{ padding: '16px 0', width: '100%' }}
  153. >
  154. {this.getNavMenuItems(this.menus)}
  155. </Menu>
  156. </Sider>
  157. );
  158. }
  159. }