diff --git a/mock/api.js b/mock/api.js index 4daf20fa9c2815923dedc670e2905e4e9abcce95..ddbf3bc0416d0a0df818109315bb8841cd628aa9 100644 --- a/mock/api.js +++ b/mock/api.js @@ -113,6 +113,8 @@ export const getNotice = [ description: '那是一种内在的东西, 他们到达不了,也无法触及的', updatedAt: new Date(), member: '科学搬砖组', + href: '', + memberLink: '', }, { id: 'xxx2', @@ -121,6 +123,8 @@ export const getNotice = [ description: '希望是一个好东西,也许是最好的,好东西是不会消亡的', updatedAt: new Date('2017-07-24 11:00:00'), member: '全组都是吴彦祖', + href: '', + memberLink: '', }, { id: 'xxx3', @@ -129,6 +133,8 @@ export const getNotice = [ description: '城镇中有那么多的酒馆,她却偏偏走进了我的酒馆', updatedAt: new Date(), member: '中二少女团', + href: '', + memberLink: '', }, { id: 'xxx4', @@ -137,6 +143,8 @@ export const getNotice = [ description: '那时候我只会想自己想要什么,从不想自己拥有什么', updatedAt: new Date('2017-07-23 06:23:00'), member: '程序员日常', + href: '', + memberLink: '', }, { id: 'xxx5', @@ -145,6 +153,8 @@ export const getNotice = [ description: '凛冬将至', updatedAt: new Date('2017-07-23 06:23:00'), member: '高逼格设计天团', + href: '', + memberLink: '', }, { id: 'xxx6', @@ -153,6 +163,8 @@ export const getNotice = [ description: '生命就像一盒巧克力,结果往往出人意料', updatedAt: new Date('2017-07-23 06:23:00'), member: '骗你来学计算机', + href: '', + memberLink: '', }, ]; diff --git a/package.json b/package.json index 81d8f5ed3ac2c71b18ad466853cdb424d19bbf38..cebd098de1019742de3d00b84d413dbe53754059 100755 --- a/package.json +++ b/package.json @@ -17,15 +17,16 @@ }, "dependencies": { "antd": "next", + "dva": "^2.0.3", "classnames": "^2.2.5", - "dva": "^1.2.1", "lodash": "^4.17.4", "numeral": "^2.0.6", "prop-types": "^15.5.10", "qs": "^6.5.0", "react": "^15.6.2", "react-document-title": "^2.0.3", - "react-dom": "^15.6.2" + "react-dom": "^15.6.2", + "lodash.clonedeep": "^4.5.0" }, "devDependencies": { "babel-eslint": "^8.0.1", @@ -56,7 +57,7 @@ "nightmare": "^2.10.0", "react-test-renderer": "^15.6.2", "redbox-react": "^1.3.2", - "roadhog": "^1.0.2", + "roadhog": "^1.2.1", "roadhog-api-doc": "^0.1.8", "stylelint": "^8.1.0", "stylelint-config-standard": "^17.0.0" diff --git a/src/common/nav.js b/src/common/nav.js index 0984770c3e4f85405cc29cfaa4adb49e15bf6f6e..42153e2b40f37fdfb3c8e929710958c4911f1d41 100644 --- a/src/common/nav.js +++ b/src/common/nav.js @@ -34,6 +34,7 @@ import RegisterResult from '../routes/User/RegisterResult'; const data = [{ component: BasicLayout, + layout: 'BasicLayout', name: '首页', // for breadcrumb path: '', children: [{ @@ -152,6 +153,7 @@ const data = [{ }], }, { component: UserLayout, + layout: 'UserLayout', children: [{ name: '帐户', icon: 'user', diff --git a/src/components/PageHeader/index.js b/src/components/PageHeader/index.js index 130f35ca5b8d1a651551f219fd9fbc7091caa1d0..035c7e50036e1212034f78c1241feebf7c8bfe3d 100644 --- a/src/components/PageHeader/index.js +++ b/src/components/PageHeader/index.js @@ -18,6 +18,8 @@ export default class PageHeader extends PureComponent { static contextTypes = { routes: PropTypes.array, params: PropTypes.object, + location: PropTypes.object, + breadcrumbNameMap: PropTypes.object, }; onChange = (key) => { if (this.props.onTabChange) { @@ -28,10 +30,12 @@ export default class PageHeader extends PureComponent { return { routes: this.props.routes || this.context.routes, params: this.props.params || this.context.params, + location: this.props.location || this.context.location, + breadcrumbNameMap: this.props.breadcrumbNameMap || this.context.breadcrumbNameMap, }; }; render() { - const { routes, params } = this.getBreadcrumbProps(); + const { routes, params, location, breadcrumbNameMap } = this.getBreadcrumbProps(); const { title, logo, action, content, extraContent, breadcrumbList, tabList, className } = this.props; const clsString = classNames(styles.pageHeader, className); @@ -45,6 +49,28 @@ export default class PageHeader extends PureComponent { itemRender={itemRender} /> ); + } else if (location && location.pathname) { + const pathSnippets = location.pathname.split('/').filter(i => i); + const extraBreadcrumbItems = pathSnippets.map((_, index) => { + const url = `/${pathSnippets.slice(0, index + 1).join('/')}`; + return ( + + + {breadcrumbNameMap[url] || breadcrumbNameMap[url.replace('/', '')] || url} + + + ); + }); + const breadcrumbItems = [( + + Home + + )].concat(extraBreadcrumbItems); + breadcrumb = ( + + {breadcrumbItems} + + ); } else if (breadcrumbList && breadcrumbList.length) { breadcrumb = ( diff --git a/src/index.js b/src/index.js index 22a9fcb1377d1cca0a9985ec2684360f98f67b71..ae0c9dbf9425b36325b17c66ef2bfcabe14b06af 100644 --- a/src/index.js +++ b/src/index.js @@ -1,9 +1,8 @@ import dva from 'dva'; import G2 from 'g2'; -// import { browserHistory } from 'dva/router'; import 'moment/locale/zh-cn'; import models from './models'; - +// import { browserHistory } from 'dva/router'; import './index.less'; G2.track(false); @@ -16,7 +15,7 @@ const app = dva({ // 2. Plugins // app.use({}); -// 3. Model +// 3. Model move to router models.forEach((m) => { app.model(m); }); diff --git a/src/layouts/BasicLayout.js b/src/layouts/BasicLayout.js index d9ca083bd2814efa067db26bae437b7fb8e8268f..eb2b11a691139b3f32a42f454206ab5600debbe3 100644 --- a/src/layouts/BasicLayout.js +++ b/src/layouts/BasicLayout.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import { Layout, Menu, Icon, Avatar, Dropdown, Tag, message, Spin } from 'antd'; import DocumentTitle from 'react-document-title'; import { connect } from 'dva'; -import { Link, routerRedux } from 'dva/router'; +import { Link, routerRedux, Route, Redirect, Switch } from 'dva/router'; import moment from 'moment'; import groupBy from 'lodash/groupBy'; import styles from './BasicLayout.less'; @@ -11,14 +11,15 @@ import HeaderSearch from '../components/HeaderSearch'; import NoticeIcon from '../components/NoticeIcon'; import GlobalFooter from '../components/GlobalFooter'; import { getNavData } from '../common/nav'; +import { getRouteData } from '../utils/utils'; const { Header, Sider, Content } = Layout; const { SubMenu } = Menu; class BasicLayout extends React.PureComponent { static childContextTypes = { - routes: PropTypes.array, - params: PropTypes.object, + location: PropTypes.object, + breadcrumbNameMap: PropTypes.object, } constructor(props) { super(props); @@ -29,8 +30,14 @@ class BasicLayout extends React.PureComponent { }; } getChildContext() { - const { routes, params } = this.props; - return { routes, params }; + const { location } = this.props; + const routeData = getRouteData('BasicLayout'); + const menuData = getNavData().reduce((arr, current) => arr.concat(current.children), []); + const breadcrumbNameMap = {}; + routeData.concat(menuData).forEach((item) => { + breadcrumbNameMap[item.path] = item.name; + }); + return { location, breadcrumbNameMap }; } componentDidMount() { this.props.dispatch({ @@ -98,13 +105,15 @@ class BasicLayout extends React.PureComponent { }); } getPageTitle() { - const { routes } = this.props; - for (let i = routes.length - 1; i >= 0; i -= 1) { - if (routes[i].breadcrumbName) { - return `${routes[i].breadcrumbName} - Ant Design Pro`; + const { 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`; } - } - return 'Ant Design Pro'; + }); + return title; } getNoticeData() { const { notices = [] } = this.props; @@ -161,7 +170,7 @@ class BasicLayout extends React.PureComponent { } } render() { - const { children, currentUser, collapsed, fetchingNotices } = this.props; + const { currentUser, collapsed, fetchingNotices } = this.props; const menu = ( @@ -171,7 +180,6 @@ class BasicLayout extends React.PureComponent { 退出登录 ); - const noticeData = this.getNoticeData(); // Don't show popup menu when it is been collapsed @@ -270,7 +278,21 @@ class BasicLayout extends React.PureComponent { - {children} + + { + getRouteData('BasicLayout').map(item => + ( + + ) + ) + } + + Copyright 2017 蚂蚁金服体 class UserLayout extends React.PureComponent { static childContextTypes = { - routes: PropTypes.array, - params: PropTypes.object, + location: PropTypes.object, } getChildContext() { - const { routes, params } = this.props; - return { routes, params }; + const { location } = this.props; + return { location }; } getPageTitle() { - const { routes } = this.props; - for (let i = routes.length - 1; i >= 0; i -= 1) { - if (routes[i].breadcrumbName) { - return `${routes[i].breadcrumbName} - Ant Design Pro`; + const { 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`; } - } - return 'Ant Design Pro'; + }); + return title; } render() { - const { children } = this.props; - return (
@@ -52,7 +52,18 @@ class UserLayout extends React.PureComponent {

Ant Design 是西湖区最具影响力的 Web 设计规范

- {children} + { + getRouteData('UserLayout').map(item => + ( + + ) + ) + }
diff --git a/src/router.js b/src/router.js index 70c08fa72ceff37220de8d9b10b27490b2e313f6..f50bdf240a4fc456f7d947ad1155293a44d8aade 100644 --- a/src/router.js +++ b/src/router.js @@ -1,48 +1,19 @@ import React from 'react'; -import { Router, Route, Redirect } from 'dva/router'; +import { Router, Route, Switch, Redirect } from 'dva/router'; import { LocaleProvider } from 'antd'; import zhCN from 'antd/lib/locale-provider/zh_CN'; -import navData from './common/nav'; - -function getRoutes(data, level = 0) { - return data.map((item, i) => { - let children; - if (item.children) { - children = getRoutes(item.children, level + 1); - } - let homePageRedirect; - if (level === 1 && i === 0) { - let indexPath; - // First children router - if (item.children && item.children[0]) { - indexPath = `/${item.path}/${item.children[0].path}`; - } else { - indexPath = item.path; - } - homePageRedirect = ; - } - if (item.noRoute) { - return null; - } - return ( - - {homePageRedirect} - {children} - - ); - }); -} +import BasicLayout from './layouts/BasicLayout'; +import UserLayout from './layouts/UserLayout'; function RouterConfig({ history }) { return ( - {getRoutes(navData)} + + + + + ); diff --git a/src/routes/Forms/StepForm/index.js b/src/routes/Forms/StepForm/index.js index bca88bb0a8afc3f76f3d12da12aaa29805898f85..628c77485b80e01e3ee659f119cd7884a10271e0 100644 --- a/src/routes/Forms/StepForm/index.js +++ b/src/routes/Forms/StepForm/index.js @@ -1,8 +1,10 @@ -import React, { cloneElement, PureComponent } from 'react'; +import React, { PureComponent } from 'react'; import { connect } from 'dva'; import { Card, Steps, Form } from 'antd'; import PageHeaderLayout from '../../../layouts/PageHeaderLayout'; import Step1 from './Step1'; +import Step2 from './Step2'; +import Step3 from './Step3'; import styles from '../style.less'; const { Step } = Steps; @@ -10,16 +12,26 @@ const { Step } = Steps; @Form.create() class StepForm extends PureComponent { getCurrentStep() { - const { routes } = this.props; - switch (routes[routes.length - 1].path) { + const { location } = this.props; + const { pathname } = location; + const pathList = pathname.split('/'); + switch (pathList[pathList.length - 1]) { case 'step-form': return 0; case 'confirm': return 1; case 'result': return 2; default: return 0; } } + getCurrentComponent() { + const componentMap = { + 0: Step1, + 1: Step2, + 2: Step3, + }; + return componentMap[this.getCurrentStep()]; + } render() { - const { form, stepFormData, submitting, dispatch, children } = this.props; + const { form, stepFormData, submitting, dispatch } = this.props; const formItemLayout = { labelCol: { span: 5, @@ -28,6 +40,7 @@ class StepForm extends PureComponent { span: 19, }, }; + const CurrentComponent = this.getCurrentComponent(); return ( @@ -37,19 +50,13 @@ class StepForm extends PureComponent { - {children ? cloneElement(children, { - form, - formItemLayout, - data: stepFormData, - submitting, - dispatch, - }) : ( - - )} + diff --git a/src/routes/List/CoverCardList.js b/src/routes/List/CoverCardList.js index 08f65f7f0b2cec5563adcf6b35a3f91696fc5537..cf4da3b1456da05295ea690d46b98a8887f199dc 100644 --- a/src/routes/List/CoverCardList.js +++ b/src/routes/List/CoverCardList.js @@ -1,7 +1,7 @@ import React, { PureComponent } from 'react'; import moment from 'moment'; import { connect } from 'dva'; -import { Link, routerRedux } from 'dva/router'; +import { routerRedux } from 'dva/router'; import { Row, Col, Form, Card, Select, List, Input } from 'antd'; import PageHeaderLayout from '../../layouts/PageHeaderLayout'; @@ -78,33 +78,31 @@ export default class CoverCardList extends PureComponent { dataSource={list} renderItem={item => ( - - } - > - -
- {moment(item.updatedAt).fromNow()} -
- - { - item.members.map((member, i) => ( - - )) - } - -
+ } + > + +
+ {moment(item.updatedAt).fromNow()} +
+ + { + item.members.map((member, i) => ( + + )) + } +
- - +
+
)} /> diff --git a/src/utils/utils.js b/src/utils/utils.js index 18b3284d25984f27e25d1249fb3d45a274f9b93b..014b4d5af680c0e174e8a33a5f1c05a9605ef9d1 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -1,4 +1,7 @@ import moment from 'moment'; +import cloneDeep from 'lodash/cloneDeep'; +import navData from '../common/nav'; + export function fixedZero(val) { return val * 1 < 10 ? `0${val}` : val; @@ -48,3 +51,31 @@ export function getTimeDistance(type) { return [moment(`${year}-01-01 00:00:00`), moment(`${year}-12-31 23:59:59`)]; } } + +function getPlainNode(nodeList, parentPath = '') { + const arr = []; + nodeList.forEach((node) => { + const item = node; + item.path = `${parentPath}/${item.path || ''}`.replace(/\/+/g, '/'); + item.exact = true; + if (item.children && !item.component) { + arr.push(...getPlainNode(item.children, item.path)); + } else { + if (item.children && item.component) { + item.exact = false; + } + arr.push(item); + } + }); + return arr; +} + +export function getRouteData(path) { + if (!navData.some(item => item.layout === path) || + !(navData.filter(item => item.layout === path)[0].children)) { + return null; + } + const dataList = cloneDeep(navData.filter(item => item.layout === path)[0]); + const nodeList = getPlainNode(dataList.children); + return nodeList; +}