Procházet zdrojové kódy

Router config (#442)

* adjust menu and router config

* rebase master

* remove invalid breadcrumb item

* add nested layout

* Some improvements

* get name from menu.js

* - use HOC to pass routerData instead of context
ddcat1115 před 8 roky
rodič
revize
577ded4b34

+ 1 - 1
.eslintrc

@@ -53,6 +53,6 @@
     }
   },
   "settings": {
-    "polyfills": ["fetch"]
+    "polyfills": ["fetch", "promises"]
   }
 }

+ 134 - 0
src/common/menu.js

@@ -0,0 +1,134 @@
+const menuData = [{
+  name: 'dashborad',
+  icon: 'dashboard',
+  path: 'dashboard',
+  children: [{
+    name: '分析页',
+    path: 'analysis',
+  }, {
+    name: '监控页',
+    path: 'monitor',
+  }, {
+    name: '工作台',
+    path: 'workplace',
+    // hideInMenu: true,
+  }],
+}, {
+  name: '表单页',
+  icon: 'form',
+  path: 'form',
+  children: [{
+    name: '基础表单',
+    path: 'basic-form',
+  }, {
+    name: '分步表单',
+    path: 'step-form',
+  }, {
+    name: '高级表单',
+    path: 'advanced-form',
+  }],
+}, {
+  name: '列表页',
+  icon: 'table',
+  path: 'list',
+  children: [{
+    name: '搜索列表',
+    icon: 'search',
+    path: 'search',
+    children: [{
+      name: '搜索列表(项目)',
+      path: 'projects',
+    }, {
+      name: '搜索列表(应用)',
+      path: 'applications',
+    }, {
+      name: '搜索列表(文章)',
+      path: 'articles',
+    }],
+  }, {
+    name: '查询表格',
+    path: 'table-list',
+  }, {
+    name: '标准列表',
+    path: 'basic-list',
+  }, {
+    name: '卡片列表',
+    path: 'card-list',
+  }],
+}, {
+  name: '详情页',
+  icon: 'profile',
+  path: 'profile',
+  children: [{
+    name: '基础详情页',
+    path: 'basic',
+  }, {
+    name: '高级详情页',
+    path: 'advanced',
+  }],
+}, {
+  name: '结果页',
+  icon: 'check-circle-o',
+  path: 'result',
+  children: [{
+    name: '成功',
+    path: 'success',
+  }, {
+    name: '失败',
+    path: 'fail',
+  }],
+}, {
+  name: '异常页',
+  icon: 'warning',
+  path: 'exception',
+  children: [{
+    name: '403',
+    path: '403',
+  }, {
+    name: '404',
+    path: '404',
+  }, {
+    name: '500',
+    path: '500',
+  }],
+}, {
+  name: '账户',
+  icon: 'user',
+  path: 'user',
+  children: [{
+    name: '登录',
+    path: 'login',
+  }, {
+    name: '注册',
+    path: 'register',
+  }, {
+    name: '注册结果',
+    path: 'register-result',
+  }],
+}, {
+  name: '使用文档',
+  icon: 'book',
+  path: 'http://pro.ant.design/docs/getting-started',
+  target: '_blank',
+}];
+
+function formatter(data, parentPath = '') {
+  const list = [];
+  data.forEach((item) => {
+    if (item.children) {
+      list.push({
+        ...item,
+        path: `${parentPath}${item.path}`,
+        children: formatter(item.children, `${parentPath}${item.path}/`),
+      });
+    } else {
+      list.push({
+        ...item,
+        path: `${parentPath}${item.path}`,
+      });
+    }
+  });
+  return list;
+}
+
+export const getMenuData = () => formatter(menuData);

+ 0 - 209
src/common/nav.js

@@ -1,209 +0,0 @@
-import dynamic from 'dva/dynamic';
-
-// wrapper of dynamic
-const dynamicWrapper = (app, models, component) => dynamic({
-  app,
-  models: () => models.map(m => import(`../models/${m}.js`)),
-  component,
-});
-
-// nav data
-export const getNavData = app => [
-  {
-    component: dynamicWrapper(app, ['user', 'login'], () => import('../layouts/BasicLayout')),
-    layout: 'BasicLayout',
-    name: '首页', // for breadcrumb
-    path: '/',
-    children: [
-      {
-        name: 'Dashboard',
-        icon: 'dashboard',
-        path: 'dashboard',
-        children: [
-          {
-            name: '分析页',
-            path: 'analysis',
-            component: dynamicWrapper(app, ['chart'], () => import('../routes/Dashboard/Analysis')),
-          },
-          {
-            name: '监控页',
-            path: 'monitor',
-            component: dynamicWrapper(app, ['monitor'], () => import('../routes/Dashboard/Monitor')),
-          },
-          {
-            name: '工作台',
-            path: 'workplace',
-            component: dynamicWrapper(app, ['project', 'activities', 'chart'], () => import('../routes/Dashboard/Workplace')),
-          },
-        ],
-      },
-      {
-        name: '表单页',
-        path: 'form',
-        icon: 'form',
-        children: [
-          {
-            name: '基础表单',
-            path: 'basic-form',
-            component: dynamicWrapper(app, ['form'], () => import('../routes/Forms/BasicForm')),
-          },
-          {
-            name: '分步表单',
-            path: 'step-form',
-            component: dynamicWrapper(app, ['form'], () => import('../routes/Forms/StepForm')),
-            children: [
-              {
-                path: 'confirm',
-                component: dynamicWrapper(app, ['form'], () => import('../routes/Forms/StepForm/Step2')),
-              },
-              {
-                path: 'result',
-                component: dynamicWrapper(app, ['form'], () => import('../routes/Forms/StepForm/Step3')),
-              },
-            ],
-          },
-          {
-            name: '高级表单',
-            path: 'advanced-form',
-            component: dynamicWrapper(app, ['form'], () => import('../routes/Forms/AdvancedForm')),
-          },
-        ],
-      },
-      {
-        name: '列表页',
-        path: 'list',
-        icon: 'table',
-        children: [
-          {
-            name: '查询表格',
-            path: 'table-list',
-            component: dynamicWrapper(app, ['rule'], () => import('../routes/List/TableList')),
-          },
-          {
-            name: '标准列表',
-            path: 'basic-list',
-            component: dynamicWrapper(app, ['list'], () => import('../routes/List/BasicList')),
-          },
-          {
-            name: '卡片列表',
-            path: 'card-list',
-            component: dynamicWrapper(app, ['list'], () => import('../routes/List/CardList')),
-          },
-          {
-            name: '搜索列表',
-            path: 'search',
-            component: dynamicWrapper(app, [], () => import('../routes/List/List')),
-            children: [{
-              name: '搜索列表(项目)',
-              path: 'projects',
-              component: dynamicWrapper(app, ['list'], () => import('../routes/List/Projects')),
-            }, {
-              name: '搜索列表(应用)',
-              path: 'applications',
-              component: dynamicWrapper(app, ['list'], () => import('../routes/List/Applications')),
-            }, {
-              name: '搜索列表(文章)',
-              path: 'articles',
-              component: dynamicWrapper(app, ['list'], () => import('../routes/List/Articles')),
-            }],
-          },
-        ],
-      },
-      {
-        name: '详情页',
-        path: 'profile',
-        icon: 'profile',
-        children: [
-          {
-            name: '基础详情页',
-            path: 'basic',
-            component: dynamicWrapper(app, ['profile'], () => import('../routes/Profile/BasicProfile')),
-          },
-          {
-            name: '高级详情页',
-            path: 'advanced',
-            component: dynamicWrapper(app, ['profile'], () => import('../routes/Profile/AdvancedProfile')),
-          },
-        ],
-      },
-      {
-        name: '结果',
-        path: 'result',
-        icon: 'check-circle-o',
-        children: [
-          {
-            name: '成功',
-            path: 'success',
-            component: dynamicWrapper(app, [], () => import('../routes/Result/Success')),
-          },
-          {
-            name: '失败',
-            path: 'fail',
-            component: dynamicWrapper(app, [], () => import('../routes/Result/Error')),
-          },
-        ],
-      },
-      {
-        name: '异常',
-        path: 'exception',
-        icon: 'warning',
-        children: [
-          {
-            name: '403',
-            path: '403',
-            component: dynamicWrapper(app, [], () => import('../routes/Exception/403')),
-          },
-          {
-            name: '404',
-            path: '404',
-            component: dynamicWrapper(app, [], () => import('../routes/Exception/404')),
-          },
-          {
-            name: '500',
-            path: '500',
-            component: dynamicWrapper(app, [], () => import('../routes/Exception/500')),
-          },
-        ],
-      },
-    ],
-  },
-  {
-    component: dynamicWrapper(app, [], () => import('../layouts/UserLayout')),
-    path: '/user',
-    layout: 'UserLayout',
-    children: [
-      {
-        name: '帐户',
-        icon: 'user',
-        path: 'user',
-        children: [
-          {
-            name: '登录',
-            path: 'login',
-            component: dynamicWrapper(app, ['login'], () => import('../routes/User/Login')),
-          },
-          {
-            name: '注册',
-            path: 'register',
-            component: dynamicWrapper(app, ['register'], () => import('../routes/User/Register')),
-          },
-          {
-            name: '注册结果',
-            path: 'register-result',
-            component: dynamicWrapper(app, [], () => import('../routes/User/RegisterResult')),
-          },
-        ],
-      },
-    ],
-  },
-  {
-    component: dynamicWrapper(app, [], () => import('../layouts/BlankLayout')),
-    layout: 'BlankLayout',
-    children: {
-      name: '使用文档',
-      path: 'http://pro.ant.design/docs/getting-started',
-      target: '_blank',
-      icon: 'book',
-    },
-  },
-];

+ 132 - 0
src/common/router.js

@@ -0,0 +1,132 @@
+import React from 'react';
+import dynamic from 'dva/dynamic';
+import { getMenuData } from './menu';
+
+// wrapper of dynamic
+const dynamicWrapper = (app, models, component) => dynamic({
+  app,
+  models: () => models.map(m => import(`../models/${m}.js`)),
+  // add routerData prop
+  component: () => {
+    const p = component();
+    return new Promise((resolve, reject) => {
+      p.then((Comp) => {
+        resolve(props => <Comp {...props} routerData={getRouterData(app)} />);
+      }).catch(err => reject(err));
+    });
+  },
+});
+
+function getFlatMenuData(menus) {
+  let keys = {};
+  menus.forEach((item) => {
+    if (item.children) {
+      keys[item.path] = item.name;
+      keys = { ...keys, ...getFlatMenuData(item.children) };
+    } else {
+      keys[item.path] = item.name;
+    }
+  });
+  return keys;
+}
+
+export const getRouterData = (app) => {
+  const routerData = {
+    '/': {
+      component: dynamicWrapper(app, ['user', 'login'], () => import('../layouts/BasicLayout')),
+    },
+    '/dashboard/analysis': {
+      component: dynamicWrapper(app, ['chart'], () => import('../routes/Dashboard/Analysis')),
+    },
+    '/dashboard/monitor': {
+      component: dynamicWrapper(app, ['monitor'], () => import('../routes/Dashboard/Monitor')),
+    },
+    '/dashboard/workplace': {
+      component: dynamicWrapper(app, ['project', 'activities', 'chart'], () => import('../routes/Dashboard/Workplace')),
+      // hideInBreadcrumb: true,
+      // name: '工作台',
+    },
+    '/form/basic-form': {
+      component: dynamicWrapper(app, ['form'], () => import('../routes/Forms/BasicForm')),
+    },
+    '/form/step-form': {
+      component: dynamicWrapper(app, ['form'], () => import('../routes/Forms/StepForm')),
+    },
+    '/form/step-form/confirm': {
+      component: dynamicWrapper(app, ['form'], () => import('../routes/Forms/StepForm/Step2')),
+    },
+    '/form/step-form/result': {
+      component: dynamicWrapper(app, ['form'], () => import('../routes/Forms/StepForm/Step3')),
+    },
+    '/form/advanced-form': {
+      component: dynamicWrapper(app, ['form'], () => import('../routes/Forms/AdvancedForm')),
+    },
+    '/list/table-list': {
+      component: dynamicWrapper(app, ['rule'], () => import('../routes/List/TableList')),
+    },
+    '/list/basic-list': {
+      component: dynamicWrapper(app, ['list'], () => import('../routes/List/BasicList')),
+    },
+    '/list/card-list': {
+      component: dynamicWrapper(app, ['list'], () => import('../routes/List/CardList')),
+    },
+    '/list/search': {
+      component: dynamicWrapper(app, ['list'], () => import('../routes/List/List')),
+    },
+    '/list/search/projects': {
+      component: dynamicWrapper(app, ['list'], () => import('../routes/List/Projects')),
+    },
+    '/list/search/applications': {
+      component: dynamicWrapper(app, ['list'], () => import('../routes/List/Applications')),
+    },
+    '/list/search/articles': {
+      component: dynamicWrapper(app, ['list'], () => import('../routes/List/Articles')),
+    },
+    '/profile/basic': {
+      component: dynamicWrapper(app, ['profile'], () => import('../routes/Profile/BasicProfile')),
+    },
+    '/profile/advanced': {
+      component: dynamicWrapper(app, ['profile'], () => import('../routes/Profile/AdvancedProfile')),
+    },
+    '/result/success': {
+      component: dynamicWrapper(app, [], () => import('../routes/Result/Success')),
+    },
+    '/result/fail': {
+      component: dynamicWrapper(app, [], () => import('../routes/Result/Error')),
+    },
+    '/exception/403': {
+      component: dynamicWrapper(app, [], () => import('../routes/Exception/403')),
+    },
+    '/exception/404': {
+      component: dynamicWrapper(app, [], () => import('../routes/Exception/404')),
+    },
+    '/exception/500': {
+      component: dynamicWrapper(app, [], () => import('../routes/Exception/500')),
+    },
+    '/user': {
+      component: dynamicWrapper(app, [], () => import('../layouts/UserLayout')),
+    },
+    '/user/login': {
+      component: dynamicWrapper(app, ['login'], () => import('../routes/User/Login')),
+    },
+    '/user/register': {
+      component: dynamicWrapper(app, ['register'], () => import('../routes/User/Register')),
+    },
+    '/user/register-result': {
+      component: dynamicWrapper(app, [], () => import('../routes/User/RegisterResult')),
+    },
+    // '/user/:id': {
+    //   component: dynamicWrapper(app, [], () => import('../routes/User/SomeComponent')),
+    // },
+  };
+  // Get name from ./menu.js or just set it in the router data.
+  const menuData = getFlatMenuData(getMenuData());
+  const routerDataWithName = {};
+  Object.keys(routerData).forEach((item) => {
+    routerDataWithName[item] = {
+      ...routerData[item],
+      name: routerData[item].name || menuData[item.replace(/^\//, '')],
+    };
+  });
+  return routerDataWithName;
+};

+ 4 - 4
src/components/PageHeader/index.js

@@ -14,7 +14,7 @@ function getBreadcrumb(breadcrumbNameMap, url) {
   if (breadcrumbNameMap[urlWithoutSplash]) {
     return breadcrumbNameMap[urlWithoutSplash];
   }
-  let breadcrumb = '';
+  let breadcrumb = {};
   Object.keys(breadcrumbNameMap).forEach((item) => {
     const itemRegExpStr = `^${item.replace(/:[\w-]+/g, '[\\w-]+')}$`;
     const itemRegExp = new RegExp(itemRegExpStr);
@@ -78,15 +78,15 @@ export default class PageHeader extends PureComponent {
         const url = `/${pathSnippets.slice(0, index + 1).join('/')}`;
         const currentBreadcrumb = getBreadcrumb(breadcrumbNameMap, url);
         const isLinkable = (index !== pathSnippets.length - 1) && currentBreadcrumb.component;
-        return (
+        return currentBreadcrumb.name && !currentBreadcrumb.hideInBreadcrumb ? (
           <Breadcrumb.Item key={url}>
             {createElement(
               isLinkable ? linkElement : 'span',
               { [linkElement === 'a' ? 'href' : 'to']: url },
-              currentBreadcrumb.name || url,
+              currentBreadcrumb.name,
             )}
           </Breadcrumb.Item>
-        );
+        ) : null;
       });
       const breadcrumbItems = [(
         <Breadcrumb.Item key="home">

+ 78 - 50
src/components/SiderMenu/index.js

@@ -3,6 +3,7 @@ import { Layout, Menu, Icon } from 'antd';
 import { Link } from 'dva/router';
 import logo from '../../assets/logo.svg';
 import styles from './index.less';
+import { getMenuData } from '../../common/menu';
 
 const { Sider } = Layout;
 const { SubMenu } = Menu;
@@ -10,8 +11,7 @@ const { SubMenu } = Menu;
 export default class SiderMenu extends PureComponent {
   constructor(props) {
     super(props);
-    // 把一级 Layout 的 children 作为菜单项
-    this.menus = props.navData.reduce((arr, current) => arr.concat(current.children), []);
+    this.menus = getMenuData();
     this.state = {
       openKeys: this.getDefaultCollapsedSubMenus(props),
     };
@@ -23,22 +23,49 @@ export default class SiderMenu extends PureComponent {
     });
   }
   getDefaultCollapsedSubMenus(props) {
-    const currentMenuSelectedKeys = [...this.getCurrentMenuSelectedKeys(props)];
-    currentMenuSelectedKeys.splice(-1, 1);
+    const { location: { pathname } } = props || this.props;
+    const snippets = pathname.split('/').slice(1, -1);
+    const currentPathSnippets = snippets.map((item, index) => {
+      const arr = snippets.filter((_, i) => i <= index);
+      return arr.join('/');
+    });
+    let currentMenuSelectedKeys = [];
+    currentPathSnippets.forEach((item) => {
+      currentMenuSelectedKeys = currentMenuSelectedKeys.concat(this.getSelectedMenuKeys(item));
+    });
     if (currentMenuSelectedKeys.length === 0) {
       return ['dashboard'];
     }
     return currentMenuSelectedKeys;
   }
-  getCurrentMenuSelectedKeys(props) {
-    const { location: { pathname } } = props || this.props;
-    const keys = pathname.split('/').slice(1);
-    if (keys.length === 1 && keys[0] === '') {
-      return [this.menus[0].key];
-    }
+  getFlatMenuKeys(menus) {
+    let keys = [];
+    menus.forEach((item) => {
+      if (item.children) {
+        keys.push(item.path);
+        keys = keys.concat(this.getFlatMenuKeys(item.children));
+      } else {
+        keys.push(item.path);
+      }
+    });
     return keys;
   }
-  getNavMenuItems(menusData, parentPath = '') {
+  getSelectedMenuKeys = (path) => {
+    const flatMenuKeys = this.getFlatMenuKeys(this.menus);
+
+    if (flatMenuKeys.indexOf(path.replace(/^\//, '')) > -1) {
+      return [path.replace(/^\//, '')];
+    }
+    if (flatMenuKeys.indexOf(path.replace(/^\//, '').replace(/\/$/, '')) > -1) {
+      return [path.replace(/^\//, '').replace(/\/$/, '')];
+    }
+    return flatMenuKeys.filter((item) => {
+      const itemRegExpStr = `^${item.replace(/:[\w-]+/g, '[\\w-]+')}$`;
+      const itemRegExp = new RegExp(itemRegExpStr);
+      return itemRegExp.test(path.replace(/^\//, ''));
+    });
+  }
+  getNavMenuItems(menusData) {
     if (!menusData) {
       return [];
     }
@@ -47,48 +74,50 @@ export default class SiderMenu extends PureComponent {
         return null;
       }
       let itemPath;
-      if (item.path.indexOf('http') === 0) {
+      if (item.path && item.path.indexOf('http') === 0) {
         itemPath = item.path;
       } else {
-        itemPath = `${parentPath}/${item.path || ''}`.replace(/\/+/g, '/');
+        itemPath = `/${item.path || ''}`.replace(/\/+/g, '/');
       }
       if (item.children && item.children.some(child => child.name)) {
-        return (
-          <SubMenu
-            title={
-              item.icon ? (
-                <span>
-                  <Icon type={item.icon} />
-                  <span>{item.name}</span>
-                </span>
-              ) : item.name
-            }
-            key={item.key || item.path}
-          >
-            {this.getNavMenuItems(item.children, itemPath)}
-          </SubMenu>
-        );
+        return item.hideInMenu ? null :
+          (
+            <SubMenu
+              title={
+                item.icon ? (
+                  <span>
+                    <Icon type={item.icon} />
+                    <span>{item.name}</span>
+                  </span>
+                ) : item.name
+              }
+              key={item.key || item.path}
+            >
+              {this.getNavMenuItems(item.children)}
+            </SubMenu>
+          );
       }
       const icon = item.icon && <Icon type={item.icon} />;
-      return (
-        <Menu.Item key={item.key || item.path}>
-          {
-            /^https?:\/\//.test(itemPath) ? (
-              <a href={itemPath} target={item.target}>
-                {icon}<span>{item.name}</span>
-              </a>
-            ) : (
-              <Link
-                to={itemPath}
-                target={item.target}
-                replace={itemPath === this.props.location.pathname}
-              >
-                {icon}<span>{item.name}</span>
-              </Link>
-            )
-          }
-        </Menu.Item>
-      );
+      return item.hideInMenu ? null :
+        (
+          <Menu.Item key={item.key || item.path}>
+            {
+              /^https?:\/\//.test(itemPath) ? (
+                <a href={itemPath} target={item.target}>
+                  {icon}<span>{item.name}</span>
+                </a>
+              ) : (
+                <Link
+                  to={itemPath}
+                  target={item.target}
+                  replace={itemPath === this.props.location.pathname}
+                >
+                  {icon}<span>{item.name}</span>
+                </Link>
+              )
+            }
+          </Menu.Item>
+        );
     });
   }
   handleOpenChange = (openKeys) => {
@@ -101,8 +130,7 @@ export default class SiderMenu extends PureComponent {
     });
   }
   render() {
-    const { collapsed } = this.props;
-
+    const { collapsed, location: { pathname } } = this.props;
     // Don't show popup menu when it is been collapsed
     const menuProps = collapsed ? {} : {
       openKeys: this.state.openKeys,
@@ -128,7 +156,7 @@ export default class SiderMenu extends PureComponent {
           mode="inline"
           {...menuProps}
           onOpenChange={this.handleOpenChange}
-          selectedKeys={this.getCurrentMenuSelectedKeys()}
+          selectedKeys={this.getSelectedMenuKeys(pathname)}
           style={{ padding: '16px 0', width: '100%' }}
         >
           {this.getNavMenuItems(this.menus)}

+ 15 - 39
src/layouts/BasicLayout.js

@@ -10,6 +10,7 @@ import GlobalHeader from '../components/GlobalHeader';
 import GlobalFooter from '../components/GlobalFooter';
 import SiderMenu from '../components/SiderMenu';
 import NotFound from '../routes/Exception/404';
+import { getRoutes } from '../utils/utils';
 
 const { Content } = Layout;
 
@@ -38,56 +39,31 @@ class BasicLayout extends React.PureComponent {
   static childContextTypes = {
     location: PropTypes.object,
     breadcrumbNameMap: PropTypes.object,
-    routeData: PropTypes.array,
   }
   getChildContext() {
-    const { location, navData, getRouteData } = this.props;
-    const routeData = getRouteData('BasicLayout');
-    const firstMenuData = navData.reduce((arr, current) => arr.concat(current.children), []);
-    const menuData = this.getMenuData(firstMenuData, '');
-    const breadcrumbNameMap = {};
-
-    routeData.concat(menuData).forEach((item) => {
-      breadcrumbNameMap[item.path] = {
-        name: item.name,
-        component: item.component,
-      };
-    });
-    return { location, breadcrumbNameMap, routeData };
+    const { location, routerData } = this.props;
+    return {
+      location,
+      breadcrumbNameMap: routerData,
+    };
   }
   getPageTitle() {
-    const { location, getRouteData } = this.props;
+    const { routerData, location } = this.props;
     const { pathname } = location;
     let title = 'Ant Design Pro';
-    getRouteData('BasicLayout').forEach((item) => {
-      if (item.path === pathname) {
-        title = `${item.name} - Ant Design Pro`;
-      }
-    });
+    if (routerData[pathname] && routerData[pathname].name) {
+      title = `${routerData[pathname].name} - Ant Design Pro`;
+    }
     return title;
   }
-  getMenuData = (data, parentPath) => {
-    let arr = [];
-    data.forEach((item) => {
-      if (item.name) {
-        arr.push({ path: `${parentPath}/${item.path}`, name: item.name });
-      }
-      if (item.children) {
-        arr = arr.concat(this.getMenuData(item.children, `${parentPath}/${item.path}`));
-      }
-    });
-    return arr;
-  }
   render() {
     const {
-      currentUser, collapsed, fetchingNotices, notices, getRouteData, navData, location, dispatch,
+      currentUser, collapsed, fetchingNotices, notices, routerData, match, location, dispatch,
     } = this.props;
-
     const layout = (
       <Layout>
         <SiderMenu
           collapsed={collapsed}
-          navData={navData}
           location={location}
           dispatch={dispatch}
         />
@@ -103,19 +79,19 @@ class BasicLayout extends React.PureComponent {
             <div style={{ minHeight: 'calc(100vh - 260px)' }}>
               <Switch>
                 {
-                  getRouteData('BasicLayout').map(item =>
+                  getRoutes(match.path, routerData).map(item =>
                     (
                       <Route
-                        exact={item.exact}
-                        key={item.path}
+                        key={item.key}
                         path={item.path}
                         component={item.component}
+                        exact={item.exact}
                       />
                     )
                   )
                 }
                 <Redirect exact from="/" to="/dashboard/analysis" />
-                <Route component={NotFound} />
+                <Route render={NotFound} />
               </Switch>
             </div>
             <GlobalFooter

+ 9 - 19
src/layouts/UserLayout.js

@@ -1,11 +1,11 @@
 import React from 'react';
-import PropTypes from 'prop-types';
 import { Link, Route } from 'dva/router';
 import DocumentTitle from 'react-document-title';
 import { Icon } from 'antd';
 import GlobalFooter from '../components/GlobalFooter';
 import styles from './UserLayout.less';
 import logo from '../assets/logo.svg';
+import { getRoutes } from '../utils/utils';
 
 const links = [{
   title: '帮助',
@@ -21,27 +21,17 @@ const links = [{
 const copyright = <div>Copyright <Icon type="copyright" /> 2017 蚂蚁金服体验技术部出品</div>;
 
 class UserLayout extends React.PureComponent {
-  static childContextTypes = {
-    location: PropTypes.object,
-  }
-  getChildContext() {
-    const { location } = this.props;
-    return { location };
-  }
   getPageTitle() {
-    const { getRouteData, location } = this.props;
+    const { routerData, location } = this.props;
     const { pathname } = location;
     let title = 'Ant Design Pro';
-    getRouteData('UserLayout').forEach((item) => {
-      if (item.path === pathname) {
-        title = `${item.name} - Ant Design Pro`;
-      }
-    });
+    if (routerData[pathname] && routerData[pathname].name) {
+      title = `${routerData[pathname].name} - Ant Design Pro`;
+    }
     return title;
   }
   render() {
-    const { getRouteData } = this.props;
-
+    const { routerData, match } = this.props;
     return (
       <DocumentTitle title={this.getPageTitle()}>
         <div className={styles.container}>
@@ -55,13 +45,13 @@ class UserLayout extends React.PureComponent {
             <div className={styles.desc}>Ant Design 是西湖区最具影响力的 Web 设计规范</div>
           </div>
           {
-            getRouteData('UserLayout').map(item =>
+            getRoutes(match.path, routerData).map(item =>
               (
                 <Route
-                  exact={item.exact}
-                  key={item.path}
+                  key={item.key}
                   path={item.path}
                   component={item.component}
+                  exact={item.exact}
                 />
               )
             )

+ 6 - 41
src/router.js

@@ -3,9 +3,7 @@ import { Router, Route, Switch } from 'dva/router';
 import { LocaleProvider, Spin } from 'antd';
 import zhCN from 'antd/lib/locale-provider/zh_CN';
 import dynamic from 'dva/dynamic';
-import cloneDeep from 'lodash/cloneDeep';
-import { getNavData } from './common/nav';
-import { getPlainNode } from './utils/utils';
+import { getRouterData } from './common/router';
 
 import styles from './index.less';
 
@@ -13,49 +11,16 @@ dynamic.setDefaultLoadingComponent(() => {
   return <Spin size="large" className={styles.globalSpin} />;
 });
 
-function getRouteData(navData, path) {
-  if (!navData.some(item => item.layout === path) ||
-    !(navData.filter(item => item.layout === path)[0].children)) {
-    return null;
-  }
-  const route = cloneDeep(navData.filter(item => item.layout === path)[0]);
-  const nodeList = getPlainNode(route.children);
-  return nodeList;
-}
-
-function getLayout(navData, path) {
-  if (!navData.some(item => item.layout === path) ||
-    !(navData.filter(item => item.layout === path)[0].children)) {
-    return null;
-  }
-  const route = navData.filter(item => item.layout === path)[0];
-  return {
-    component: route.component,
-    layout: route.layout,
-    name: route.name,
-    path: route.path,
-  };
-}
-
 function RouterConfig({ history, app }) {
-  const navData = getNavData(app);
-  const UserLayout = getLayout(navData, 'UserLayout').component;
-  const BasicLayout = getLayout(navData, 'BasicLayout').component;
-
-  const passProps = {
-    app,
-    navData,
-    getRouteData: (path) => {
-      return getRouteData(navData, path);
-    },
-  };
-
+  const routerData = getRouterData(app);
+  const UserLayout = routerData['/user'].component;
+  const BasicLayout = routerData['/'].component;
   return (
     <LocaleProvider locale={zhCN}>
       <Router history={history}>
         <Switch>
-          <Route path="/user" render={props => <UserLayout {...props} {...passProps} />} />
-          <Route path="/" render={props => <BasicLayout {...props} {...passProps} />} />
+          <Route path="/user" render={props => <UserLayout {...props} />} />
+          <Route path="/" render={props => <BasicLayout {...props} />} />
         </Switch>
       </Router>
     </LocaleProvider>

+ 8 - 11
src/routes/List/List.js

@@ -1,16 +1,12 @@
 import React, { Component } from 'react';
-import PropTypes from 'prop-types';
-import { routerRedux, Route, Switch } from 'dva/router';
+import { routerRedux, Route, Switch, Redirect } from 'dva/router';
 import { connect } from 'dva';
 import { Input } from 'antd';
 import PageHeaderLayout from '../../layouts/PageHeaderLayout';
+import { getRoutes } from '../../utils/utils';
 
 @connect()
 export default class SearchList extends Component {
-  static contextTypes = {
-    routeData: PropTypes.array,
-  };
-
   handleTabChange = (key) => {
     const { dispatch, match } = this.props;
     switch (key) {
@@ -52,9 +48,8 @@ export default class SearchList extends Component {
       </div>
     );
 
-    const { match } = this.props;
-    const { routeData } = this.context;
-    const routes = routeData.filter(item => item.path === match.path)[0].children;
+    const { match, routerData } = this.props;
+    const routes = getRoutes(match.path, routerData);
 
     return (
       <PageHeaderLayout
@@ -68,13 +63,15 @@ export default class SearchList extends Component {
             routes.map(item =>
               (
                 <Route
-                  key={item.path}
-                  path={`${match.path}/${item.path}`}
+                  key={item.key}
+                  path={item.path}
                   component={item.component}
+                  exact={item.exact}
                 />
               )
             )
           }
+          <Redirect exact from={`${match.path}`} to={`${match.path}${routes[0]}`} />
         </Switch>
       </PageHeaderLayout>
     );

+ 40 - 0
src/utils/utils.js

@@ -92,3 +92,43 @@ export function digitUppercase(n) {
 
   return s.replace(/(零.)*零元/, '元').replace(/(零.)+/g, '零').replace(/^整$/, '零元整');
 }
+
+function getRelation(str1, str2) {
+  if (str1 === str2) {
+    console.warn('Two path are equal!');
+  }
+  const arr1 = str1.split('/');
+  const arr2 = str2.split('/');
+  if (arr2.every((item, index) => item === arr1[index])) {
+    return 1;
+  } else if (arr1.every((item, index) => item === arr2[index])) {
+    return 2;
+  }
+  return 3;
+}
+
+export function getRoutes(path, routerData) {
+  let routes = Object.keys(routerData).filter(routePath =>
+    routePath.indexOf(path) === 0 && routePath !== path);
+  routes = routes.map(item => item.replace(path, ''));
+  let renderArr = [];
+  renderArr.push(routes[0]);
+  for (let i = 1; i < routes.length; i += 1) {
+    let isAdd = false;
+    isAdd = renderArr.every(item => getRelation(item, routes[i]) === 3);
+    renderArr = renderArr.filter(item => getRelation(item, routes[i]) !== 1);
+    if (isAdd) {
+      renderArr.push(routes[i]);
+    }
+  }
+  const renderRoutes = renderArr.map((item) => {
+    const exact = !routes.some(route => route !== item && getRelation(route, item) === 1);
+    return {
+      key: `${path}${item}`,
+      path: `${path}${item}`,
+      component: routerData[`${path}${item}`].component,
+      exact,
+    };
+  });
+  return renderRoutes;
+}