BaseMenu.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. import React, { PureComponent } from 'react';
  2. import classNames from 'classnames';
  3. import { Menu, Icon } from 'antd';
  4. import Link from 'umi/link';
  5. import { urlToList } from '../_utils/pathTools';
  6. import { getMenuMatches } from './SiderMenuUtils';
  7. import { isUrl } from '@/utils/utils';
  8. import styles from './index.less';
  9. const { SubMenu } = Menu;
  10. // Allow menu.js config icon as string or ReactNode
  11. // icon: 'setting',
  12. // icon: 'http://demo.com/icon.png',
  13. // icon: <Icon type="setting" />,
  14. const getIcon = icon => {
  15. if (typeof icon === 'string' && isUrl(icon)) {
  16. return <img src={icon} alt="icon" className={styles.icon} />;
  17. }
  18. if (typeof icon === 'string') {
  19. return <Icon type={icon} />;
  20. }
  21. return icon;
  22. };
  23. export default class BaseMenu extends PureComponent {
  24. /**
  25. * 获得菜单子节点
  26. * @memberof SiderMenu
  27. */
  28. getNavMenuItems = (menusData, parent) => {
  29. if (!menusData) {
  30. return [];
  31. }
  32. return menusData
  33. .filter(item => item.name && !item.hideInMenu)
  34. .map(item => this.getSubMenuOrItem(item, parent))
  35. .filter(item => item);
  36. };
  37. // Get the currently selected menu
  38. getSelectedMenuKeys = pathname => {
  39. const { flatMenuKeys } = this.props;
  40. return urlToList(pathname).map(itemPath => getMenuMatches(flatMenuKeys, itemPath).pop());
  41. };
  42. /**
  43. * get SubMenu or Item
  44. */
  45. getSubMenuOrItem = item => {
  46. // doc: add hideChildrenInMenu
  47. if (item.children && !item.hideChildrenInMenu && item.children.some(child => child.name)) {
  48. const { name } = item;
  49. return (
  50. <SubMenu
  51. title={
  52. item.icon ? (
  53. <span>
  54. {getIcon(item.icon)}
  55. <span>{name}</span>
  56. </span>
  57. ) : (
  58. name
  59. )
  60. }
  61. key={item.path}
  62. >
  63. {this.getNavMenuItems(item.children)}
  64. </SubMenu>
  65. );
  66. }
  67. return <Menu.Item key={item.path}>{this.getMenuItemPath(item)}</Menu.Item>;
  68. };
  69. /**
  70. * 判断是否是http链接.返回 Link 或 a
  71. * Judge whether it is http link.return a or Link
  72. * @memberof SiderMenu
  73. */
  74. getMenuItemPath = item => {
  75. const { name } = item;
  76. const itemPath = this.conversionPath(item.path);
  77. const icon = getIcon(item.icon);
  78. const { target } = item;
  79. // Is it a http link
  80. if (/^https?:\/\//.test(itemPath)) {
  81. return (
  82. <a href={itemPath} target={target}>
  83. {icon}
  84. <span>{name}</span>
  85. </a>
  86. );
  87. }
  88. const { location, isMobile, onCollapse } = this.props;
  89. return (
  90. <Link
  91. to={itemPath}
  92. target={target}
  93. replace={itemPath === location.pathname}
  94. onClick={
  95. isMobile
  96. ? () => {
  97. onCollapse(true);
  98. }
  99. : undefined
  100. }
  101. >
  102. {icon}
  103. <span>{name}</span>
  104. </Link>
  105. );
  106. };
  107. conversionPath = path => {
  108. if (path && path.indexOf('http') === 0) {
  109. return path;
  110. }
  111. return `/${path || ''}`.replace(/\/+/g, '/');
  112. };
  113. render() {
  114. const {
  115. openKeys,
  116. theme,
  117. mode,
  118. location: { pathname },
  119. className,
  120. collapsed,
  121. } = this.props;
  122. // if pathname can't match, use the nearest parent's key
  123. let selectedKeys = this.getSelectedMenuKeys(pathname);
  124. if (!selectedKeys.length && openKeys) {
  125. selectedKeys = [openKeys[openKeys.length - 1]];
  126. }
  127. let props = {};
  128. if (openKeys && !collapsed) {
  129. props = {
  130. openKeys: openKeys.length === 0 ? [...selectedKeys] : openKeys,
  131. };
  132. }
  133. const { handleOpenChange, style, menuData } = this.props;
  134. const cls = classNames(className, {
  135. 'top-nav-menu': mode === 'horizontal',
  136. });
  137. return (
  138. <Menu
  139. key="Menu"
  140. mode={mode}
  141. theme={theme}
  142. onOpenChange={handleOpenChange}
  143. selectedKeys={selectedKeys}
  144. style={style}
  145. className={cls}
  146. {...props}
  147. >
  148. {this.getNavMenuItems(menuData)}
  149. </Menu>
  150. );
  151. }
  152. }