From de68881f329782296535c5c71185d8dee3b7753f Mon Sep 17 00:00:00 2001 From: zuiidea Date: Thu, 23 Mar 2017 19:30:00 +0800 Subject: [PATCH 1/2] adjust layout --- src/components/Layout/Bread.js | 62 ++++++++++++++++++++++++ src/components/Layout/Bread.less | 8 ++++ src/components/Layout/Footer.js | 9 ++++ src/components/Layout/Footer.less | 12 +++++ src/components/Layout/Header.js | 63 ++++++++++++++++++++++++ src/components/Layout/Header.less | 57 ++++++++++++++++++++++ src/components/Layout/Layout.less | 67 +------------------------- src/components/Layout/Menu.js | 80 +++++++++++++++++++++++++++++++ src/components/Layout/Sider.js | 39 +++++++++++++++ src/components/Layout/Sider.less | 1 + src/components/layout/bread.js | 2 +- src/components/layout/footer.js | 2 +- src/components/layout/header.js | 34 +++++++------ src/components/layout/menu.js | 1 + 14 files changed, 354 insertions(+), 83 deletions(-) create mode 100644 src/components/Layout/Bread.js create mode 100644 src/components/Layout/Bread.less create mode 100644 src/components/Layout/Footer.js create mode 100644 src/components/Layout/Footer.less create mode 100644 src/components/Layout/Header.js create mode 100644 src/components/Layout/Header.less create mode 100644 src/components/Layout/Menu.js create mode 100644 src/components/Layout/Sider.js create mode 100644 src/components/Layout/Sider.less diff --git a/src/components/Layout/Bread.js b/src/components/Layout/Bread.js new file mode 100644 index 0000000..5db9e34 --- /dev/null +++ b/src/components/Layout/Bread.js @@ -0,0 +1,62 @@ +import React, { PropTypes } from 'react' +import { Breadcrumb, Icon } from 'antd' +import styles from './Bread.less' +import { menu } from '../../utils' + +let pathSet = [] +const getPathSet = function (menuArray, parentPath) { + parentPath = parentPath || '/' + menuArray.forEach(item => { + pathSet[(parentPath + item.key).replace(/\//g, '-').hyphenToHump()] = { + path: parentPath + item.key, + name: item.name, + icon: item.icon || '', + clickable: item.clickable === undefined, + } + if (item.child) { + getPathSet(item.child, `${parentPath}${item.key}/`) + } + }) +} +getPathSet(menu) + +function Bread ({ location }) { + let pathNames = [] + location.pathname.substr(1).split('/').forEach((item, key) => { + if (key > 0) { + pathNames.push((`${pathNames[key - 1]}-${item}`).hyphenToHump()) + } else { + pathNames.push((`-${item}`).hyphenToHump()) + } + }) + const breads = pathNames.map((item, key) => { + if (!(item in pathSet)) { + item = 'Dashboard' + } + return ( + + {pathSet[item].icon + ? + : ''} + {pathSet[item].name} + + ) + }) + + return ( +
+ + + 主页 + + {breads} + +
+ ) +} + +Bread.propTypes = { + location: PropTypes.object, +} + +export default Bread diff --git a/src/components/Layout/Bread.less b/src/components/Layout/Bread.less new file mode 100644 index 0000000..9c091e5 --- /dev/null +++ b/src/components/Layout/Bread.less @@ -0,0 +1,8 @@ +@import "../vars.less"; + +.bread { + height: 64px; + line-height: 64px; + padding: 0 24px; + margin-bottom: -24px; +} diff --git a/src/components/Layout/Footer.js b/src/components/Layout/Footer.js new file mode 100644 index 0000000..6e7f6bb --- /dev/null +++ b/src/components/Layout/Footer.js @@ -0,0 +1,9 @@ +import React from 'react' +import styles from './Footer.less' +import { config } from '../../utils' + +const Footer = () =>
+ {config.footerText} +
+ +export default Footer diff --git a/src/components/Layout/Footer.less b/src/components/Layout/Footer.less new file mode 100644 index 0000000..9f5a967 --- /dev/null +++ b/src/components/Layout/Footer.less @@ -0,0 +1,12 @@ +@import "../vars.less"; + +.footer { + height: 48px; + line-height: 48px; + text-align: center; + font-size: 12px; + color: #999; + background: #fff; + box-shadow: @shadow-2; + width: 100%; +} diff --git a/src/components/Layout/Header.js b/src/components/Layout/Header.js new file mode 100644 index 0000000..f778050 --- /dev/null +++ b/src/components/Layout/Header.js @@ -0,0 +1,63 @@ +import React, { PropTypes } from 'react' +import { Menu, Icon, Popover } from 'antd' +import styles from './Header.less' +import Menus from './Menu' + +const SubMenu = Menu.SubMenu + +function Header ({ user, logout, switchSider, siderFold, isNavbar, menuPopoverVisible, location, switchMenuPopover, navOpenKeys, changeOpenKeys }) { + let handleClickMenu = e => e.key === 'logout' && logout() + const menusProps = { + siderFold: false, + darkTheme: false, + isNavbar, + handleClickNavMenu: switchMenuPopover, + location, + navOpenKeys, + changeOpenKeys, + } + return ( +
+ {isNavbar + ? }> +
+ +
+
+ :
+ +
} +
+
+ +
+ + + {user.name} < /span>} + > + + 注销 + + + +
+
+ ) +} + +Header.propTypes = { + user: PropTypes.object, + logout: PropTypes.func, + switchSider: PropTypes.func, + siderFold: PropTypes.bool, + isNavbar: PropTypes.bool, + menuPopoverVisible: PropTypes.bool, + location: PropTypes.object, + switchMenuPopover: PropTypes.func, + navOpenKeys: PropTypes.array, + changeOpenKeys: PropTypes.func, +} + +export default Header diff --git a/src/components/Layout/Header.less b/src/components/Layout/Header.less new file mode 100644 index 0000000..1eca871 --- /dev/null +++ b/src/components/Layout/Header.less @@ -0,0 +1,57 @@ +@import "../vars.less"; + +.header { + :global .ant-menu-horizontal { + & > .ant-menu-submenu { + float: right; + } + border: none; + display: inline-block; + } + box-shadow: @shadow-2; + position: relative; + display: flex; + justify-content: space-between; + height: 47px; + background-color: #fff; + + .rightWarpper { + display: flex; + } + + .button { + width: 47px; + height: 47px; + line-height: 47px; + text-align: center; + font-size: 18px; + cursor: pointer; + transition: @transition-ease-in; + + &:hover { + color: @primary-color; + background-color: fade(@primary-color, 15%); + } + } +} + +.popovermenu { + width: 280px; + margin-left: 6px; + + :global .ant-popover-inner-content { + padding: 0; + + .ant-menu-inline .ant-menu-item, + .ant-menu-vertical .ant-menu-item { + border-right: 0; + height: 48px; + line-height: 48px; + } + + .ant-menu-inline .ant-menu-item-selected, + .ant-menu-inline .ant-menu-selected { + border-right: 0; + } + } +} diff --git a/src/components/Layout/Layout.less b/src/components/Layout/Layout.less index fcc402c..7c5e000 100644 --- a/src/components/Layout/Layout.less +++ b/src/components/Layout/Layout.less @@ -144,6 +144,7 @@ .ant-menu-root { height: ~"calc(100vh - 144px)"; overflow-x: hidden; + border-right: none; &::-webkit-scrollbar-thumb { background-color: transparent; @@ -183,40 +184,6 @@ } } - .header { - :global .ant-menu-horizontal { - & > .ant-menu-submenu { - float: right; - } - border: none; - } - box-shadow: @shadow-2; - position: relative; - - .siderbutton { - height: 47px; - width: 47px; - line-height: 47px; - text-align: center; - font-size: 18px; - cursor: pointer; - position: absolute; - transition: @transition-ease-in; - - &:hover { - color: @primary-color; - background-color: fade(@primary-color, 15%); - } - } - } - - .bread { - height: 64px; - line-height: 64px; - padding: 0 24px; - margin-bottom: -24px; - } - .main { margin-left: 224px; overflow: auto; @@ -241,17 +208,6 @@ position: relative; } } - - .footer { - height: 48px; - line-height: 48px; - text-align: center; - font-size: 12px; - color: #999; - background: #fff; - box-shadow: @shadow-2; - width: 100%; - } } :global .ant-menu-dark { @@ -306,27 +262,6 @@ } } -.popovermenu { - width: 280px; - margin-left: 6px; - - :global .ant-popover-inner-content { - padding: 0; - - .ant-menu-inline .ant-menu-item, - .ant-menu-vertical .ant-menu-item { - border-right: 0; - height: 48px; - line-height: 48px; - } - - .ant-menu-inline .ant-menu-item-selected, - .ant-menu-inline .ant-menu-selected { - border-right: 0; - } - } -} - .spin { :global .ant-spin-container { height: 100vh; diff --git a/src/components/Layout/Menu.js b/src/components/Layout/Menu.js new file mode 100644 index 0000000..378bfc2 --- /dev/null +++ b/src/components/Layout/Menu.js @@ -0,0 +1,80 @@ +import React, { PropTypes } from 'react' +import { Menu, Icon } from 'antd' +import { Link } from 'dva/router' +import { menu } from '../../utils' + +const topMenus = menu.map(item => item.key) +const getMenus = function (menuArray, siderFold, parentPath = '/') { + return menuArray.map(item => { + const linkTo = parentPath + item.key + if (item.child) { + return ( + {item.icon ? : ''}{siderFold && topMenus.indexOf(item.key) >= 0 ? '' : item.name}}> + {getMenus(item.child, siderFold, `${linkTo}/`)} + + ) + } + return ( + + + + {item.icon ? : ''} + {siderFold && topMenus.indexOf(item.key) >= 0 ? '' : item.name} + + + ) + }) +} + +function Menus ({ siderFold, darkTheme, location, handleClickNavMenu, navOpenKeys, changeOpenKeys }) { + const menuItems = getMenus(menu, siderFold) + + const getAncestorKeys = (key) => { + const map = { + '/navigation/navigation2': ['/navigation'], + } + return map[key] || [] + } + + const onOpenChange = (openKeys) => { + const latestOpenKey = openKeys.find(key => !(navOpenKeys.indexOf(key) > -1)) + const latestCloseKey = navOpenKeys.find(key => !(openKeys.indexOf(key) > -1)) + let nextOpenKeys = [] + if (latestOpenKey) { + nextOpenKeys = getAncestorKeys(latestOpenKey).concat(latestOpenKey) + } + if (latestCloseKey) { + nextOpenKeys = getAncestorKeys(latestCloseKey) + } + changeOpenKeys(nextOpenKeys) + } + // 菜单栏收起时,不能操作openKeys + let menuProps = !siderFold ? { + onOpenChange, + openKeys: navOpenKeys, + } : {} + + return ( + + {menuItems} + + ) +} + +Menus.propTypes = { + siderFold: PropTypes.bool, + darkTheme: PropTypes.bool, + location: PropTypes.object, + isNavbar: PropTypes.bool, + handleClickNavMenu: PropTypes.func, + navOpenKeys: PropTypes.array, + changeOpenKeys: PropTypes.func, +} + +export default Menus diff --git a/src/components/Layout/Sider.js b/src/components/Layout/Sider.js new file mode 100644 index 0000000..a143ef5 --- /dev/null +++ b/src/components/Layout/Sider.js @@ -0,0 +1,39 @@ +import React, { PropTypes } from 'react' +import { Icon, Switch } from 'antd' +import styles from './Layout.less' +import { config } from '../../utils' +import Menus from './Menu' + +function Sider ({ siderFold, darkTheme, location, changeTheme, navOpenKeys, changeOpenKeys }) { + const menusProps = { + siderFold, + darkTheme, + location, + navOpenKeys, + changeOpenKeys, + } + return ( +
+
+ {'logo'} + {siderFold ? '' : {config.logoText}} +
+ + {!siderFold ?
+ 切换主题 + +
: ''} +
+ ) +} + +Sider.propTypes = { + siderFold: PropTypes.bool, + darkTheme: PropTypes.bool, + location: PropTypes.object, + changeTheme: PropTypes.func, + navOpenKeys: PropTypes.array, + changeOpenKeys: PropTypes.func, +} + +export default Sider diff --git a/src/components/Layout/Sider.less b/src/components/Layout/Sider.less new file mode 100644 index 0000000..9146181 --- /dev/null +++ b/src/components/Layout/Sider.less @@ -0,0 +1 @@ +@import "../vars.less"; diff --git a/src/components/layout/bread.js b/src/components/layout/bread.js index 39a4b0f..5db9e34 100644 --- a/src/components/layout/bread.js +++ b/src/components/layout/bread.js @@ -1,6 +1,6 @@ import React, { PropTypes } from 'react' import { Breadcrumb, Icon } from 'antd' -import styles from './Layout.less' +import styles from './Bread.less' import { menu } from '../../utils' let pathSet = [] diff --git a/src/components/layout/footer.js b/src/components/layout/footer.js index 6dcc09f..6e7f6bb 100644 --- a/src/components/layout/footer.js +++ b/src/components/layout/footer.js @@ -1,5 +1,5 @@ import React from 'react' -import styles from './Layout.less' +import styles from './Footer.less' import { config } from '../../utils' const Footer = () =>
diff --git a/src/components/layout/header.js b/src/components/layout/header.js index 14e430d..f778050 100644 --- a/src/components/layout/header.js +++ b/src/components/layout/header.js @@ -1,6 +1,6 @@ import React, { PropTypes } from 'react' import { Menu, Icon, Popover } from 'antd' -import styles from './Layout.less' +import styles from './Header.less' import Menus from './Menu' const SubMenu = Menu.SubMenu @@ -20,25 +20,29 @@ function Header ({ user, logout, switchSider, siderFold, isNavbar, menuPopoverVi
{isNavbar ? }> -
+
- :
+ :
} - - - - {user.name} < /span>} - > - - 注销 - - - +
+
+ +
+ + + {user.name} < /span>} + > + + 注销 + + + +
) } diff --git a/src/components/layout/menu.js b/src/components/layout/menu.js index f5eeac4..378bfc2 100644 --- a/src/components/layout/menu.js +++ b/src/components/layout/menu.js @@ -16,6 +16,7 @@ const getMenus = function (menuArray, siderFold, parentPath = '/') { } return ( + {item.icon ? : ''} {siderFold && topMenus.indexOf(item.key) >= 0 ? '' : item.name} From 4425a289e7897debb1687094e1871123b8fcde97 Mon Sep 17 00:00:00 2001 From: zuiidea Date: Fri, 24 Mar 2017 11:14:49 +0800 Subject: [PATCH 2/2] switch requst form robe-ajax to axios --- README.md | 2 +- mock/app.js | 2 +- mock/users.js | 6 +++--- package.json | 2 +- proxy.config.js | 2 +- src/services/app.js | 9 ++++++--- src/services/dashboard.js | 13 ++++++------- src/services/users.js | 12 ++++++++---- src/utils/mock.js | 2 +- src/utils/request.js | 24 ++++++++---------------- 10 files changed, 36 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 794955d..22871f1 100644 --- a/README.md +++ b/README.md @@ -114,7 +114,7 @@ npm run lint - 如需重写antd样式配置,请修改`src/theme.js` - 项目配置文件在`src/utils/config.js` - 如需重写异步请求函数,请修改`src/utils/request.js` - (关于为什么使用robe-ajax而不是fetch:在一个无服务器的环境中模拟数据请求,[Mock](https://github.com/nuysoft/Mock)不能拦截Fetch,只能拦截XHR,所以我选了一个类似jquery Ajax的库robe-ajax) + (关于为什么使用axios而不是fetch:在一个无服务器的环境中模拟数据请求,[Mock](https://github.com/nuysoft/Mock)不能拦截Fetch) ## 参考 diff --git a/mock/app.js b/mock/app.js index 974989d..c4e675d 100644 --- a/mock/app.js +++ b/mock/app.js @@ -14,7 +14,7 @@ let dataKey = mockStorge('AdminUsers', [ module.exports = { 'POST /api/login' (req, res) { - const userItem = req.body + const userItem = JSON.parse(req.body) const response = { success: false, message: '', diff --git a/mock/users.js b/mock/users.js index 4388afa..eecdebf 100644 --- a/mock/users.js +++ b/mock/users.js @@ -59,7 +59,7 @@ module.exports = { }, 'POST /api/users' (req, res) { - const newData = req.body + const newData = JSON.parse(req.body) newData.createTime = Mock.mock('@now') newData.avatar = Mock.Random.image('100x100', Mock.Random.color(), '#757575', 'png', newData.nickName.substr(0, 1)) @@ -75,7 +75,7 @@ module.exports = { }, 'DELETE /api/users' (req, res) { - const deleteItem = req.body + const deleteItem = JSON.parse(req.body) usersListData.data = usersListData.data.filter((item) => { if (item.id === deleteItem.id) { @@ -92,7 +92,7 @@ module.exports = { }, 'PUT /api/users' (req, res) { - const editItem = req.body + const editItem = JSON.parse(req.body) editItem.createTime = Mock.mock('@now') editItem.avatar = Mock.Random.image('100x100', Mock.Random.color(), '#757575', 'png', editItem.nickName.substr(0, 1)) diff --git a/package.json b/package.json index 831f120..770f9cc 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ }, "devDependencies": { "atool-build": "^0.7.6", + "axios": "^0.15.3", "babel-eslint": "^6.0.4", "babel-plugin-dev-expression": "^0.2.1", "babel-plugin-dva-hmr": "^0.1.0", @@ -43,7 +44,6 @@ "mockjs": "^1.0.1-beta3", "rc-tween-one": "^1.0.0", "redbox-react": "^1.2.10", - "robe-ajax": "^1.0.0", "watchjs": "^0.0.0" }, "pre-commit": [ diff --git a/proxy.config.js b/proxy.config.js index 3665129..569b217 100644 --- a/proxy.config.js +++ b/proxy.config.js @@ -1,7 +1,7 @@ const mock = {} require('fs').readdirSync(require('path').join(`${__dirname}/mock`)).forEach((file) => { - Object.assign(mock, require('./mock/' + file)) + Object.assign(mock, require(`./mock/${file}`)) }) module.exports = mock diff --git a/src/services/app.js b/src/services/app.js index b0c19d1..20f0eeb 100644 --- a/src/services/app.js +++ b/src/services/app.js @@ -1,21 +1,24 @@ import { request } from '../utils' export async function login (params) { - return request('/api/login', { + return request({ + url: '/api/login', method: 'post', data: params, }) } export async function logout (params) { - return request('/api/logout', { + return request({ + url: '/api/logout', method: 'post', data: params, }) } export async function userInfo (params) { - return request('/api/userInfo', { + return request({ + url: '/api/userInfo', method: 'get', data: params, }) diff --git a/src/services/dashboard.js b/src/services/dashboard.js index bd5e34f..2756027 100644 --- a/src/services/dashboard.js +++ b/src/services/dashboard.js @@ -1,23 +1,22 @@ import { request } from '../utils' export async function myCity (params) { - return request('http://www.zuimeitianqi.com/zuimei/myCity', { - method: 'get', - cross: true, + return request({ + url: 'http://www.zuimeitianqi.com/zuimei/myCity', data: params, }) } export async function queryWeather (params) { - return request('http://www.zuimeitianqi.com/zuimei/queryWeather', { - method: 'get', - cross: true, + return request({ + url: 'http://www.zuimeitianqi.com/zuimei/queryWeather', data: params, }) } export async function query (params) { - return request('/api/dashboard', { + return request({ + url: '/api/dashboard', method: 'get', data: params, }) diff --git a/src/services/users.js b/src/services/users.js index 2460eb1..960e6b7 100644 --- a/src/services/users.js +++ b/src/services/users.js @@ -1,28 +1,32 @@ import { request } from '../utils' export async function query (params) { - return request('/api/users', { + return request({ + url: '/api/users', method: 'get', data: params, }) } export async function create (params) { - return request('/api/users', { + return request({ + url: '/api/users', method: 'post', data: params, }) } export async function remove (params) { - return request('/api/users', { + return request({ + url: '/api/users', method: 'delete', data: params, }) } export async function update (params) { - return request('/api/users', { + return request({ + url: '/api/users', method: 'put', data: params, }) diff --git a/src/utils/mock.js b/src/utils/mock.js index 385e30b..197765b 100644 --- a/src/utils/mock.js +++ b/src/utils/mock.js @@ -24,7 +24,7 @@ for (let i in mockData) { if (options.url.split('?')[1]) { options.query = serialize(options.url.split('?')[1]) } else { - options.query = options.body ? serialize(options.body) : {} + options.query = options.body ? JSON.parse(options.body) : {} } } let res = {} diff --git a/src/utils/request.js b/src/utils/request.js index febe924..98f82de 100644 --- a/src/utils/request.js +++ b/src/utils/request.js @@ -1,20 +1,12 @@ -import Ajax from 'robe-ajax' +import axios from 'axios' +import qs from 'qs' -export default function request (url, options) { - if (options.cross) { - return Ajax.getJSON('http://query.yahooapis.com/v1/public/yql', { - q: `select * from json where url='${url}?${Ajax.param(options.data)}'`, - format: 'json', - }) +export default function request (options) { + if (options.url.indexOf('//') > -1 && window.location.origin !== `${options.url.split('//')[0]}//${options.url.split('//')[1].split('/')[0]}`) { + options.url = `http://query.yahooapis.com/v1/public/yql?q=select * from json where url='${options.url}?${qs.stringify(options.data)}'&format=json` + delete options.data } - const { method = 'get' } = options - return Ajax.ajax({ - url, - method, - data: options.data || {}, - processData: method === 'get', - dataType: 'JSON', - }).done((data) => { - return data + return axios(options).then((result) => { + return result.data }) }