Unverified Commit 577ded4b authored by ddcat1115's avatar ddcat1115 Committed by GitHub

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
parent 6e37a939
...@@ -53,6 +53,6 @@ ...@@ -53,6 +53,6 @@
} }
}, },
"settings": { "settings": {
"polyfills": ["fetch"] "polyfills": ["fetch", "promises"]
} }
} }
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);
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',
},
},
];
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;
};
...@@ -14,7 +14,7 @@ function getBreadcrumb(breadcrumbNameMap, url) { ...@@ -14,7 +14,7 @@ function getBreadcrumb(breadcrumbNameMap, url) {
if (breadcrumbNameMap[urlWithoutSplash]) { if (breadcrumbNameMap[urlWithoutSplash]) {
return breadcrumbNameMap[urlWithoutSplash]; return breadcrumbNameMap[urlWithoutSplash];
} }
let breadcrumb = ''; let breadcrumb = {};
Object.keys(breadcrumbNameMap).forEach((item) => { Object.keys(breadcrumbNameMap).forEach((item) => {
const itemRegExpStr = `^${item.replace(/:[\w-]+/g, '[\\w-]+')}$`; const itemRegExpStr = `^${item.replace(/:[\w-]+/g, '[\\w-]+')}$`;
const itemRegExp = new RegExp(itemRegExpStr); const itemRegExp = new RegExp(itemRegExpStr);
...@@ -78,15 +78,15 @@ export default class PageHeader extends PureComponent { ...@@ -78,15 +78,15 @@ export default class PageHeader extends PureComponent {
const url = `/${pathSnippets.slice(0, index + 1).join('/')}`; const url = `/${pathSnippets.slice(0, index + 1).join('/')}`;
const currentBreadcrumb = getBreadcrumb(breadcrumbNameMap, url); const currentBreadcrumb = getBreadcrumb(breadcrumbNameMap, url);
const isLinkable = (index !== pathSnippets.length - 1) && currentBreadcrumb.component; const isLinkable = (index !== pathSnippets.length - 1) && currentBreadcrumb.component;
return ( return currentBreadcrumb.name && !currentBreadcrumb.hideInBreadcrumb ? (
<Breadcrumb.Item key={url}> <Breadcrumb.Item key={url}>
{createElement( {createElement(
isLinkable ? linkElement : 'span', isLinkable ? linkElement : 'span',
{ [linkElement === 'a' ? 'href' : 'to']: url }, { [linkElement === 'a' ? 'href' : 'to']: url },
currentBreadcrumb.name || url, currentBreadcrumb.name,
)} )}
</Breadcrumb.Item> </Breadcrumb.Item>
); ) : null;
}); });
const breadcrumbItems = [( const breadcrumbItems = [(
<Breadcrumb.Item key="home"> <Breadcrumb.Item key="home">
......
...@@ -3,6 +3,7 @@ import { Layout, Menu, Icon } from 'antd'; ...@@ -3,6 +3,7 @@ import { Layout, Menu, Icon } from 'antd';
import { Link } from 'dva/router'; import { Link } from 'dva/router';
import logo from '../../assets/logo.svg'; import logo from '../../assets/logo.svg';
import styles from './index.less'; import styles from './index.less';
import { getMenuData } from '../../common/menu';
const { Sider } = Layout; const { Sider } = Layout;
const { SubMenu } = Menu; const { SubMenu } = Menu;
...@@ -10,8 +11,7 @@ const { SubMenu } = Menu; ...@@ -10,8 +11,7 @@ const { SubMenu } = Menu;
export default class SiderMenu extends PureComponent { export default class SiderMenu extends PureComponent {
constructor(props) { constructor(props) {
super(props); super(props);
// 把一级 Layout 的 children 作为菜单项 this.menus = getMenuData();
this.menus = props.navData.reduce((arr, current) => arr.concat(current.children), []);
this.state = { this.state = {
openKeys: this.getDefaultCollapsedSubMenus(props), openKeys: this.getDefaultCollapsedSubMenus(props),
}; };
...@@ -23,22 +23,49 @@ export default class SiderMenu extends PureComponent { ...@@ -23,22 +23,49 @@ export default class SiderMenu extends PureComponent {
}); });
} }
getDefaultCollapsedSubMenus(props) { getDefaultCollapsedSubMenus(props) {
const currentMenuSelectedKeys = [...this.getCurrentMenuSelectedKeys(props)]; const { location: { pathname } } = props || this.props;
currentMenuSelectedKeys.splice(-1, 1); 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) { if (currentMenuSelectedKeys.length === 0) {
return ['dashboard']; return ['dashboard'];
} }
return currentMenuSelectedKeys; return currentMenuSelectedKeys;
} }
getCurrentMenuSelectedKeys(props) { getFlatMenuKeys(menus) {
const { location: { pathname } } = props || this.props; let keys = [];
const keys = pathname.split('/').slice(1); menus.forEach((item) => {
if (keys.length === 1 && keys[0] === '') { if (item.children) {
return [this.menus[0].key]; keys.push(item.path);
} keys = keys.concat(this.getFlatMenuKeys(item.children));
} else {
keys.push(item.path);
}
});
return keys; 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) { if (!menusData) {
return []; return [];
} }
...@@ -47,48 +74,50 @@ export default class SiderMenu extends PureComponent { ...@@ -47,48 +74,50 @@ export default class SiderMenu extends PureComponent {
return null; return null;
} }
let itemPath; let itemPath;
if (item.path.indexOf('http') === 0) { if (item.path && item.path.indexOf('http') === 0) {
itemPath = item.path; itemPath = item.path;
} else { } else {
itemPath = `${parentPath}/${item.path || ''}`.replace(/\/+/g, '/'); itemPath = `/${item.path || ''}`.replace(/\/+/g, '/');
} }
if (item.children && item.children.some(child => child.name)) { if (item.children && item.children.some(child => child.name)) {
return ( return item.hideInMenu ? null :
<SubMenu (
title={ <SubMenu
item.icon ? ( title={
<span> item.icon ? (
<Icon type={item.icon} /> <span>
<span>{item.name}</span> <Icon type={item.icon} />
</span> <span>{item.name}</span>
) : item.name </span>
} ) : item.name
key={item.key || item.path} }
> key={item.key || item.path}
{this.getNavMenuItems(item.children, itemPath)} >
</SubMenu> {this.getNavMenuItems(item.children)}
); </SubMenu>
);
} }
const icon = item.icon && <Icon type={item.icon} />; const icon = item.icon && <Icon type={item.icon} />;
return ( return item.hideInMenu ? null :
<Menu.Item key={item.key || item.path}> (
{ <Menu.Item key={item.key || item.path}>
/^https?:\/\//.test(itemPath) ? ( {
<a href={itemPath} target={item.target}> /^https?:\/\//.test(itemPath) ? (
{icon}<span>{item.name}</span> <a href={itemPath} target={item.target}>
</a> {icon}<span>{item.name}</span>
) : ( </a>
<Link ) : (
to={itemPath} <Link
target={item.target} to={itemPath}
replace={itemPath === this.props.location.pathname} target={item.target}
> replace={itemPath === this.props.location.pathname}
{icon}<span>{item.name}</span> >
</Link> {icon}<span>{item.name}</span>
) </Link>
} )
</Menu.Item> }
); </Menu.Item>
);
}); });
} }
handleOpenChange = (openKeys) => { handleOpenChange = (openKeys) => {
...@@ -101,8 +130,7 @@ export default class SiderMenu extends PureComponent { ...@@ -101,8 +130,7 @@ export default class SiderMenu extends PureComponent {
}); });
} }
render() { render() {
const { collapsed } = this.props; const { collapsed, location: { pathname } } = this.props;
// Don't show popup menu when it is been collapsed // Don't show popup menu when it is been collapsed
const menuProps = collapsed ? {} : { const menuProps = collapsed ? {} : {
openKeys: this.state.openKeys, openKeys: this.state.openKeys,
...@@ -128,7 +156,7 @@ export default class SiderMenu extends PureComponent { ...@@ -128,7 +156,7 @@ export default class SiderMenu extends PureComponent {
mode="inline" mode="inline"
{...menuProps} {...menuProps}
onOpenChange={this.handleOpenChange} onOpenChange={this.handleOpenChange}
selectedKeys={this.getCurrentMenuSelectedKeys()} selectedKeys={this.getSelectedMenuKeys(pathname)}
style={{ padding: '16px 0', width: '100%' }} style={{ padding: '16px 0', width: '100%' }}
> >
{this.getNavMenuItems(this.menus)} {this.getNavMenuItems(this.menus)}
......
...@@ -10,6 +10,7 @@ import GlobalHeader from '../components/GlobalHeader'; ...@@ -10,6 +10,7 @@ import GlobalHeader from '../components/GlobalHeader';
import GlobalFooter from '../components/GlobalFooter'; import GlobalFooter from '../components/GlobalFooter';
import SiderMenu from '../components/SiderMenu'; import SiderMenu from '../components/SiderMenu';
import NotFound from '../routes/Exception/404'; import NotFound from '../routes/Exception/404';
import { getRoutes } from '../utils/utils';
const { Content } = Layout; const { Content } = Layout;
...@@ -38,56 +39,31 @@ class BasicLayout extends React.PureComponent { ...@@ -38,56 +39,31 @@ class BasicLayout extends React.PureComponent {
static childContextTypes = { static childContextTypes = {
location: PropTypes.object, location: PropTypes.object,
breadcrumbNameMap: PropTypes.object, breadcrumbNameMap: PropTypes.object,
routeData: PropTypes.array,
} }
getChildContext() { getChildContext() {
const { location, navData, getRouteData } = this.props; const { location, routerData } = this.props;
const routeData = getRouteData('BasicLayout'); return {
const firstMenuData = navData.reduce((arr, current) => arr.concat(current.children), []); location,
const menuData = this.getMenuData(firstMenuData, ''); breadcrumbNameMap: routerData,
const breadcrumbNameMap = {}; };
routeData.concat(menuData).forEach((item) => {
breadcrumbNameMap[item.path] = {
name: item.name,
component: item.component,
};
});
return { location, breadcrumbNameMap, routeData };
} }
getPageTitle() { getPageTitle() {
const { location, getRouteData } = this.props; const { routerData, location } = this.props;
const { pathname } = location; const { pathname } = location;
let title = 'Ant Design Pro'; let title = 'Ant Design Pro';
getRouteData('BasicLayout').forEach((item) => { if (routerData[pathname] && routerData[pathname].name) {
if (item.path === pathname) { title = `${routerData[pathname].name} - Ant Design Pro`;
title = `${item.name} - Ant Design Pro`; }
}
});
return title; 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() { render() {
const { const {
currentUser, collapsed, fetchingNotices, notices, getRouteData, navData, location, dispatch, currentUser, collapsed, fetchingNotices, notices, routerData, match, location, dispatch,
} = this.props; } = this.props;
const layout = ( const layout = (
<Layout> <Layout>
<SiderMenu <SiderMenu
collapsed={collapsed} collapsed={collapsed}
navData={navData}
location={location} location={location}
dispatch={dispatch} dispatch={dispatch}
/> />
...@@ -103,19 +79,19 @@ class BasicLayout extends React.PureComponent { ...@@ -103,19 +79,19 @@ class BasicLayout extends React.PureComponent {
<div style={{ minHeight: 'calc(100vh - 260px)' }}> <div style={{ minHeight: 'calc(100vh - 260px)' }}>
<Switch> <Switch>
{ {
getRouteData('BasicLayout').map(item => getRoutes(match.path, routerData).map(item =>
( (
<Route <Route
exact={item.exact} key={item.key}
key={item.path}
path={item.path} path={item.path}
component={item.component} component={item.component}
exact={item.exact}
/> />
) )
) )
} }
<Redirect exact from="/" to="/dashboard/analysis" /> <Redirect exact from="/" to="/dashboard/analysis" />
<Route component={NotFound} /> <Route render={NotFound} />
</Switch> </Switch>
</div> </div>
<GlobalFooter <GlobalFooter
......
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types';
import { Link, Route } from 'dva/router'; import { Link, Route } from 'dva/router';
import DocumentTitle from 'react-document-title'; import DocumentTitle from 'react-document-title';
import { Icon } from 'antd'; import { Icon } from 'antd';
import GlobalFooter from '../components/GlobalFooter'; import GlobalFooter from '../components/GlobalFooter';
import styles from './UserLayout.less'; import styles from './UserLayout.less';
import logo from '../assets/logo.svg'; import logo from '../assets/logo.svg';
import { getRoutes } from '../utils/utils';
const links = [{ const links = [{
title: '帮助', title: '帮助',
...@@ -21,27 +21,17 @@ const links = [{ ...@@ -21,27 +21,17 @@ const links = [{
const copyright = <div>Copyright <Icon type="copyright" /> 2017 蚂蚁金服体验技术部出品</div>; const copyright = <div>Copyright <Icon type="copyright" /> 2017 蚂蚁金服体验技术部出品</div>;
class UserLayout extends React.PureComponent { class UserLayout extends React.PureComponent {
static childContextTypes = {
location: PropTypes.object,
}
getChildContext() {
const { location } = this.props;
return { location };
}
getPageTitle() { getPageTitle() {
const { getRouteData, location } = this.props; const { routerData, location } = this.props;
const { pathname } = location; const { pathname } = location;
let title = 'Ant Design Pro'; let title = 'Ant Design Pro';
getRouteData('UserLayout').forEach((item) => { if (routerData[pathname] && routerData[pathname].name) {
if (item.path === pathname) { title = `${routerData[pathname].name} - Ant Design Pro`;
title = `${item.name} - Ant Design Pro`; }
}
});
return title; return title;
} }
render() { render() {
const { getRouteData } = this.props; const { routerData, match } = this.props;
return ( return (
<DocumentTitle title={this.getPageTitle()}> <DocumentTitle title={this.getPageTitle()}>
<div className={styles.container}> <div className={styles.container}>
...@@ -55,13 +45,13 @@ class UserLayout extends React.PureComponent { ...@@ -55,13 +45,13 @@ class UserLayout extends React.PureComponent {
<div className={styles.desc}>Ant Design 是西湖区最具影响力的 Web 设计规范</div> <div className={styles.desc}>Ant Design 是西湖区最具影响力的 Web 设计规范</div>
</div> </div>
{ {
getRouteData('UserLayout').map(item => getRoutes(match.path, routerData).map(item =>
( (
<Route <Route
exact={item.exact} key={item.key}
key={item.path}
path={item.path} path={item.path}
component={item.component} component={item.component}
exact={item.exact}
/> />
) )
) )
......
...@@ -3,9 +3,7 @@ import { Router, Route, Switch } from 'dva/router'; ...@@ -3,9 +3,7 @@ import { Router, Route, Switch } from 'dva/router';
import { LocaleProvider, Spin } from 'antd'; import { LocaleProvider, Spin } from 'antd';
import zhCN from 'antd/lib/locale-provider/zh_CN'; import zhCN from 'antd/lib/locale-provider/zh_CN';
import dynamic from 'dva/dynamic'; import dynamic from 'dva/dynamic';
import cloneDeep from 'lodash/cloneDeep'; import { getRouterData } from './common/router';
import { getNavData } from './common/nav';
import { getPlainNode } from './utils/utils';
import styles from './index.less'; import styles from './index.less';
...@@ -13,49 +11,16 @@ dynamic.setDefaultLoadingComponent(() => { ...@@ -13,49 +11,16 @@ dynamic.setDefaultLoadingComponent(() => {
return <Spin size="large" className={styles.globalSpin} />; 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 }) { function RouterConfig({ history, app }) {
const navData = getNavData(app); const routerData = getRouterData(app);
const UserLayout = getLayout(navData, 'UserLayout').component; const UserLayout = routerData['/user'].component;
const BasicLayout = getLayout(navData, 'BasicLayout').component; const BasicLayout = routerData['/'].component;
const passProps = {
app,
navData,
getRouteData: (path) => {
return getRouteData(navData, path);
},
};
return ( return (
<LocaleProvider locale={zhCN}> <LocaleProvider locale={zhCN}>
<Router history={history}> <Router history={history}>
<Switch> <Switch>
<Route path="/user" render={props => <UserLayout {...props} {...passProps} />} /> <Route path="/user" render={props => <UserLayout {...props} />} />
<Route path="/" render={props => <BasicLayout {...props} {...passProps} />} /> <Route path="/" render={props => <BasicLayout {...props} />} />
</Switch> </Switch>
</Router> </Router>
</LocaleProvider> </LocaleProvider>
......
import React, { Component } from 'react'; import React, { Component } from 'react';
import PropTypes from 'prop-types'; import { routerRedux, Route, Switch, Redirect } from 'dva/router';
import { routerRedux, Route, Switch } from 'dva/router';
import { connect } from 'dva'; import { connect } from 'dva';
import { Input } from 'antd'; import { Input } from 'antd';
import PageHeaderLayout from '../../layouts/PageHeaderLayout'; import PageHeaderLayout from '../../layouts/PageHeaderLayout';
import { getRoutes } from '../../utils/utils';
@connect() @connect()
export default class SearchList extends Component { export default class SearchList extends Component {
static contextTypes = {
routeData: PropTypes.array,
};
handleTabChange = (key) => { handleTabChange = (key) => {
const { dispatch, match } = this.props; const { dispatch, match } = this.props;
switch (key) { switch (key) {
...@@ -52,9 +48,8 @@ export default class SearchList extends Component { ...@@ -52,9 +48,8 @@ export default class SearchList extends Component {
</div> </div>
); );
const { match } = this.props; const { match, routerData } = this.props;
const { routeData } = this.context; const routes = getRoutes(match.path, routerData);
const routes = routeData.filter(item => item.path === match.path)[0].children;
return ( return (
<PageHeaderLayout <PageHeaderLayout
...@@ -68,13 +63,15 @@ export default class SearchList extends Component { ...@@ -68,13 +63,15 @@ export default class SearchList extends Component {
routes.map(item => routes.map(item =>
( (
<Route <Route
key={item.path} key={item.key}
path={`${match.path}/${item.path}`} path={item.path}
component={item.component} component={item.component}
exact={item.exact}
/> />
) )
) )
} }
<Redirect exact from={`${match.path}`} to={`${match.path}${routes[0]}`} />
</Switch> </Switch>
</PageHeaderLayout> </PageHeaderLayout>
); );
......
...@@ -92,3 +92,43 @@ export function digitUppercase(n) { ...@@ -92,3 +92,43 @@ export function digitUppercase(n) {
return s.replace(/(零.)*零元/, '').replace(/(零.)+/g, '').replace(/^整$/, '零元整'); 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;
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment