BaseMenu.js 4.5 KB

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