SliderMenu.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. import React, { PureComponent } from 'react';
  2. import { Layout } from 'antd';
  3. import pathToRegexp from 'path-to-regexp';
  4. import Link from 'umi/link';
  5. import styles from './index.less';
  6. import BaseMenu, { getMenuMatches } from './BaseMenu';
  7. import { urlToList } from '../_utils/pathTools';
  8. const { Sider } = Layout;
  9. /**
  10. * 获得菜单子节点
  11. * @memberof SiderMenu
  12. */
  13. const getDefaultCollapsedSubMenus = props => {
  14. const {
  15. location: { pathname },
  16. flatMenuKeys,
  17. } = props;
  18. return urlToList(pathname)
  19. .map(item => {
  20. return getMenuMatches(flatMenuKeys, item)[0];
  21. })
  22. .filter(item => item);
  23. };
  24. /**
  25. * Recursively flatten the data
  26. * [{path:string},{path:string}] => {path,path2}
  27. * @param menu
  28. */
  29. export const getFlatMenuKeys = menu =>
  30. menu.reduce((keys, item) => {
  31. keys.push(item.path);
  32. if (item.children) {
  33. return keys.concat(getFlatMenuKeys(item.children));
  34. }
  35. return keys;
  36. }, []);
  37. /**
  38. * Find all matched menu keys based on paths
  39. * @param flatMenuKeys: [/abc, /abc/:id, /abc/:id/info]
  40. * @param paths: [/abc, /abc/11, /abc/11/info]
  41. */
  42. export const getMenuMatchKeys = (flatMenuKeys, paths) =>
  43. paths.reduce(
  44. (matchKeys, path) =>
  45. matchKeys.concat(flatMenuKeys.filter(item => pathToRegexp(item).test(path))),
  46. []
  47. );
  48. export default class SiderMenu extends PureComponent {
  49. constructor(props) {
  50. super(props);
  51. this.flatMenuKeys = getFlatMenuKeys(props.menuData);
  52. this.state = {
  53. openKeys: getDefaultCollapsedSubMenus(props),
  54. };
  55. }
  56. static getDerivedStateFromProps(props, state) {
  57. const { pathname } = state;
  58. if (props.location.pathname !== pathname) {
  59. return {
  60. pathname: props.location.pathname,
  61. openKeys: getDefaultCollapsedSubMenus(props),
  62. };
  63. }
  64. return null;
  65. }
  66. isMainMenu = key => {
  67. const { menuData } = this.props;
  68. return menuData.some(item => {
  69. if (key) {
  70. return item.key === key || item.path === key;
  71. }
  72. return false;
  73. });
  74. };
  75. handleOpenChange = openKeys => {
  76. const moreThanOne = openKeys.filter(openKey => this.isMainMenu(openKey)).length > 1;
  77. this.setState({
  78. openKeys: moreThanOne ? [openKeys.pop()] : [...openKeys],
  79. });
  80. };
  81. render() {
  82. const { logo, collapsed, onCollapse, fixSiderbar, theme } = this.props;
  83. const { openKeys } = this.state;
  84. const defaultProps = collapsed ? {} : { openKeys };
  85. return (
  86. <Sider
  87. trigger={null}
  88. collapsible
  89. collapsed={collapsed}
  90. breakpoint="lg"
  91. onCollapse={onCollapse}
  92. width={256}
  93. className={`${styles.sider} ${fixSiderbar ? styles.fixSiderbar : ''} ${
  94. theme === 'light' ? styles.light : ''
  95. }`}
  96. >
  97. <div className={styles.logo} key="logo" id="logo">
  98. <Link to="/">
  99. <img src={logo} alt="logo" />
  100. <h1>Ant Design Pro</h1>
  101. </Link>
  102. </div>
  103. <BaseMenu
  104. {...this.props}
  105. key="Menu"
  106. mode="inline"
  107. handleOpenChange={this.handleOpenChange}
  108. onOpenChange={this.handleOpenChange}
  109. style={{ padding: '16px 0', width: '100%' }}
  110. {...defaultProps}
  111. />
  112. </Sider>
  113. );
  114. }
  115. }