-
-
+
+
+
-
-
-
- )
+
+ )
+ }
}
Header.propTypes = {
- menuList: PropTypes.array,
+ menus: PropTypes.array,
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,
+ collapsed: PropTypes.bool,
+ onSignOut: PropTypes.func,
+ onCollapseChange: PropTypes.func,
}
export default Header
diff --git a/src/components/Layout/Header.less b/src/components/Layout/Header.less
index 707dc1e..1917977 100644
--- a/src/components/Layout/Header.less
+++ b/src/components/Layout/Header.less
@@ -1,6 +1,7 @@
@import '~themes/vars.less';
.header {
+ padding: 0;
:global {
.ant-menu-submenu-title {
height: 56px;
@@ -50,9 +51,8 @@
align-items: center;
background-color: #fff;
- .rightWarpper {
+ .rightContainer {
display: flex;
- padding-right: 16px;
}
.button {
diff --git a/src/components/Layout/Layout.less b/src/components/Layout/Layout.less
deleted file mode 100644
index 70a1135..0000000
--- a/src/components/Layout/Layout.less
+++ /dev/null
@@ -1,189 +0,0 @@
-@import '~themes/vars.less';
-
-:global {
- .ant-layout-header {
- padding: 0;
- }
-
- .ant-layout-sider {
- transition: all 0.3s;
-
- .ant-menu-root {
- height: ~'calc(100vh - 144px)';
- overflow-x: hidden;
- border-right: none;
-
- &::-webkit-scrollbar-thumb {
- background-color: transparent;
- }
-
- &:hover {
- &::-webkit-scrollbar-thumb {
- background-color: rgba(0, 0, 0, 0.2);
- }
- }
- }
-
- .ant-menu {
- .ant-menu-item,
- .ant-menu-submenu-title {
- overflow: hidden;
- white-space: normal;
- }
- }
- }
-
- .ant-layout-content {
- padding: 24px;
- }
-
- .ant-layout-footer {
- text-align: center;
- padding: 0 24px;
- height: 40px;
- font-size: 12px;
- line-height: 40px;
- }
-
- .ant-back-top {
- right: 50px;
- }
-
- .ant-switch.ant-switch-large {
- line-height: 24px;
- height: 26px;
-
- &:after,
- &:before {
- width: 22px;
- height: 22px;
- }
- }
-
- .flex-vertical-center {
- display: flex;
- align-items: center;
- }
-
- .ant-form-item {
- margin-bottom: 12px;
- }
-}
-
-.logo {
- height: 96px;
- display: flex;
- align-items: center;
- justify-content: center;
-
- img {
- width: 40px;
- margin-right: 8px;
- }
-
- span {
- vertical-align: text-bottom;
- font-size: 16px;
- text-transform: uppercase;
- display: inline-block;
- font-weight: 700;
- color: @primary-color;
- white-space: nowrap;
- :local {
- animation: fadeLeftIn 500ms linear;
- animation-fill-mode: both;
- }
- }
-}
-
-.sidemenu {
- :global {
- .ant-menu-item {
- width: 100%;
- }
- }
-}
-
-.switchtheme {
- width: 100%;
- position: absolute;
- bottom: 0;
- height: 48px;
- background-color: #fff;
- border-top: solid 1px #f8f8f8;
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 0 16px;
- overflow: hidden;
- z-index: 9;
- transition: all 0.3s;
-
- span {
- white-space: nowrap;
- overflow: hidden;
- font-size: 12px;
- }
-
- :global {
- .anticon {
- min-width: 14px;
- margin-right: 4px;
- font-size: 14px;
- }
- }
-}
-
-.bread {
- margin-bottom: 24px;
-
- :global {
- .ant-breadcrumb {
- display: flex;
- align-items: center;
- }
- }
-}
-
-.dark {
- .switchtheme {
- background-color: #000d18;
- border-color: #001629;
- }
-}
-
-.light {
- :global {
- .ant-layout-sider {
- background: #fff;
- }
- }
-}
-@media (max-width: 767px) {
- .bread {
- margin-bottom: 12px;
- }
-
- :global {
- .ant-layout-content {
- padding: 12px;
- }
-
- .ant-back-top {
- right: 20px;
- bottom: 20px;
- }
- }
-}
-
-@keyframes fadeLeftIn {
- 0% {
- transform: translateX(5px);
- opacity: 0;
- }
-
- 100% {
- transform: translateX(0);
- opacity: 1;
- }
-}
diff --git a/src/components/Layout/Menu.js b/src/components/Layout/Menu.js
index d85ea53..e3da460 100644
--- a/src/components/Layout/Menu.js
+++ b/src/components/Layout/Menu.js
@@ -1,167 +1,106 @@
-import React, { PureComponent } from 'react'
+import React, { PureComponent, Fragment } from 'react'
import PropTypes from 'prop-types'
import { Menu, Icon } from 'antd'
import Navlink from 'umi/navlink'
-import { arrayToTree, queryArray, pathMatchRegexp, addLangPrefix } from 'utils'
-import styles from './Layout.less'
+import withRouter from 'umi/withRouter'
+import {
+ arrayToTree,
+ queryAncestors,
+ pathMatchRegexp,
+ addLangPrefix,
+} from 'utils'
+import store from 'store'
const { SubMenu } = Menu
-let openKeysFlag = false
+
+@withRouter
class Menus extends PureComponent {
- render() {
- const {
- siderFold,
- darkTheme,
- navOpenKeys,
- changeOpenKeys,
- menuList,
- location,
- } = this.props
- // 生成树状
- const menuTree = arrayToTree(
- menuList.filter(_ => _.menuParentId !== '-1'),
- 'id',
- 'menuParentId'
+ state = {
+ openKeys: store.get('openKeys') || [],
+ }
+
+ onOpenChange = openKeys => {
+ const { menus } = this.props
+ const rootSubmenuKeys = menus.filter(_ => !_.menuParentId).map(_ => _.id)
+
+ const latestOpenKey = openKeys.find(
+ key => this.state.openKeys.indexOf(key) === -1
)
- const levelMap = {}
- // 递归生成菜单
- const getMenus = (menuTreeN, siderFoldN) => {
- return menuTreeN.map(item => {
- if (item.children) {
- if (item.menuParentId) {
- levelMap[item.id] = item.menuParentId
- }
- return (
-
- {item.icon && }
- {(!siderFoldN || !menuTree.includes(item)) && item.name}
-
- }
- >
- {getMenus(item.children, siderFoldN)}
-
- )
- }
+ let newOpenKeys = openKeys
+ if (rootSubmenuKeys.indexOf(latestOpenKey) !== -1) {
+ newOpenKeys = latestOpenKey ? [latestOpenKey] : []
+ }
+
+ this.setState({
+ openKeys: newOpenKeys,
+ })
+ store.set('openKeys', newOpenKeys)
+ }
+
+ generateMenus = data => {
+ return data.map(item => {
+ if (item.children) {
return (
-
-
- {item.icon && }
- {item.name}
-
-
+
+ {item.icon && }
+ {item.name}
+
+ }
+ >
+ {this.generateMenus(item.children)}
+
)
- })
- }
- const menuItems = getMenus(menuTree, siderFold)
-
- // 保持选中
- const getAncestorKeys = key => {
- let map = {}
- const getParent = index => {
- const result = [String(levelMap[index])]
- if (levelMap[result[0]]) {
- result.unshift(getParent(result[0])[0])
- }
- return result
}
- for (let index in levelMap) {
- if ({}.hasOwnProperty.call(levelMap, index)) {
- map[index] = getParent(index)
- }
- }
- return map[key] || []
- }
-
- const onOpenChange = openKeys => {
- if (navOpenKeys.length) {
- changeOpenKeys([])
- openKeysFlag = true
- }
- 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)
- }
- if (latestCloseKey) {
- nextOpenKeys = getAncestorKeys(latestCloseKey)
- }
- changeOpenKeys(nextOpenKeys)
- }
-
- let menuProps = !siderFold
- ? {
- onOpenChange,
- openKeys: navOpenKeys,
- }
- : {}
-
- // 寻找选中路由
- let currentMenu
- let defaultSelectedKeys
- for (let item of menuList) {
- if (item.route && pathMatchRegexp(item.route, location.pathname)) {
- if (!navOpenKeys.length && item.menuParentId && !openKeysFlag)
- changeOpenKeys([String(item.menuParentId)])
- currentMenu = item
- break
- }
- }
- const getPathArray = (array, current, pid, id) => {
- let result = [String(current[id])]
- const getPath = item => {
- if (item && item[pid]) {
- if (item[pid] === '-1') {
- result.unshift(String(item['breadcrumbParentId']))
- } else {
- result.unshift(String(item[pid]))
- getPath(queryArray(array, id, item[pid]))
- }
- }
- }
- getPath(current)
- return result
- }
- if (currentMenu) {
- defaultSelectedKeys = getPathArray(
- menuList,
- currentMenu,
- 'menuParentId',
- 'id'
+ return (
+
+
+ {item.icon && }
+ {item.name}
+
+
)
- }
+ })
+ }
- if (!defaultSelectedKeys) {
- defaultSelectedKeys = ['1']
- }
+ render() {
+ const { collapsed, theme, menus, location } = this.props
+
+ // Generating tree-structured data for menu content.
+ const menuTree = arrayToTree(menus, 'id', 'menuParentId')
+
+ // Find a menu that matches the pathname.
+ const currentMenu = menus.find(
+ _ => _.route && pathMatchRegexp(_.route, location.pathname)
+ )
+
+ // Find the key that should be selected according to the current menu.
+ const selectedKeys = currentMenu
+ ? queryAncestors(menus, currentMenu, 'menuParentId').map(_ => _.id)
+ : []
return (
)
}
}
+
Menus.propTypes = {
- menuList: PropTypes.array,
- siderFold: PropTypes.bool,
- darkTheme: PropTypes.bool,
- navOpenKeys: PropTypes.array,
- changeOpenKeys: PropTypes.func,
- location: PropTypes.object,
+ menus: PropTypes.array,
+ collapsed: PropTypes.bool,
+ theme: PropTypes.string,
}
export default Menus
diff --git a/src/components/Layout/Sider.js b/src/components/Layout/Sider.js
index 474b05f..01b0293 100644
--- a/src/components/Layout/Sider.js
+++ b/src/components/Layout/Sider.js
@@ -1,69 +1,67 @@
import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
-import { Icon, Switch } from 'antd'
+import { Icon, Switch, Layout } from 'antd'
import { withI18n, Trans } from '@lingui/react'
import { config } from 'utils'
-import styles from './Layout.less'
import Menus from './Menu'
+import styles from './Sider.less'
@withI18n()
class Sider extends PureComponent {
render() {
const {
- siderFold,
- darkTheme,
- location,
- changeTheme,
- navOpenKeys,
- changeOpenKeys,
- menuList,
i18n,
+ menus,
+ theme,
+ collapsed,
+ onThemeChange,
+ onCollapseChange,
} = this.props
- const menusProps = {
- menuList,
- siderFold,
- darkTheme,
- location,
- navOpenKeys,
- changeOpenKeys,
- }
return (
-
-
+
+

- {siderFold ? '' :
{config.siteName}}
+ {collapsed ? null :
{config.siteName}
}
-
- {!siderFold ? (
-
+
+
+
+ {collapsed ? null : (
+
Switch Theme
- ) : (
- ''
)}
-
+
)
}
}
Sider.propTypes = {
- menuList: PropTypes.array,
- siderFold: PropTypes.bool,
- darkTheme: PropTypes.bool,
- location: PropTypes.object,
- changeTheme: PropTypes.func,
- navOpenKeys: PropTypes.array,
- changeOpenKeys: PropTypes.func,
+ menus: PropTypes.array,
+ theme: PropTypes.string,
+ collapsed: PropTypes.bool,
+ onThemeChange: PropTypes.func,
+ onCollapseChange: 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..eded13d
--- /dev/null
+++ b/src/components/Layout/Sider.less
@@ -0,0 +1,84 @@
+@import '~themes/vars.less';
+
+.logoContainer {
+ height: 96px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ img {
+ width: 40px;
+ margin-right: 8px;
+ }
+
+ h1 {
+ vertical-align: text-bottom;
+ font-size: 16px;
+ text-transform: uppercase;
+ display: inline-block;
+ font-weight: 700;
+ color: @primary-color;
+ white-space: nowrap;
+ margin-bottom: 0;
+ .text-gradient();
+
+ :local {
+ animation: fadeRightIn 300ms @ease-in-out;
+ animation-fill-mode: both;
+ }
+ }
+}
+
+.menuContainer {
+ height: ~'calc(100vh - 144px)';
+ overflow-x: hidden;
+ border-right: none;
+ flex: 1;
+
+ &::-webkit-scrollbar-thumb {
+ background-color: transparent;
+ }
+
+ &:hover {
+ &::-webkit-scrollbar-thumb {
+ background-color: rgba(0, 0, 0, 0.2);
+ }
+ }
+}
+
+.switchTheme {
+ width: 100%;
+ height: 48px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 0 16px;
+ overflow: hidden;
+ transition: all 0.3s;
+
+ span {
+ white-space: nowrap;
+ overflow: hidden;
+ font-size: 12px;
+ }
+
+ :global {
+ .anticon {
+ min-width: 14px;
+ margin-right: 4px;
+ font-size: 14px;
+ }
+ }
+}
+
+@keyframes fadeLeftIn {
+ 0% {
+ transform: translateX(5px);
+ opacity: 0;
+ }
+
+ 100% {
+ transform: translateX(0);
+ opacity: 1;
+ }
+}
diff --git a/src/components/Layout/index.js b/src/components/Layout/index.js
index c170d87..3ad0894 100644
--- a/src/components/Layout/index.js
+++ b/src/components/Layout/index.js
@@ -2,6 +2,5 @@ import Header from './Header'
import Menu from './Menu'
import Bread from './Bread'
import Sider from './Sider'
-import styles from './Layout.less'
-export { Header, Menu, Bread, Sider, styles }
+export { Header, Menu, Bread, Sider }
diff --git a/src/layouts/BaseLayout.js b/src/layouts/BaseLayout.js
index 4bd1c81..5ad52f5 100644
--- a/src/layouts/BaseLayout.js
+++ b/src/layouts/BaseLayout.js
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types'
import { connect } from 'dva'
import { Helmet } from 'react-helmet'
import { Loader } from 'components'
-import { pathMatchRegexp } from 'utils'
+import { queryLayout } from 'utils'
import NProgress from 'nprogress'
import config from 'utils/config'
import withRouter from 'umi/withRouter'
@@ -17,51 +17,6 @@ const LayoutMap = {
public: PublicLayout,
}
-/**
- * Query which layout should be used for the current path based on the configuration.
- * @param {layouts} layouts Layout configuration.
- * @param {pathname} pathname Path name to be queried.
- * @return {string} Return frist object when query success.
- */
-const queryLayout = (layouts, pathname) => {
- let result = 'public'
-
- const isMatch = regepx => {
- return regepx instanceof RegExp
- ? regepx.test(pathname)
- : pathMatchRegexp(regepx, pathname)
- }
-
- for (const item of layouts) {
- let include = false
- let exlude = false
- if (item.include) {
- for (const regepx of item.include) {
- if (isMatch(regepx)) {
- include = true
- break
- }
- }
- }
-
- if (include && item.exlude) {
- for (const regepx of item.exlude) {
- if (isMatch(regepx)) {
- exlude = true
- break
- }
- }
- }
-
- if (include && !exlude) {
- result = item.name
- break
- }
- }
-
- return result
-}
-
@withRouter
@connect(({ loading }) => ({ loading }))
class BaseLayout extends PureComponent {
diff --git a/src/layouts/PrimaryLayout.js b/src/layouts/PrimaryLayout.js
index ae0e047..8b58e09 100644
--- a/src/layouts/PrimaryLayout.js
+++ b/src/layouts/PrimaryLayout.js
@@ -7,114 +7,82 @@ import { connect } from 'dva'
import { MyLayout } from 'components'
import { BackTop, Layout } from 'antd'
import { GlobalFooter } from 'ant-design-pro'
-import { classnames, config, pathMatchRegexp } from 'utils'
+import { config, pathMatchRegexp } from 'utils'
import Error from '../pages/404'
+import styles from './PrimaryLayout.less'
-const { Content, Sider } = Layout
-const { Header, Bread, styles } = MyLayout
-const { prefix } = config
+const { Content } = Layout
+const { Header, Bread } = MyLayout
@withRouter
@connect(({ app, loading }) => ({ app, loading }))
class PrimaryLayout extends PureComponent {
render() {
- const { children, dispatch, app, location } = this.props
- const {
- user,
- siderFold,
- darkTheme,
- isNavbar,
- menuPopoverVisible,
- navOpenKeys,
- menuList,
- permissions,
- } = app
- let { pathname } = location
- pathname = pathname.startsWith('/') ? pathname : `/${pathname}`
- const current = menuList.filter(item =>
- pathMatchRegexp(item.route || '', pathname)
+ const { app, location, dispatch, children } = this.props
+ const { user, theme, routeList, permissions, collapsed } = app
+
+ // Find a route that matches the pathname.
+ const currentRoute = routeList.find(
+ _ => _.route && pathMatchRegexp(_.route, location.pathname)
)
- const hasPermission = current.length
- ? permissions.visit.includes(current[0].id)
+
+ // Query whether you have permission to enter this page
+ const hasPermission = currentRoute
+ ? permissions.visit.includes(currentRoute.id)
: false
+ // MenuParentId is equal to -1 is not a available menu.
+ const menus = routeList.filter(_ => _.menuParentId !== '-1')
+
const headerProps = {
- menuList,
user,
- location,
- siderFold,
- isNavbar,
- menuPopoverVisible,
- navOpenKeys,
- switchMenuPopover() {
- dispatch({ type: 'app/switchMenuPopver' })
+ menus,
+ collapsed,
+ onSignOut() {
+ dispatch({ type: 'app/signOut' })
},
- logout() {
- dispatch({ type: 'app/logout' })
- },
- switchSider() {
- dispatch({ type: 'app/switchSider' })
- },
- changeOpenKeys(openKeys) {
+ onCollapseChange(collapsed) {
dispatch({
- type: 'app/handleNavOpenKeys',
- payload: { navOpenKeys: openKeys },
+ type: 'app/handleCollapseChange',
+ payload: collapsed,
})
},
}
const siderProps = {
- menuList,
- location,
- siderFold,
- darkTheme,
- navOpenKeys,
- changeTheme() {
- dispatch({ type: 'app/switchTheme' })
- },
- changeOpenKeys(openKeys) {
- window.localStorage.setItem(
- `${prefix}navOpenKeys`,
- JSON.stringify(openKeys)
- )
+ menus,
+ theme,
+ collapsed,
+ onThemeChange(theme) {
dispatch({
- type: 'app/handleNavOpenKeys',
- payload: { navOpenKeys: openKeys },
+ type: 'app/handleThemeChange',
+ payload: theme,
+ })
+ },
+ onCollapseChange(collapsed) {
+ dispatch({
+ type: 'app/handleCollapseChange',
+ payload: collapsed,
})
},
}
- const breadProps = {
- menuList,
- location,
- }
-
return (
-
- {!isNavbar ? (
-
- {siderProps.menuList.length === 0 ? null : (
-
- )}
-
- ) : null}
+
+
-
-
+
+
{hasPermission ? children : }
document.getElementById('PrimaryLayoutContainer')}
+ className={styles.backTop}
+ target={() => document.getElementById('primaryLayout')}
/>
diff --git a/src/layouts/PrimaryLayout.less b/src/layouts/PrimaryLayout.less
new file mode 100644
index 0000000..bd157d6
--- /dev/null
+++ b/src/layouts/PrimaryLayout.less
@@ -0,0 +1,18 @@
+@media (max-width: 767px) {
+ .content {
+ padding: 12px;
+ }
+
+ .backTop {
+ right: 20px;
+ bottom: 20px;
+ }
+}
+
+.backTop {
+ right: 50px;
+}
+
+.content {
+ padding: 24px;
+}
diff --git a/src/models/app.js b/src/models/app.js
index 04e39e6..8d0389c 100644
--- a/src/models/app.js
+++ b/src/models/app.js
@@ -5,11 +5,11 @@
import { router } from 'utils'
import { parse, stringify } from 'qs'
-import config from 'config'
+import store from 'store'
import { RoleType } from 'utils/constant'
-import { queryMenuList, logoutUser, queryUserInfo } from 'api'
-
-const { prefix } = config
+import { queryLayout } from 'utils'
+import { queryRouteList, logoutUser, queryUserInfo } from 'api'
+import config from 'config'
export default {
namespace: 'app',
@@ -18,7 +18,7 @@ export default {
permissions: {
visit: [],
},
- menuList: [
+ routeList: [
{
id: 1,
icon: 'laptop',
@@ -26,14 +26,10 @@ export default {
router: '/dashboard',
},
],
- menuPopoverVisible: false,
- siderFold: window.localStorage.getItem(`${prefix}siderFold`) === 'true',
- darkTheme: window.localStorage.getItem(`${prefix}darkTheme`) === 'true',
- isNavbar: document.body.clientWidth < 769,
- navOpenKeys:
- JSON.parse(window.localStorage.getItem(`${prefix}navOpenKeys`)) || [],
locationPathname: '',
locationQuery: {},
+ theme: store.get('theme') || 'light',
+ collapsed: store.get('collapsed') || false,
},
subscriptions: {
setupHistory({ dispatch, history }) {
@@ -63,17 +59,18 @@ export default {
*query({ payload }, { call, put, select }) {
const { success, user } = yield call(queryUserInfo, payload)
const { locationPathname } = yield select(_ => _.app)
+
if (success && user) {
- const { list } = yield call(queryMenuList)
+ const { list } = yield call(queryRouteList)
const { permissions } = user
- let menuList = list
+ let routeList = list
if (
permissions.role === RoleType.ADMIN ||
permissions.role === RoleType.DEVELOPER
) {
permissions.visit = list.map(item => item.id)
} else {
- menuList = list.filter(item => {
+ routeList = list.filter(item => {
const cases = [
permissions.visit.includes(item.id),
item.mpid
@@ -89,7 +86,7 @@ export default {
payload: {
user,
permissions,
- menuList,
+ routeList,
},
})
if (location.pathname === '/login') {
@@ -97,10 +94,7 @@ export default {
pathname: '/dashboard',
})
}
- } else if (
- config.openPages &&
- config.openPages.indexOf(locationPathname) < 0
- ) {
+ } else if (queryLayout(config.layouts, locationPathname) !== 'public') {
router.push({
pathname: '/login',
search: stringify({
@@ -110,8 +104,8 @@ export default {
}
},
- *logout({ payload }, { call, put }) {
- const data = yield call(logoutUser, parse(payload))
+ *signOut({ payload }, { call, put }) {
+ const data = yield call(logoutUser)
if (data.success) {
yield put({
type: 'updateState',
@@ -133,14 +127,6 @@ export default {
throw data
}
},
-
- *changeNavbar(action, { put, select }) {
- const { app } = yield select(_ => _)
- const isNavbar = document.body.clientWidth < 769
- if (isNavbar !== app.isNavbar) {
- yield put({ type: 'handleNavbar', payload: isNavbar })
- }
- },
},
reducers: {
updateState(state, { payload }) {
@@ -150,29 +136,14 @@ export default {
}
},
- switchSider(state) {
- window.localStorage.setItem(`${prefix}siderFold`, !state.siderFold)
- state.siderFold = !state.siderFold
+ handleThemeChange(state, { payload }) {
+ store.set('theme', payload)
+ state.theme = payload
},
- switchTheme(state) {
- window.localStorage.setItem(`${prefix}darkTheme`, !state.darkTheme)
- state.darkTheme = !state.darkTheme
- },
-
- switchMenuPopver(state) {
- state.menuPopoverVisible = !state.menuPopoverVisible
- },
-
- handleNavbar(state, { payload }) {
- state.isNavbar = payload
- },
-
- handleNavOpenKeys(state, { payload: navOpenKeys }) {
- return {
- ...state,
- ...navOpenKeys,
- }
+ handleCollapseChange(state, { payload }) {
+ store.set('collapsed', payload)
+ state.collapsed = payload
},
},
}
diff --git a/src/pages/login/index.less b/src/pages/login/index.less
index 4b8d902..d7642d6 100644
--- a/src/pages/login/index.less
+++ b/src/pages/login/index.less
@@ -44,6 +44,7 @@
display: inline-block;
font-weight: 700;
color: @primary-color;
+ .text-gradient();
}
}
diff --git a/src/services/api.js b/src/services/api.js
index f372b59..14c9d41 100644
--- a/src/services/api.js
+++ b/src/services/api.js
@@ -1,5 +1,5 @@
const api = {
- queryMenuList: '/menus',
+ queryRouteList: '/routes',
queryUserInfo: '/user',
logoutUser: '/user/logout',
diff --git a/src/themes/mixin.less b/src/themes/mixin.less
index 8e5a36e..d841ab3 100644
--- a/src/themes/mixin.less
+++ b/src/themes/mixin.less
@@ -13,3 +13,16 @@
text-overflow: ellipsis;
overflow: hidden;
}
+
+.text-gradient {
+ background-image: -webkit-gradient(
+ linear,
+ 37.219838% 34.532506%,
+ 36.425669% 93.178216%,
+ from(#29cdff),
+ to(#0a60ff),
+ color-stop(0.37, #148eff)
+ );
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+}
diff --git a/src/utils/index.js b/src/utils/index.js
index 60a1e78..61949eb 100644
--- a/src/utils/index.js
+++ b/src/utils/index.js
@@ -15,31 +15,27 @@ export const { languages, defaultLanguage } = i18n
* @param {array} array An array where all values are objects, like [{key:1},{key:2}].
* @param {string} key The key of the object that needs to be queried.
* @param {string} value The value of the object that needs to be queried.
- * @return {object|null} Return frist object when query success.
+ * @return {object|undefined} Return frist object when query success.
*/
export function queryArray(array, key, value) {
if (!Array.isArray(array)) {
- return null
+ return
}
- const item = array.filter(_ => _[key] === value)
- if (item.length) {
- return item[0]
- }
- return null
+ return array.find(_ => _[key] === value)
}
/**
* Convert an array to a tree-structured array.
* @param {array} array The Array need to Converted.
* @param {string} id The alias of the unique ID of the object in the array.
- * @param {string} pid The alias of the parent ID of the object in the array.
+ * @param {string} parentId The alias of the parent ID of the object in the array.
* @param {string} children The alias of children of the object in the array.
* @return {array} Return a tree-structured array.
*/
export function arrayToTree(
array,
id = 'id',
- pid = 'pid',
+ parentId = 'pid',
children = 'children'
) {
const result = []
@@ -51,7 +47,7 @@ export function arrayToTree(
})
data.forEach(item => {
- const hashParent = hash[item[pid]]
+ const hashParent = hash[item[parentId]]
if (hashParent) {
!hashParent[children] && (hashParent[children] = [])
hashParent[children].push(item)
@@ -143,6 +139,101 @@ export const router = myRouter
* @param {string} pathname Specify the pathname to match.
* @return {array|null} Return the result of the match or null.
*/
-export const pathMatchRegexp = (regexp, pathname) => {
+export function pathMatchRegexp(regexp, pathname) {
return pathToRegexp(regexp).exec(deLangPrefix(pathname))
}
+
+/**
+ * In an array object, traverse all parent IDs based on the value of an object.
+ * @param {array} array The Array need to Converted.
+ * @param {string} current Specify the value of the object that needs to be queried.
+ * @param {string} parentId The alias of the parent ID of the object in the array.
+ * @param {string} id The alias of the unique ID of the object in the array.
+ * @return {array} Return a key array.
+ */
+export function queryPathKeys(array, current, parentId, id = 'id') {
+ const result = [current]
+ const hashMap = new Map()
+ array.forEach(item => hashMap.set(item[id], item))
+
+ const getPath = current => {
+ const currentParentId = hashMap.get(current)[parentId]
+ if (currentParentId) {
+ result.push(currentParentId)
+ getPath(currentParentId)
+ }
+ }
+
+ getPath(current)
+ return result
+}
+
+/**
+ * In an array of objects, specify an object that traverses the objects whose parent ID matches.
+ * @param {array} array The Array need to Converted.
+ * @param {string} current Specify the object that needs to be queried.
+ * @param {string} parentId The alias of the parent ID of the object in the array.
+ * @param {string} id The alias of the unique ID of the object in the array.
+ * @return {array} Return a key array.
+ */
+export function queryAncestors(array, current, parentId, id = 'id') {
+ const result = [current]
+ const hashMap = new Map()
+ array.forEach(item => hashMap.set(item[id], item))
+
+ const getPath = current => {
+ const currentParentId = hashMap.get(current[id])[parentId]
+ if (currentParentId) {
+ result.push(hashMap.get(currentParentId))
+ getPath(hashMap.get(currentParentId))
+ }
+ }
+
+ getPath(current)
+ return result
+}
+
+/**
+ * Query which layout should be used for the current path based on the configuration.
+ * @param {layouts} layouts Layout configuration.
+ * @param {pathname} pathname Path name to be queried.
+ * @return {string} Return frist object when query success.
+ */
+export function queryLayout(layouts, pathname) {
+ let result = 'public'
+
+ const isMatch = regepx => {
+ return regepx instanceof RegExp
+ ? regepx.test(pathname)
+ : pathMatchRegexp(regepx, pathname)
+ }
+
+ for (const item of layouts) {
+ let include = false
+ let exlude = false
+ if (item.include) {
+ for (const regepx of item.include) {
+ if (isMatch(regepx)) {
+ include = true
+ break
+ }
+ }
+ }
+
+ if (include && item.exlude) {
+ for (const regepx of item.exlude) {
+ if (isMatch(regepx)) {
+ exlude = true
+ break
+ }
+ }
+ }
+
+ if (include && !exlude) {
+ result = item.name
+ break
+ }
+ }
+
+ return result
+}
diff --git a/yarn.lock b/yarn.lock
index e9703b5..d76fe63 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -11577,6 +11577,10 @@ stealthy-require@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b"
+store@^2.0.12:
+ version "2.0.12"
+ resolved "https://registry.yarnpkg.com/store/-/store-2.0.12.tgz#8c534e2a0b831f72b75fc5f1119857c44ef5d593"
+
stream-browserify@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db"