Jelajahi Sumber

add meun test and Modify part of the logic (#917)

陈帅 8 tahun lalu
induk
melakukan
3b8262599e

+ 1 - 1
src/common/menu.js

@@ -115,7 +115,7 @@ const menuData = [{
   }],
 }];
 
-function formatter(data, parentPath = '', parentAuthority) {
+function formatter(data, parentPath = '/', parentAuthority) {
   return data.map((item) => {
     let { path } = item;
     if (!isUrl(path)) {

+ 66 - 49
src/components/PageHeader/index.js

@@ -4,7 +4,7 @@ import pathToRegexp from 'path-to-regexp';
 import { Breadcrumb, Tabs } from 'antd';
 import classNames from 'classnames';
 import styles from './index.less';
-
+import { urlToList } from '../utils/pathTools';
 
 const { TabPane } = Tabs;
 export function getBreadcrumb(breadcrumbNameMap, url) {
@@ -19,14 +19,6 @@ export function getBreadcrumb(breadcrumbNameMap, url) {
   return breadcrumb || {};
 }
 
-// /userinfo/2144/id => ['/userinfo','/useinfo/2144,'/userindo/2144/id']
-export function urlToList(url) {
-  const urllist = url.split('/').filter(i => i);
-  return urllist.map((urlItem, index) => {
-    return `/${urllist.slice(0, index + 1).join('/')}`;
-  });
-}
-
 export default class PageHeader extends PureComponent {
   static contextTypes = {
     routes: PropTypes.array,
@@ -44,29 +36,35 @@ export default class PageHeader extends PureComponent {
       routes: this.props.routes || this.context.routes,
       params: this.props.params || this.context.params,
       routerLocation: this.props.location || this.context.location,
-      breadcrumbNameMap: this.props.breadcrumbNameMap || this.context.breadcrumbNameMap,
+      breadcrumbNameMap:
+        this.props.breadcrumbNameMap || this.context.breadcrumbNameMap,
     };
   };
   // Generated according to props
-  conversionFromProps= () => {
+  conversionFromProps = () => {
     const {
-      breadcrumbList, breadcrumbSeparator, linkElement = 'a',
+      breadcrumbList,
+      breadcrumbSeparator,
+      linkElement = 'a',
     } = this.props;
     return (
-      <Breadcrumb
-        className={styles.breadcrumb}
-        separator={breadcrumbSeparator}
-      >
+      <Breadcrumb className={styles.breadcrumb} separator={breadcrumbSeparator}>
         {breadcrumbList.map(item => (
           <Breadcrumb.Item key={item.title}>
-            {item.href ? (createElement(linkElement, {
-          [linkElement === 'a' ? 'href' : 'to']: item.href,
-        }, item.title)) : item.title}
+            {item.href
+              ? createElement(
+                  linkElement,
+                  {
+                    [linkElement === 'a' ? 'href' : 'to']: item.href,
+                  },
+                  item.title,
+                )
+              : item.title}
           </Breadcrumb.Item>
-      ))}
+        ))}
       </Breadcrumb>
     );
-  }
+  };
   conversionFromLocation = (routerLocation, breadcrumbNameMap) => {
     const { breadcrumbSeparator, linkElement = 'a' } = this.props;
     // Convert the url to an array
@@ -74,7 +72,8 @@ export default class PageHeader extends PureComponent {
     // Loop data mosaic routing
     const extraBreadcrumbItems = pathSnippets.map((url, index) => {
       const currentBreadcrumb = getBreadcrumb(breadcrumbNameMap, url);
-      const isLinkable = (index !== pathSnippets.length - 1) && currentBreadcrumb.component;
+      const isLinkable =
+        index !== pathSnippets.length - 1 && currentBreadcrumb.component;
       return currentBreadcrumb.name && !currentBreadcrumb.hideInBreadcrumb ? (
         <Breadcrumb.Item key={url}>
           {createElement(
@@ -88,26 +87,33 @@ export default class PageHeader extends PureComponent {
     // Add home breadcrumbs to your head
     extraBreadcrumbItems.unshift(
       <Breadcrumb.Item key="home">
-        {createElement(linkElement, {
-        [linkElement === 'a' ? 'href' : 'to']: '/' }, '首页')}
-      </Breadcrumb.Item>
+        {createElement(
+          linkElement,
+          {
+            [linkElement === 'a' ? 'href' : 'to']: '/',
+          },
+          '首页',
+        )}
+      </Breadcrumb.Item>,
     );
     return (
-      <Breadcrumb
-        className={styles.breadcrumb}
-        separator={breadcrumbSeparator}
-      >
+      <Breadcrumb className={styles.breadcrumb} separator={breadcrumbSeparator}>
         {extraBreadcrumbItems}
       </Breadcrumb>
     );
-  }
+  };
   /**
    * 将参数转化为面包屑
    * Convert parameters into breadcrumbs
    */
   conversionBreadcrumbList = () => {
     const { breadcrumbList, breadcrumbSeparator } = this.props;
-    const { routes, params, routerLocation, breadcrumbNameMap } = this.getBreadcrumbProps();
+    const {
+      routes,
+      params,
+      routerLocation,
+      breadcrumbNameMap,
+    } = this.getBreadcrumbProps();
     if (breadcrumbList && breadcrumbList.length) {
       return this.conversionFromProps();
     }
@@ -130,24 +136,37 @@ export default class PageHeader extends PureComponent {
       return this.conversionFromLocation(routerLocation, breadcrumbNameMap);
     }
     return null;
-  }
+  };
   // 渲染Breadcrumb 子节点
   // Render the Breadcrumb child node
   itemRender = (route, params, routes, paths) => {
     const { linkElement = 'a' } = this.props;
     const last = routes.indexOf(route) === routes.length - 1;
-    return (last || !route.component)
-      ? <span>{route.breadcrumbName}</span>
-      : createElement(linkElement, {
-        href: paths.join('/') || '/',
-        to: paths.join('/') || '/',
-      }, route.breadcrumbName);
-  }
+    return last || !route.component ? (
+      <span>{route.breadcrumbName}</span>
+    ) : (
+      createElement(
+        linkElement,
+        {
+          href: paths.join('/') || '/',
+          to: paths.join('/') || '/',
+        },
+        route.breadcrumbName,
+      )
+    );
+  };
 
   render() {
     const {
-      title, logo, action, content, extraContent,
-      tabList, className, tabActiveKey, tabBarExtraContent,
+      title,
+      logo,
+      action,
+      content,
+      extraContent,
+      tabList,
+      className,
+      tabActiveKey,
+      tabBarExtraContent,
     } = this.props;
     const clsString = classNames(styles.pageHeader, className);
 
@@ -175,12 +194,13 @@ export default class PageHeader extends PureComponent {
             </div>
             <div className={styles.row}>
               {content && <div className={styles.content}>{content}</div>}
-              {extraContent && <div className={styles.extraContent}>{extraContent}</div>}
+              {extraContent && (
+                <div className={styles.extraContent}>{extraContent}</div>
+              )}
             </div>
           </div>
         </div>
-        {
-          tabList &&
+        {tabList &&
           tabList.length && (
             <Tabs
               className={styles.tabs}
@@ -188,12 +208,9 @@ export default class PageHeader extends PureComponent {
               onChange={this.onChange}
               tabBarExtraContent={tabBarExtraContent}
             >
-              {
-                tabList.map(item => <TabPane tab={item.tab} key={item.key} />)
-              }
+              {tabList.map(item => <TabPane tab={item.tab} key={item.key} />)}
             </Tabs>
-          )
-        }
+          )}
       </div>
     );
   }

+ 5 - 23
src/components/PageHeader/index.test.js

@@ -1,23 +1,5 @@
-import { getBreadcrumb, urlToList } from './index';
-
-describe('test urlToList', () => {
-  it('A path', () => {
-    expect(urlToList('/userinfo')).toEqual(['/userinfo']);
-  });
-  it('Secondary path', () => {
-    expect(urlToList('/userinfo/2144')).toEqual([
-      '/userinfo',
-      '/userinfo/2144',
-    ]);
-  });
-  it('Three paths', () => {
-    expect(urlToList('/userinfo/2144/addr')).toEqual([
-      '/userinfo',
-      '/userinfo/2144',
-      '/userinfo/2144/addr',
-    ]);
-  });
-});
+import { getBreadcrumb } from './index';
+import { urlToList } from '../utils/pathTools';
 
 const routerData = {
   '/dashboard/analysis': {
@@ -36,17 +18,17 @@ const routerData = {
 describe('test getBreadcrumb', () => {
   it('Simple url', () => {
     expect(getBreadcrumb(routerData, '/dashboard/analysis').name).toEqual(
-      '分析页'
+      '分析页',
     );
   });
   it('Parameters url', () => {
     expect(getBreadcrumb(routerData, '/userinfo/2144').name).toEqual(
-      '用户信息'
+      '用户信息',
     );
   });
   it('The middle parameter url', () => {
     expect(getBreadcrumb(routerData, '/userinfo/2144/addr').name).toEqual(
-      '收货订单'
+      '收货订单',
     );
   });
   it('Loop through the parameters', () => {

+ 63 - 70
src/components/SiderMenu/SiderMenu.js

@@ -3,6 +3,7 @@ import { Layout, Menu, Icon } from 'antd';
 import pathToRegexp from 'path-to-regexp';
 import { Link } from 'dva/router';
 import styles from './index.less';
+import { urlToList } from '../utils/pathTools';
 
 const { Sider } = Layout;
 const { SubMenu } = Menu;
@@ -21,10 +22,17 @@ const getIcon = (icon) => {
   return icon;
 };
 
+export const getMeunMatcheys = (flatMenuKeys, path) => {
+  return flatMenuKeys.filter((item) => {
+    return pathToRegexp(item).test(path);
+  });
+};
+
 export default class SiderMenu extends PureComponent {
   constructor(props) {
     super(props);
     this.menus = props.menuData;
+    this.flatMenuKeys = this.getFlatMenuKeys(props.menuData);
     this.state = {
       openKeys: this.getDefaultCollapsedSubMenus(props),
     };
@@ -43,30 +51,11 @@ export default class SiderMenu extends PureComponent {
    */
   getDefaultCollapsedSubMenus(props) {
     const { location: { pathname } } = props || this.props;
-    // eg. /list/search/articles = > ['','list','search','articles']
-    let snippets = pathname.split('/');
-    // Delete the end
-    // eg.  delete 'articles'
-    snippets.pop();
-    // Delete the head
-    // eg. delete ''
-    snippets.shift();
-    // eg. After the operation is completed, the array should be ['list','search']
-    // eg. Forward the array as ['list','list/search']
-    snippets = snippets.map((item, index) => {
-      // If the array length > 1
-      if (index > 0) {
-        // eg. search => ['list','search'].join('/')
-        return snippets.slice(0, index + 1).join('/');
-      }
-      // index 0 to not do anything
-      return item;
-    });
-    snippets = snippets.map((item) => {
-      return this.getSelectedMenuKeys(`/${item}`)[0];
-    });
-    // eg. ['list','list/search']
-    return snippets;
+    return urlToList(pathname)
+      .map((item) => {
+        return getMeunMatcheys(this.flatMenuKeys, item)[0];
+      })
+      .filter(item => item);
   }
   /**
    * Recursively flatten the data
@@ -77,29 +66,17 @@ export default class SiderMenu extends PureComponent {
     let keys = [];
     menus.forEach((item) => {
       if (item.children) {
-        keys.push(item.path);
         keys = keys.concat(this.getFlatMenuKeys(item.children));
-      } else {
-        keys.push(item.path);
       }
+      keys.push(item.path);
     });
     return keys;
   }
   /**
-   * Get selected child nodes
-   * /user/chen => ['user','/user/:id']
+   * 判断是否是http链接.返回 Link 或 a
+   * Judge whether it is http link.return a or Link
+   * @memberof SiderMenu
    */
-  getSelectedMenuKeys = (path) => {
-    const flatMenuKeys = this.getFlatMenuKeys(this.menus);
-    return flatMenuKeys.filter((item) => {
-      return pathToRegexp(`/${item}(.*)`).test(path);
-    });
-  }
-  /**
-  * 判断是否是http链接.返回 Link 或 a
-  * Judge whether it is http link.return a or Link
-  * @memberof SiderMenu
-  */
   getMenuItemPath = (item) => {
     const itemPath = this.conversionPath(item.path);
     const icon = getIcon(item.icon);
@@ -108,7 +85,8 @@ export default class SiderMenu extends PureComponent {
     if (/^https?:\/\//.test(itemPath)) {
       return (
         <a href={itemPath} target={target}>
-          {icon}<span>{name}</span>
+          {icon}
+          <span>{name}</span>
         </a>
       );
     }
@@ -117,16 +95,23 @@ export default class SiderMenu extends PureComponent {
         to={itemPath}
         target={target}
         replace={itemPath === this.props.location.pathname}
-        onClick={this.props.isMobile ? () => { this.props.onCollapse(true); } : undefined}
+        onClick={
+          this.props.isMobile
+            ? () => {
+                this.props.onCollapse(true);
+              }
+            : undefined
+        }
       >
-        {icon}<span>{name}</span>
+        {icon}
+        <span>{name}</span>
       </Link>
     );
-  }
+  };
   /**
    * get SubMenu or Item
    */
-  getSubMenuOrItem=(item) => {
+  getSubMenuOrItem = (item) => {
     if (item.children && item.children.some(child => child.name)) {
       return (
         <SubMenu
@@ -136,8 +121,10 @@ export default class SiderMenu extends PureComponent {
                 {getIcon(item.icon)}
                 <span>{item.name}</span>
               </span>
-            ) : item.name
-            }
+            ) : (
+              item.name
+            )
+          }
           key={item.path}
         >
           {this.getNavMenuItems(item.children)}
@@ -145,16 +132,14 @@ export default class SiderMenu extends PureComponent {
       );
     } else {
       return (
-        <Menu.Item key={item.path}>
-          {this.getMenuItemPath(item)}
-        </Menu.Item>
+        <Menu.Item key={item.path}>{this.getMenuItemPath(item)}</Menu.Item>
       );
     }
-  }
+  };
   /**
-  * 获得菜单子节点
-  * @memberof SiderMenu
-  */
+   * 获得菜单子节点
+   * @memberof SiderMenu
+   */
   getNavMenuItems = (menusData) => {
     if (!menusData) {
       return [];
@@ -162,49 +147,57 @@ export default class SiderMenu extends PureComponent {
     return menusData
       .filter(item => item.name && !item.hideInMenu)
       .map((item) => {
+        // make dom
         const ItemDom = this.getSubMenuOrItem(item);
         return this.checkPermissionItem(item.authority, ItemDom);
       })
-      .filter(item => !!item);
-  }
+      .filter(item => item);
+  };
+  // Get the currently selected menu
+  getSelectedMenuKeys = () => {
+    const { location: { pathname } } = this.props;
+    return urlToList(pathname).map(itemPath =>
+      getMeunMatcheys(this.flatMenuKeys, itemPath).pop(),
+    );
+  };
   // conversion Path
   // 转化路径
-  conversionPath=(path) => {
+  conversionPath = (path) => {
     if (path && path.indexOf('http') === 0) {
       return path;
     } else {
       return `/${path || ''}`.replace(/\/+/g, '/');
     }
-  }
+  };
   // permission to check
   checkPermissionItem = (authority, ItemDom) => {
     if (this.props.Authorized && this.props.Authorized.check) {
       const { check } = this.props.Authorized;
-      return check(
-        authority,
-        ItemDom
-      );
+      return check(authority, ItemDom);
     }
     return ItemDom;
-  }
+  };
   handleOpenChange = (openKeys) => {
     const lastOpenKey = openKeys[openKeys.length - 1];
     const isMainMenu = this.menus.some(
-      item => lastOpenKey && (item.key === lastOpenKey || item.path === lastOpenKey)
+      item =>
+        lastOpenKey && (item.key === lastOpenKey || item.path === lastOpenKey),
     );
     this.setState({
       openKeys: isMainMenu ? [lastOpenKey] : [...openKeys],
     });
-  }
+  };
   render() {
-    const { logo, collapsed, location: { pathname }, onCollapse } = this.props;
+    const { logo, collapsed, onCollapse } = this.props;
     const { openKeys } = this.state;
     // Don't show popup menu when it is been collapsed
-    const menuProps = collapsed ? {} : {
-      openKeys,
-    };
+    const menuProps = collapsed
+      ? {}
+      : {
+        openKeys,
+      };
     // if pathname can't match, use the nearest parent's key
-    let selectedKeys = this.getSelectedMenuKeys(pathname);
+    let selectedKeys = this.getSelectedMenuKeys();
     if (!selectedKeys.length) {
       selectedKeys = [openKeys[openKeys.length - 1]];
     }

+ 36 - 0
src/components/SiderMenu/SilderMenu.test.js

@@ -0,0 +1,36 @@
+import { getMeunMatcheys } from './SiderMenu';
+
+const meun = [
+  '/dashboard',
+  '/userinfo',
+  '/dashboard/name',
+  '/userinfo/:id',
+  '/userinfo/:id/info',
+];
+
+describe('test meun match', () => {
+  it('simple path', () => {
+    expect(getMeunMatcheys(meun, '/dashboard')).toEqual(['/dashboard']);
+  });
+  it('error path', () => {
+    expect(getMeunMatcheys(meun, '/dashboardname')).toEqual([]);
+  });
+
+  it('Secondary path', () => {
+    expect(getMeunMatcheys(meun, '/dashboard/name')).toEqual([
+      '/dashboard/name',
+    ]);
+  });
+
+  it('Parameter path', () => {
+    expect(getMeunMatcheys(meun, '/userinfo/2144')).toEqual([
+      '/userinfo/:id',
+    ]);
+  });
+
+  it('three parameter path', () => {
+    expect(getMeunMatcheys(meun, '/userinfo/2144/info')).toEqual([
+      '/userinfo/:id/info',
+    ]);
+  });
+});

+ 7 - 0
src/components/utils/pathTools.js

@@ -0,0 +1,7 @@
+// /userinfo/2144/id => ['/userinfo','/useinfo/2144,'/userindo/2144/id']
+export function urlToList(url) {
+  const urllist = url.split('/').filter(i => i);
+  return urllist.map((urlItem, index) => {
+    return `/${urllist.slice(0, index + 1).join('/')}`;
+  });
+}

+ 20 - 0
src/components/utils/pathTools.test.js

@@ -0,0 +1,20 @@
+import { urlToList } from './pathTools';
+
+describe('test urlToList', () => {
+  it('A path', () => {
+    expect(urlToList('/userinfo')).toEqual(['/userinfo']);
+  });
+  it('Secondary path', () => {
+    expect(urlToList('/userinfo/2144')).toEqual([
+      '/userinfo',
+      '/userinfo/2144',
+    ]);
+  });
+  it('Three paths', () => {
+    expect(urlToList('/userinfo/2144/addr')).toEqual([
+      '/userinfo',
+      '/userinfo/2144',
+      '/userinfo/2144/addr',
+    ]);
+  });
+});