diff --git a/src/components/DataTable/DataTable.js b/src/components/DataTable/DataTable.js index 23eaf4a..597c424 100644 --- a/src/components/DataTable/DataTable.js +++ b/src/components/DataTable/DataTable.js @@ -1,7 +1,7 @@ import React from 'react' import PropTypes from 'prop-types' import { Table } from 'antd' -import { request } from '../../utils' +import { request } from 'utils' import lodash from 'lodash' import './DataTable.less' diff --git a/src/components/Layout/Bread.js b/src/components/Layout/Bread.js index 7566c54..21aa00f 100644 --- a/src/components/Layout/Bread.js +++ b/src/components/Layout/Bread.js @@ -4,7 +4,7 @@ import { Breadcrumb, Icon } from 'antd' import { Link } from 'dva/router' import styles from './Bread.less' import pathToRegexp from 'path-to-regexp' -import { queryArray } from '../../utils' +import { queryArray } from 'utils' const Bread = ({ menu }) => { // 匹配当前路由 @@ -25,7 +25,11 @@ const Bread = ({ menu }) => { } if (!current) { - pathArray.push(menu[0]) + pathArray.push(menu[0] || { + id: 1, + icon: 'laptop', + name: 'Dashboard', + }) pathArray.push({ id: 404, name: 'Not Found', diff --git a/src/components/Layout/Footer.js b/src/components/Layout/Footer.js index 6e7f6bb..dd754c0 100644 --- a/src/components/Layout/Footer.js +++ b/src/components/Layout/Footer.js @@ -1,6 +1,6 @@ import React from 'react' import styles from './Footer.less' -import { config } from '../../utils' +import { config } from 'utils' const Footer = () =>
{config.footerText} diff --git a/src/components/Layout/Header.less b/src/components/Layout/Header.less index 5e5f7f6..b984cd3 100644 --- a/src/components/Layout/Header.less +++ b/src/components/Layout/Header.less @@ -14,6 +14,7 @@ justify-content: space-between; height: 47px; background-color: #fff; + z-index: 999; .rightWarpper { display: flex; diff --git a/src/components/Layout/Menu.js b/src/components/Layout/Menu.js index a960a0e..bbc31d6 100644 --- a/src/components/Layout/Menu.js +++ b/src/components/Layout/Menu.js @@ -2,12 +2,12 @@ import React from 'react' import PropTypes from 'prop-types' import { Menu, Icon } from 'antd' import { Link } from 'dva/router' -import { arrayToTree, queryArray } from '../../utils' +import { arrayToTree, queryArray } from 'utils' import pathToRegexp from 'path-to-regexp' -const Menus = ({ siderFold, darkTheme, location, handleClickNavMenu, navOpenKeys, changeOpenKeys, menu }) => { +const Menus = ({ siderFold, darkTheme, handleClickNavMenu, navOpenKeys, changeOpenKeys, menu }) => { // 生成树状 - const menuTree = arrayToTree(menu.filter(_ => _.mpid !== -1), 'id', 'mpid') + const menuTree = arrayToTree(menu.filter(_ => _.mpid !== '-1'), 'id', 'mpid') const levelMap = {} // 递归生成菜单 @@ -22,7 +22,7 @@ const Menus = ({ siderFold, darkTheme, location, handleClickNavMenu, navOpenKeys key={item.id} title={ {item.icon && } - {(!siderFoldN || menuTree.indexOf(item) < 0) && item.name} + {(!siderFoldN || !menuTree.includes(item)) && item.name} } > {getMenus(item.children, siderFoldN)} @@ -33,7 +33,7 @@ const Menus = ({ siderFold, darkTheme, location, handleClickNavMenu, navOpenKeys {item.icon && } - {(!siderFoldN || menuTree.indexOf(item) < 0) && item.name} + {(!siderFoldN || !menuTree.includes(item)) && item.name} ) @@ -60,8 +60,8 @@ const Menus = ({ siderFold, darkTheme, location, handleClickNavMenu, navOpenKeys } const onOpenChange = (openKeys) => { - const latestOpenKey = openKeys.find(key => !(navOpenKeys.indexOf(key) > -1)) - const latestCloseKey = navOpenKeys.find(key => !(openKeys.indexOf(key) > -1)) + const latestOpenKey = openKeys.find(key => !navOpenKeys.includes(key)) + const latestCloseKey = navOpenKeys.find(key => !openKeys.includes(key)) let nextOpenKeys = [] if (latestOpenKey) { nextOpenKeys = getAncestorKeys(latestOpenKey).concat(latestOpenKey) @@ -120,7 +120,6 @@ Menus.propTypes = { menu: PropTypes.array, siderFold: PropTypes.bool, darkTheme: PropTypes.bool, - location: PropTypes.object, isNavbar: PropTypes.bool, handleClickNavMenu: PropTypes.func, navOpenKeys: PropTypes.array, diff --git a/src/components/Layout/Sider.js b/src/components/Layout/Sider.js index d60834d..22ec010 100644 --- a/src/components/Layout/Sider.js +++ b/src/components/Layout/Sider.js @@ -2,7 +2,7 @@ import React from 'react' import PropTypes from 'prop-types' import { Icon, Switch } from 'antd' import styles from './Layout.less' -import { config } from '../../utils' +import { config } from 'utils' import Menus from './Menu' const Sider = ({ siderFold, darkTheme, location, changeTheme, navOpenKeys, changeOpenKeys, menu }) => { diff --git a/src/components/Loader/Loader.js b/src/components/Loader/Loader.js new file mode 100644 index 0000000..fe24a19 --- /dev/null +++ b/src/components/Loader/Loader.js @@ -0,0 +1,20 @@ +import React from 'react' +import PropTypes from 'prop-types' +import classNames from 'classnames' +import styles from './Loader.less' + +const Loader = ({ spinning }) => { + return (
+
+
+
LOADING
+
+
) +} + + +Loader.propTypes = { + spinning: PropTypes.bool, +} + +export default Loader diff --git a/src/components/Loader/Loader.less b/src/components/Loader/Loader.less new file mode 100644 index 0000000..ba3685a --- /dev/null +++ b/src/components/Loader/Loader.less @@ -0,0 +1,64 @@ +.loader { + display: block; + background-color: #fff; + width: 100%; + position: fixed; + top: 0; + bottom: 0; + left: 0; + z-index: 100000; + display: flex; + justify-content: center; + align-items: center; + opacity: 1; + text-align: center; + + .warpper { + width: 100px; + height: 100px; + display: inline-flex; + flex-direction: column; + justify-content: space-around; + } + + .inner { + width: 48px; + height: 48px; + margin: 0 auto; + text-indent: -12345px; + border-top: 1px solid rgba(0, 0, 0, 0.08); + border-right: 1px solid rgba(0, 0, 0, 0.08); + border-bottom: 1px solid rgba(0, 0, 0, 0.08); + border-left: 1px solid rgba(0, 0, 0, 0.7); + border-radius: 50%; + z-index: 100001; + + :local { + animation: spinner 600ms infinite linear; + } + } + + .text { + width: 100px; + height: 20px; + text-align: center; + font-size: 12px; + letter-spacing: 4px; + color: #000; + } + + &.hidden { + z-index: -1; + opacity: 0; + transition: opacity 1s ease 0.5s, z-index 0.1s ease 1.5s; + } +} +@keyframes spinner { + 0% { + transform: rotate(0deg); + } + + 100% { + transform: rotate(360deg); + } +} diff --git a/src/components/Loader/package.json b/src/components/Loader/package.json new file mode 100644 index 0000000..15bde9a --- /dev/null +++ b/src/components/Loader/package.json @@ -0,0 +1,6 @@ +{ + "name": "Loader", + "version": "0.0.0", + "private": true, + "main": "./Loader.js" +} diff --git a/src/components/index.js b/src/components/index.js index 8b04fb6..acc990e 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -4,6 +4,7 @@ import Iconfont from './Iconfont' import Search from './Search' import Editor from './Editor' import FilterItem from './FilterItem' +import Loader from './Loader' import * as Layout from './Layout/index.js' import layer from './layer' @@ -16,5 +17,6 @@ export { Search, Editor, FilterItem, + Loader, layer, } diff --git a/src/mock/menu.js b/src/mock/menu.js new file mode 100644 index 0000000..c839d84 --- /dev/null +++ b/src/mock/menu.js @@ -0,0 +1,164 @@ +const { config, posts } = require('./common') + +const { apiPrefix } = config +let database = [ + { + id: '1', + icon: 'laptop', + name: 'Dashboard', + router: '/dashboard', + }, + { + id: '2', + bpid: '1', + name: 'Users', + icon: 'user', + router: '/user', + }, + { + id: '7', + bpid: '1', + name: 'Posts', + icon: 'shopping-cart', + router: '/post', + }, + { + id: '21', + mpid: '-1', + bpid: '2', + name: 'User Detail', + router: '/user/:id', + }, + { + id: '3', + bpid: '1', + name: 'Request', + icon: 'api', + router: '/request', + }, + { + id: '4', + bpid: '1', + name: 'UI Element', + icon: 'camera-o', + }, + { + id: '41', + bpid: '4', + mpid: '4', + name: 'IconFont', + icon: 'heart-o', + router: '/UIElement/iconfont', + }, + { + id: '42', + bpid: '4', + mpid: '4', + name: 'DataTable', + icon: 'database', + router: '/UIElement/dataTable', + }, + { + id: '43', + bpid: '4', + mpid: '4', + name: 'DropOption', + icon: 'bars', + router: '/UIElement/dropOption', + }, + { + id: '44', + bpid: '4', + mpid: '4', + name: 'Search', + icon: 'search', + router: '/UIElement/search', + }, + { + id: '45', + bpid: '4', + mpid: '4', + name: 'Editor', + icon: 'edit', + router: '/UIElement/editor', + }, + { + id: '46', + bpid: '4', + mpid: '4', + name: 'layer (Function)', + icon: 'credit-card', + router: '/UIElement/layer', + }, + { + id: '5', + bpid: '1', + name: 'Recharts', + icon: 'code-o', + }, + { + id: '51', + bpid: '5', + mpid: '5', + name: 'LineChart', + icon: 'line-chart', + router: '/chart/lineChart', + }, + { + id: '52', + bpid: '5', + mpid: '5', + name: 'BarChart', + icon: 'bar-chart', + router: '/chart/barChart', + }, + { + id: '53', + bpid: '5', + mpid: '5', + name: 'AreaChart', + icon: 'area-chart', + router: '/chart/areaChart', + }, + { + id: '6', + bpid: '1', + name: 'Test Navigation', + icon: 'setting', + }, + { + id: '61', + bpid: '6', + mpid: '6', + name: 'Test Navigation1', + router: '/navigation/navigation1', + }, + { + id: '62', + bpid: '6', + mpid: '6', + name: 'Test Navigation2', + router: '/navigation/navigation2', + }, + { + id: '621', + bpid: '62', + mpid: '62', + name: 'Test Navigation21', + router: '/navigation/navigation2/navigation1', + }, + { + id: '622', + bpid: '62', + mpid: '62', + name: 'Test Navigation22', + router: '/navigation/navigation2/navigation2', + }, +] + +module.exports = { + + [`GET ${apiPrefix}/menus`] (req, res) { + res.status(200).json(database) + }, +} diff --git a/src/mock/user.js b/src/mock/user.js index 8f31411..21f7d08 100644 --- a/src/mock/user.js +++ b/src/mock/user.js @@ -26,13 +26,15 @@ let usersListData = Mock.mock({ let database = usersListData.data const userPermission = { - DEFAULT: [ - 'dashboard', 'chart', - ], - ADMIN: [ - 'dashboard', 'users', 'UIElement', 'UIElementIconfont', 'chart', - ], - DEVELOPER: ['dashboard', 'users', 'UIElement', 'UIElementIconfont', 'chart'], + DEFAULT: { + visit: ['1', '2', '21', '7', '5', '51', '52', '53'], + }, + ADMIN: { + visit: ['1', '2', '7', '21', '3', '4', '41', '42', '43', '44', '45', '46', '5', '51', '52', '53', '6', '61', '62', '621', '622'], + }, + DEVELOPER: { + visit: ['1', '2', '7', '21', '3', '4', '41', '42', '43', '44', '45', '46', '5', '51', '52', '53', '6', '61', '62', '621', '622'], + }, } const adminUsers = [ diff --git a/src/models/app.js b/src/models/app.js index 57cf51a..fe0221c 100644 --- a/src/models/app.js +++ b/src/models/app.js @@ -1,13 +1,25 @@ import { query, logout } from '../services/app' +import * as menusService from '../services/menus' import { routerRedux } from 'dva/router' import { parse } from 'qs' -import { config } from '../utils' +import { config } from 'utils' const { prefix } = config export default { namespace: 'app', state: { user: {}, + permissions: { + visit: [], + }, + menu: [ + { + id: 1, + icon: 'laptop', + name: 'Dashboard', + router: '/dashboard', + }, + ], menuPopoverVisible: false, siderFold: localStorage.getItem(`${prefix}siderFold`) === 'true', darkTheme: localStorage.getItem(`${prefix}darkTheme`) === 'true', @@ -33,11 +45,25 @@ export default { *query ({ payload, }, { call, put }) { - const data = yield call(query, parse(payload)) - if (data.success && data.user) { + const { success, user } = yield call(query, payload) + if (success && user) { + const { list } = yield call(menusService.query) + const { permissions } = user yield put({ - type: 'querySuccess', - payload: data.user, + type: 'updateState', + payload: { + user, + permissions, + menu: list.filter(item => { + const cases = [ + permissions.visit.includes(item.id), + item.mpid ? permissions.visit.includes(item.mpid) || item.mpid === '-1' : true, + item.bpid ? permissions.visit.includes(item.bpid) : true, + ] + return cases.every(_ => _) + } + ), + }, }) if (location.pathname === '/login') { yield put(routerRedux.push('/dashboard')) @@ -73,10 +99,10 @@ export default { }, reducers: { - querySuccess (state, { payload: user }) { + updateState (state, { payload }) { return { ...state, - user, + ...payload, } }, diff --git a/src/models/login.js b/src/models/login.js index d47a4a8..e9137ea 100644 --- a/src/models/login.js +++ b/src/models/login.js @@ -1,6 +1,6 @@ import { login } from '../services/login' import { routerRedux } from 'dva/router' -import { queryURL } from '../utils' +import { queryURL } from 'utils' export default { namespace: 'login', diff --git a/src/models/user.js b/src/models/user.js index ef00f70..1909179 100644 --- a/src/models/user.js +++ b/src/models/user.js @@ -2,7 +2,7 @@ import modelExtend from 'dva-model-extend' import { create, remove, update } from '../services/user' import * as usersService from '../services/users' import { pageModel } from './common' -import { config } from '../utils' +import { config } from 'utils' const { query } = usersService const { prefix } = config diff --git a/src/routes/UIElement/dataTable/index.js b/src/routes/UIElement/dataTable/index.js index d4eb9b1..a0d11df 100644 --- a/src/routes/UIElement/dataTable/index.js +++ b/src/routes/UIElement/dataTable/index.js @@ -1,5 +1,5 @@ import React from 'react' -import { DataTable } from '../../../components' +import { DataTable } from 'components' import { Table, Row, Col, Card, Select } from 'antd' class DataTablePage extends React.Component { diff --git a/src/routes/UIElement/dropOption/index.js b/src/routes/UIElement/dropOption/index.js index f4bb50a..d76931b 100644 --- a/src/routes/UIElement/dropOption/index.js +++ b/src/routes/UIElement/dropOption/index.js @@ -1,5 +1,5 @@ import React from 'react' -import { DropOption } from '../../../components' +import { DropOption } from 'components' import { Table, Row, Col, Card, message } from 'antd' const DropOptionPage = () =>
diff --git a/src/routes/UIElement/editor/index.js b/src/routes/UIElement/editor/index.js index 9102622..f95d48a 100644 --- a/src/routes/UIElement/editor/index.js +++ b/src/routes/UIElement/editor/index.js @@ -1,5 +1,5 @@ import React from 'react' -import { Editor } from '../../../components' +import { Editor } from 'components' import { convertToRaw } from 'draft-js' import { Row, Col, Card } from 'antd' import draftToHtml from 'draftjs-to-html' diff --git a/src/routes/UIElement/iconfont/index.js b/src/routes/UIElement/iconfont/index.js index e15ff0c..386f9a0 100644 --- a/src/routes/UIElement/iconfont/index.js +++ b/src/routes/UIElement/iconfont/index.js @@ -1,5 +1,5 @@ import React from 'react' -import { Iconfont } from '../../../components' +import { Iconfont } from 'components' import { Table, Row, Col, Icon } from 'antd' import styles from './index.less' import './emoji.js' diff --git a/src/routes/UIElement/layer/index.js b/src/routes/UIElement/layer/index.js index 6f4e5a6..9388ad2 100644 --- a/src/routes/UIElement/layer/index.js +++ b/src/routes/UIElement/layer/index.js @@ -1,5 +1,5 @@ import React from 'react' -import { layer } from '../../../components' +import { layer } from 'components' import { Table, Row, Col, Button, Card } from 'antd' let Enum = { diff --git a/src/routes/UIElement/search/index.js b/src/routes/UIElement/search/index.js index bce70b9..be78cf5 100644 --- a/src/routes/UIElement/search/index.js +++ b/src/routes/UIElement/search/index.js @@ -1,5 +1,5 @@ import React from 'react' -import { Search } from '../../../components' +import { Search } from 'components' import { Table, Row, Col, Card } from 'antd' const SearchPage = () =>
diff --git a/src/routes/app.js b/src/routes/app.js index a152549..087c5bb 100644 --- a/src/routes/app.js +++ b/src/routes/app.js @@ -1,19 +1,26 @@ import React from 'react' import PropTypes from 'prop-types' +import pathToRegexp from 'path-to-regexp' import { connect } from 'dva' -import { Layout } from '../components' -import { classnames, config, menu } from '../utils' +import { Layout, Loader } from 'components' +import { classnames, config } from 'utils' import { Helmet } from 'react-helmet' import '../themes/index.less' import './app.less' import NProgress from 'nprogress' -const { prefix } = config +import Error from './error' +const { prefix, openPages } = config const { Header, Bread, Footer, Sider, styles } = Layout let lastHref -const App = ({ children, location, dispatch, app, loading }) => { - const { user, siderFold, darkTheme, isNavbar, menuPopoverVisible, navOpenKeys } = app +const App = ({ children, dispatch, app, loading, location }) => { + const { user, siderFold, darkTheme, isNavbar, menuPopoverVisible, navOpenKeys, menu, permissions } = app + let { pathname } = location + pathname = pathname.startsWith('/') ? pathname : `/${pathname}` + const { iconFontJS, iconFontCSS, logo } = config + const current = menu.filter(item => pathToRegexp(item.router || '').exec(pathname)) + const hasPermission = current.length ? permissions.visit.includes(current[0].id) : false const href = window.location.href if (lastHref !== href) { @@ -28,7 +35,6 @@ const App = ({ children, location, dispatch, app, loading }) => { menu, user, siderFold, - location, isNavbar, menuPopoverVisible, navOpenKeys, @@ -50,7 +56,6 @@ const App = ({ children, location, dispatch, app, loading }) => { menu, siderFold, darkTheme, - location, navOpenKeys, changeTheme () { dispatch({ type: 'app/switchTheme' }) @@ -64,13 +69,12 @@ const App = ({ children, location, dispatch, app, loading }) => { const breadProps = { menu, } - - if (config.openPages && config.openPages.indexOf(location.pathname) > -1) { - return
{children}
+ if (openPages && openPages.includes(pathname)) { + return (
+ + {children} +
) } - - const { iconFontJS, iconFontCSS, logo } = config - return (
@@ -86,10 +90,10 @@ const App = ({ children, location, dispatch, app, loading }) => { : ''}
- +
- {children} + {hasPermission ? children : }