import IconFont from '@/components/IconFont'; import { isUrl } from '@/utils/utils'; import { Icon, Menu } from 'antd'; import { MenuMode, MenuTheme } from 'antd/es/menu'; import classNames from 'classnames'; import React, { Component } from 'react'; import Link from 'umi/link'; import { urlToList } from '../_utils/pathTools'; import styles from './index.less'; import { getMenuMatches } from './SiderMenuUtils'; const { SubMenu } = Menu; // Allow menu.js config icon as string or ReactNode // icon: 'setting', // icon: 'icon-geren' #For Iconfont , // icon: 'http://demo.com/icon.png', // icon: , const getIcon = (icon?: string | React.ReactNode) => { if (typeof icon === 'string') { if (isUrl(icon)) { return icon} />; } if (icon.startsWith('icon-')) { return ; } return ; } return icon; }; /** * @type R: is route */ export interface MenuDataItem { authority?: string[] | string; children?: MenuDataItem[]; hideChildrenInMenu?: boolean; hideInMenu?: boolean; icon?: string; locale?: string; name?: string; path: string; routes?: R extends true ? MenuDataItem[] : never; [key: string]: any; } export interface BaseMenuProps { className?: string; collapsed?: boolean; flatMenuKeys?: any[]; handleOpenChange?: (openKeys: string[]) => void; isMobile?: boolean; location?: Location; menuData?: MenuDataItem[]; mode?: MenuMode; onCollapse?: (collapsed: boolean) => void; onOpenChange?: (openKeys: string[]) => void; openKeys?: string[]; style?: React.CSSProperties; theme?: MenuTheme; } export default class BaseMenu extends Component { static defaultProps: BaseMenuProps = { flatMenuKeys: [], location: window.location, onCollapse: () => void 0, isMobile: false, openKeys: [], collapsed: false, handleOpenChange: () => void 0, menuData: [], onOpenChange: () => void 0, }; /** * 获得菜单子节点 */ getNavMenuItems = (menusData: MenuDataItem[] = []): React.ReactNode[] => { return menusData .filter(item => item.name && !item.hideInMenu) .map(item => this.getSubMenuOrItem(item)) .filter(item => item); }; // Get the currently selected menu getSelectedMenuKeys = (pathname: string): string[] => { const { flatMenuKeys } = this.props; return urlToList(pathname) .map(itemPath => getMenuMatches(flatMenuKeys, itemPath).pop()) .filter(item => item) as string[]; }; /** * get SubMenu or Item */ getSubMenuOrItem = (item: MenuDataItem): React.ReactNode => { if ( Array.isArray(item.children) && !item.hideChildrenInMenu && item.children.some(child => (child.name ? true : false)) ) { return ( {getIcon(item.icon)} {name} ) : ( item.name ) } key={item.path} > {this.getNavMenuItems(item.children)} ); } return {this.getMenuItemPath(item)}; }; /** * 判断是否是http链接.返回 Link 或 a * Judge whether it is http link.return a or Link * @memberof SiderMenu */ getMenuItemPath = (item: MenuDataItem) => { const { name } = item; const itemPath = this.conversionPath(item.path); const icon = getIcon(item.icon); const { target } = item; // Is it a http link if (/^https?:\/\//.test(itemPath)) { return ( {icon} {name} ); } const { location, isMobile, onCollapse } = this.props; return ( onCollapse!(true) : void 0} > {icon} {name} ); }; conversionPath = (path: string) => { if (path && path.indexOf('http') === 0) { return path; } return `/${path || ''}`.replace(/\/+/g, '/'); }; render() { const { openKeys, theme, mode, location, className, collapsed, handleOpenChange, style, menuData, } = this.props; // if pathname can't match, use the nearest parent's key let selectedKeys = this.getSelectedMenuKeys(location!.pathname); if (!selectedKeys.length && openKeys) { selectedKeys = [openKeys[openKeys.length - 1]]; } let props = {}; if (openKeys && !collapsed) { props = { openKeys: openKeys.length === 0 ? [...selectedKeys] : openKeys, }; } const cls = classNames(className, { 'top-nav-menu': mode === 'horizontal', }); return ( {this.getNavMenuItems(menuData)} ); } }