|
|
@@ -1,8 +1,8 @@
|
|
|
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 * as H from 'history';
|
|
|
import React, { Component } from 'react';
|
|
|
import Link from 'umi/link';
|
|
|
import { urlToList } from '../_utils/pathTools';
|
|
|
@@ -16,7 +16,7 @@ const { SubMenu } = Menu;
|
|
|
// icon: 'icon-geren' #For Iconfont ,
|
|
|
// icon: 'http://demo.com/icon.png',
|
|
|
// icon: <Icon type="setting" />,
|
|
|
-const getIcon = icon => {
|
|
|
+const getIcon = (icon?: string | React.ReactNode) => {
|
|
|
if (typeof icon === 'string') {
|
|
|
if (isUrl(icon)) {
|
|
|
return <Icon component={() => <img src={icon} alt="icon" className={styles.icon} />} />;
|
|
|
@@ -29,42 +29,55 @@ const getIcon = icon => {
|
|
|
return icon;
|
|
|
};
|
|
|
|
|
|
-export declare type CollapseType = 'clickTrigger' | 'responsive';
|
|
|
-export declare type SiderTheme = 'light' | 'dark';
|
|
|
-export declare type MenuMode =
|
|
|
- | 'vertical'
|
|
|
- | 'vertical-left'
|
|
|
- | 'vertical-right'
|
|
|
- | 'horizontal'
|
|
|
- | 'inline';
|
|
|
+/**
|
|
|
+ * @type R: is route
|
|
|
+ */
|
|
|
+export interface MenuDataItem<R extends boolean = false> {
|
|
|
+ authority?: string[] | string;
|
|
|
+ children?: MenuDataItem[];
|
|
|
+ hideChildrenInMenu?: boolean;
|
|
|
+ hideInMenu?: boolean;
|
|
|
+ icon?: string;
|
|
|
+ locale?: string;
|
|
|
+ name?: string;
|
|
|
+ path: string;
|
|
|
+ routes?: R extends true ? MenuDataItem<R>[] : never;
|
|
|
+ [key: string]: any;
|
|
|
+}
|
|
|
|
|
|
-interface BaseMenuProps {
|
|
|
+export interface BaseMenuProps {
|
|
|
+ className?: string;
|
|
|
+ collapsed?: boolean;
|
|
|
flatMenuKeys?: any[];
|
|
|
- location?: H.Location;
|
|
|
- onCollapse?: (collapsed: boolean, type?: CollapseType) => void;
|
|
|
+ handleOpenChange?: (openKeys: string[]) => void;
|
|
|
isMobile?: boolean;
|
|
|
- openKeys?: any;
|
|
|
- theme?: SiderTheme;
|
|
|
+ location?: Location;
|
|
|
+ menuData?: MenuDataItem[];
|
|
|
mode?: MenuMode;
|
|
|
- className?: string;
|
|
|
- collapsed?: boolean;
|
|
|
- handleOpenChange?: (openKeys: any[]) => void;
|
|
|
- menuData?: any[];
|
|
|
- style?: React.CSSProperties;
|
|
|
+ onCollapse?: (collapsed: boolean) => void;
|
|
|
onOpenChange?: (openKeys: string[]) => void;
|
|
|
+ openKeys?: string[];
|
|
|
+ style?: React.CSSProperties;
|
|
|
+ theme?: MenuTheme;
|
|
|
}
|
|
|
|
|
|
-interface BaseMenuState {}
|
|
|
+export default class BaseMenu extends Component<BaseMenuProps> {
|
|
|
+ static defaultProps: BaseMenuProps = {
|
|
|
+ flatMenuKeys: [],
|
|
|
+ location: window.location,
|
|
|
+ onCollapse: () => void 0,
|
|
|
+ isMobile: false,
|
|
|
+ openKeys: [],
|
|
|
+ collapsed: false,
|
|
|
+ handleOpenChange: () => void 0,
|
|
|
+ menuData: [],
|
|
|
+ onOpenChange: () => void 0,
|
|
|
+ };
|
|
|
|
|
|
-export default class BaseMenu extends Component<BaseMenuProps, BaseMenuState> {
|
|
|
/**
|
|
|
* 获得菜单子节点
|
|
|
- * @memberof SiderMenu
|
|
|
*/
|
|
|
- getNavMenuItems: (menusData: any[]) => any[] = menusData => {
|
|
|
- if (!menusData) {
|
|
|
- return [];
|
|
|
- }
|
|
|
+ getNavMenuItems = (menusData: MenuDataItem[] = []): React.ReactNode[] => {
|
|
|
return menusData
|
|
|
.filter(item => item.name && !item.hideInMenu)
|
|
|
.map(item => this.getSubMenuOrItem(item))
|
|
|
@@ -72,18 +85,22 @@ export default class BaseMenu extends Component<BaseMenuProps, BaseMenuState> {
|
|
|
};
|
|
|
|
|
|
// Get the currently selected menu
|
|
|
- getSelectedMenuKeys = pathname => {
|
|
|
+ getSelectedMenuKeys = (pathname: string): string[] => {
|
|
|
const { flatMenuKeys } = this.props;
|
|
|
- return urlToList(pathname).map(itemPath => getMenuMatches(flatMenuKeys, itemPath).pop());
|
|
|
+ return urlToList(pathname)
|
|
|
+ .map(itemPath => getMenuMatches(flatMenuKeys, itemPath).pop())
|
|
|
+ .filter(item => item) as string[];
|
|
|
};
|
|
|
|
|
|
/**
|
|
|
* get SubMenu or Item
|
|
|
*/
|
|
|
- getSubMenuOrItem = item => {
|
|
|
- // doc: add hideChildrenInMenu
|
|
|
- if (item.children && !item.hideChildrenInMenu && item.children.some(child => child.name)) {
|
|
|
- const { name } = item;
|
|
|
+ getSubMenuOrItem = (item: MenuDataItem): React.ReactNode => {
|
|
|
+ if (
|
|
|
+ Array.isArray(item.children) &&
|
|
|
+ !item.hideChildrenInMenu &&
|
|
|
+ item.children.some(child => (child.name ? true : false))
|
|
|
+ ) {
|
|
|
return (
|
|
|
<SubMenu
|
|
|
title={
|
|
|
@@ -93,7 +110,7 @@ export default class BaseMenu extends Component<BaseMenuProps, BaseMenuState> {
|
|
|
<span>{name}</span>
|
|
|
</span>
|
|
|
) : (
|
|
|
- name
|
|
|
+ item.name
|
|
|
)
|
|
|
}
|
|
|
key={item.path}
|
|
|
@@ -110,7 +127,7 @@ export default class BaseMenu extends Component<BaseMenuProps, BaseMenuState> {
|
|
|
* Judge whether it is http link.return a or Link
|
|
|
* @memberof SiderMenu
|
|
|
*/
|
|
|
- getMenuItemPath = item => {
|
|
|
+ getMenuItemPath = (item: MenuDataItem) => {
|
|
|
const { name } = item;
|
|
|
const itemPath = this.conversionPath(item.path);
|
|
|
const icon = getIcon(item.icon);
|
|
|
@@ -129,14 +146,8 @@ export default class BaseMenu extends Component<BaseMenuProps, BaseMenuState> {
|
|
|
<Link
|
|
|
to={itemPath}
|
|
|
target={target}
|
|
|
- replace={itemPath === location.pathname}
|
|
|
- onClick={
|
|
|
- isMobile
|
|
|
- ? () => {
|
|
|
- onCollapse(true);
|
|
|
- }
|
|
|
- : undefined
|
|
|
- }
|
|
|
+ replace={itemPath === location!.pathname}
|
|
|
+ onClick={isMobile ? () => onCollapse!(true) : void 0}
|
|
|
>
|
|
|
{icon}
|
|
|
<span>{name}</span>
|
|
|
@@ -144,7 +155,7 @@ export default class BaseMenu extends Component<BaseMenuProps, BaseMenuState> {
|
|
|
);
|
|
|
};
|
|
|
|
|
|
- conversionPath = path => {
|
|
|
+ conversionPath = (path: string) => {
|
|
|
if (path && path.indexOf('http') === 0) {
|
|
|
return path;
|
|
|
}
|
|
|
@@ -156,7 +167,7 @@ export default class BaseMenu extends Component<BaseMenuProps, BaseMenuState> {
|
|
|
openKeys,
|
|
|
theme,
|
|
|
mode,
|
|
|
- location: { pathname },
|
|
|
+ location,
|
|
|
className,
|
|
|
collapsed,
|
|
|
handleOpenChange,
|
|
|
@@ -164,7 +175,7 @@ export default class BaseMenu extends Component<BaseMenuProps, BaseMenuState> {
|
|
|
menuData,
|
|
|
} = this.props;
|
|
|
// if pathname can't match, use the nearest parent's key
|
|
|
- let selectedKeys = this.getSelectedMenuKeys(pathname);
|
|
|
+ let selectedKeys = this.getSelectedMenuKeys(location!.pathname);
|
|
|
if (!selectedKeys.length && openKeys) {
|
|
|
selectedKeys = [openKeys[openKeys.length - 1]];
|
|
|
}
|