mirror of
https://github.com/zuiidea/antd-admin
synced 2026-10-29 20:44:05 +00:00
Optimize layout configuration to make it simpler and more flexible.
This commit is contained in:
@@ -26,6 +26,19 @@ export default {
|
||||
/chart\/Recharts\/.+Component\.js$/,
|
||||
/chart\/Recharts\/Container\.js$/,
|
||||
],
|
||||
update: routes => {
|
||||
const newRoutes = []
|
||||
for (const item of routes[0].routes) {
|
||||
newRoutes.push(item)
|
||||
if(item.path){
|
||||
newRoutes.push(
|
||||
Object.assign({}, item, { path: '/:lang(en|zh)' + item.path } )
|
||||
)
|
||||
}
|
||||
}
|
||||
routes[0].routes = newRoutes
|
||||
return routes
|
||||
},
|
||||
},
|
||||
dll: {
|
||||
include: ['dva', 'dva/router', 'dva/saga', 'dva/fetch', 'antd/es'],
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
import React, { PureComponent, Fragment } from 'react'
|
||||
import pathToRegexp from 'path-to-regexp'
|
||||
import PropTypes from 'prop-types'
|
||||
import { connect } from 'dva'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import { Loader } from 'components'
|
||||
import NProgress from 'nprogress'
|
||||
import config from 'utils/config'
|
||||
import withRouter from 'umi/withRouter'
|
||||
|
||||
import PublicLayout from './PublicLayout'
|
||||
import PrimaryLayout from './PrimaryLayout'
|
||||
import './BaseLayout.less'
|
||||
|
||||
const LayoutMap = {
|
||||
primary: PrimaryLayout,
|
||||
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)
|
||||
: pathToRegexp(regepx).exec(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 {
|
||||
previousPath = ''
|
||||
|
||||
render() {
|
||||
const { loading, children, location } = this.props
|
||||
const Container = LayoutMap[queryLayout(config.layouts, location.pathname)]
|
||||
|
||||
const currentPath = location.pathname + location.search
|
||||
if (currentPath !== this.previousPath) {
|
||||
NProgress.start()
|
||||
if (!loading.global) {
|
||||
NProgress.done()
|
||||
this.previousPath = currentPath
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<Helmet>
|
||||
<title>{config.siteName}</title>
|
||||
</Helmet>
|
||||
<Loader fullScreen spinning={loading.effects['app/query']} />
|
||||
<Container>{children}</Container>
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
BaseLayout.propTypes = {
|
||||
loading: PropTypes.object,
|
||||
}
|
||||
|
||||
export default BaseLayout
|
||||
@@ -1,4 +1,5 @@
|
||||
@import '~themes/vars.less';
|
||||
@import '~themes/index.less';
|
||||
|
||||
:global {
|
||||
#nprogress {
|
||||
@@ -7,7 +8,7 @@
|
||||
.bar {
|
||||
background: @primary-color;
|
||||
position: fixed;
|
||||
z-index: 1024;
|
||||
z-index: 2048;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
@@ -61,6 +62,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes nprogress-spinner {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
@@ -0,0 +1,135 @@
|
||||
/* global window */
|
||||
/* global document */
|
||||
import React, { PureComponent, Fragment } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import pathToRegexp from 'path-to-regexp'
|
||||
import withRouter from 'umi/withRouter'
|
||||
import { connect } from 'dva'
|
||||
import { MyLayout } from 'components'
|
||||
import { BackTop, Layout } from 'antd'
|
||||
import { classnames, config } from 'utils'
|
||||
import Error from '../pages/404'
|
||||
|
||||
const { Content, Footer, Sider } = Layout
|
||||
const { Header, Bread, styles } = MyLayout
|
||||
const { prefix } = config
|
||||
|
||||
@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,
|
||||
menu,
|
||||
permissions,
|
||||
} = app
|
||||
let { pathname } = location
|
||||
pathname = pathname.startsWith('/') ? pathname : `/${pathname}`
|
||||
const current = menu.filter(item =>
|
||||
pathToRegexp(item.route || '').exec(pathname)
|
||||
)
|
||||
const hasPermission = current.length
|
||||
? permissions.visit.includes(current[0].id)
|
||||
: false
|
||||
|
||||
const headerProps = {
|
||||
menu,
|
||||
user,
|
||||
location,
|
||||
siderFold,
|
||||
isNavbar,
|
||||
menuPopoverVisible,
|
||||
navOpenKeys,
|
||||
switchMenuPopover() {
|
||||
dispatch({ type: 'app/switchMenuPopver' })
|
||||
},
|
||||
logout() {
|
||||
dispatch({ type: 'app/logout' })
|
||||
},
|
||||
switchSider() {
|
||||
dispatch({ type: 'app/switchSider' })
|
||||
},
|
||||
changeOpenKeys(openKeys) {
|
||||
dispatch({
|
||||
type: 'app/handleNavOpenKeys',
|
||||
payload: { navOpenKeys: openKeys },
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
const siderProps = {
|
||||
menu,
|
||||
location,
|
||||
siderFold,
|
||||
darkTheme,
|
||||
navOpenKeys,
|
||||
changeTheme() {
|
||||
dispatch({ type: 'app/switchTheme' })
|
||||
},
|
||||
changeOpenKeys(openKeys) {
|
||||
window.localStorage.setItem(
|
||||
`${prefix}navOpenKeys`,
|
||||
JSON.stringify(openKeys)
|
||||
)
|
||||
dispatch({
|
||||
type: 'app/handleNavOpenKeys',
|
||||
payload: { navOpenKeys: openKeys },
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
const breadProps = {
|
||||
menu,
|
||||
location,
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<Layout
|
||||
className={classnames({
|
||||
[styles.dark]: darkTheme,
|
||||
[styles.light]: !darkTheme,
|
||||
})}
|
||||
>
|
||||
{!isNavbar && (
|
||||
<Sider trigger={null} collapsible collapsed={siderFold}>
|
||||
{siderProps.menu.length === 0 ? null : (
|
||||
<MyLayout.Sider {...siderProps} />
|
||||
)}
|
||||
</Sider>
|
||||
)}
|
||||
<Layout
|
||||
style={{ height: '100vh', overflow: 'scroll' }}
|
||||
id="PrimaryLayoutContainer"
|
||||
>
|
||||
<BackTop
|
||||
target={() => document.getElementById('PrimaryLayoutContainer')}
|
||||
/>
|
||||
<Header {...headerProps} />
|
||||
<Content>
|
||||
<Bread {...breadProps} />
|
||||
{hasPermission ? children : <Error />}
|
||||
</Content>
|
||||
<Footer>{config.footerText}</Footer>
|
||||
</Layout>
|
||||
</Layout>
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
PrimaryLayout.propTypes = {
|
||||
children: PropTypes.element.isRequired,
|
||||
location: PropTypes.object,
|
||||
dispatch: PropTypes.func,
|
||||
app: PropTypes.object,
|
||||
loading: PropTypes.object,
|
||||
}
|
||||
|
||||
export default PrimaryLayout
|
||||
@@ -0,0 +1,3 @@
|
||||
export default ({ children }) => {
|
||||
return children
|
||||
}
|
||||
@@ -1,162 +0,0 @@
|
||||
/* global window */
|
||||
/* global document */
|
||||
import React from 'react'
|
||||
import NProgress from 'nprogress'
|
||||
import PropTypes from 'prop-types'
|
||||
import pathToRegexp from 'path-to-regexp'
|
||||
import { connect } from 'dva'
|
||||
import { Loader, MyLayout } from 'components'
|
||||
import { BackTop, Layout } from 'antd'
|
||||
import { classnames, config } from 'utils'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import { withRouter } from 'dva/router'
|
||||
import Error from '../pages/404'
|
||||
import '../themes/index.less'
|
||||
import './app.less'
|
||||
|
||||
const { Content, Footer, Sider } = Layout
|
||||
const { Header, Bread, styles } = MyLayout
|
||||
const { prefix, openPages } = config
|
||||
|
||||
let lastHref
|
||||
|
||||
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 { logo } = config
|
||||
const current = menu.filter(item =>
|
||||
pathToRegexp(item.route || '').exec(pathname)
|
||||
)
|
||||
const hasPermission = current.length
|
||||
? permissions.visit.includes(current[0].id)
|
||||
: false
|
||||
const { href } = window.location
|
||||
|
||||
if (lastHref !== href) {
|
||||
NProgress.start()
|
||||
if (!loading.global) {
|
||||
NProgress.done()
|
||||
lastHref = href
|
||||
}
|
||||
}
|
||||
|
||||
const headerProps = {
|
||||
menu,
|
||||
user,
|
||||
location,
|
||||
siderFold,
|
||||
isNavbar,
|
||||
menuPopoverVisible,
|
||||
navOpenKeys,
|
||||
switchMenuPopover() {
|
||||
dispatch({ type: 'app/switchMenuPopver' })
|
||||
},
|
||||
logout() {
|
||||
dispatch({ type: 'app/logout' })
|
||||
},
|
||||
switchSider() {
|
||||
dispatch({ type: 'app/switchSider' })
|
||||
},
|
||||
changeOpenKeys(openKeys) {
|
||||
dispatch({
|
||||
type: 'app/handleNavOpenKeys',
|
||||
payload: { navOpenKeys: openKeys },
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
const siderProps = {
|
||||
menu,
|
||||
location,
|
||||
siderFold,
|
||||
darkTheme,
|
||||
navOpenKeys,
|
||||
changeTheme() {
|
||||
dispatch({ type: 'app/switchTheme' })
|
||||
},
|
||||
changeOpenKeys(openKeys) {
|
||||
window.localStorage.setItem(
|
||||
`${prefix}navOpenKeys`,
|
||||
JSON.stringify(openKeys)
|
||||
)
|
||||
dispatch({
|
||||
type: 'app/handleNavOpenKeys',
|
||||
payload: { navOpenKeys: openKeys },
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
const breadProps = {
|
||||
menu,
|
||||
location,
|
||||
}
|
||||
|
||||
if (openPages && openPages.includes(pathname)) {
|
||||
return (
|
||||
<div>
|
||||
<Loader fullScreen spinning={loading.effects['app/query']} />
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Loader fullScreen spinning={loading.effects['app/query']} />
|
||||
<Helmet>
|
||||
<title>ANTD ADMIN</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="icon" href={logo} type="image/x-icon" />
|
||||
</Helmet>
|
||||
|
||||
<Layout
|
||||
className={classnames({
|
||||
[styles.dark]: darkTheme,
|
||||
[styles.light]: !darkTheme,
|
||||
})}
|
||||
>
|
||||
{!isNavbar && (
|
||||
<Sider trigger={null} collapsible collapsed={siderFold}>
|
||||
{siderProps.menu.length === 0 ? null : (
|
||||
<MyLayout.Sider {...siderProps} />
|
||||
)}
|
||||
</Sider>
|
||||
)}
|
||||
<Layout
|
||||
style={{ height: '100vh', overflow: 'scroll' }}
|
||||
id="mainContainer"
|
||||
>
|
||||
<BackTop target={() => document.getElementById('mainContainer')} />
|
||||
<Header {...headerProps} />
|
||||
<Content>
|
||||
<Bread {...breadProps} />
|
||||
{hasPermission ? children : <Error />}
|
||||
</Content>
|
||||
<Footer>{config.footerText}</Footer>
|
||||
</Layout>
|
||||
</Layout>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
App.propTypes = {
|
||||
children: PropTypes.element.isRequired,
|
||||
location: PropTypes.object,
|
||||
dispatch: PropTypes.func,
|
||||
app: PropTypes.object,
|
||||
loading: PropTypes.object,
|
||||
}
|
||||
|
||||
export default withRouter(
|
||||
connect(({ app, loading }) => ({ app, loading }))(App)
|
||||
)
|
||||
+13
-10
@@ -1,13 +1,16 @@
|
||||
import React from 'react'
|
||||
import React, { PureComponent } from 'react'
|
||||
import { LocaleProvider } from 'antd'
|
||||
import enUS from 'antd/lib/locale-provider/en_US'
|
||||
import withRouter from 'umi/withRouter'
|
||||
import App from './app'
|
||||
import BaseLayout from './BaseLayout'
|
||||
|
||||
export default withRouter(props => {
|
||||
return (
|
||||
<LocaleProvider locale={enUS}>
|
||||
<App>{props.children}</App>
|
||||
</LocaleProvider>
|
||||
)
|
||||
})
|
||||
class Layout extends PureComponent {
|
||||
render() {
|
||||
return (
|
||||
<LocaleProvider locale={enUS}>
|
||||
<BaseLayout>{this.props.children}</BaseLayout>
|
||||
</LocaleProvider>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default Layout
|
||||
|
||||
+8
-8
@@ -103,14 +103,14 @@ export default {
|
||||
config.openPages &&
|
||||
config.openPages.indexOf(locationPathname) < 0
|
||||
) {
|
||||
yield put(
|
||||
routerRedux.push({
|
||||
pathname: '/login',
|
||||
search: stringify({
|
||||
from: locationPathname,
|
||||
}),
|
||||
})
|
||||
)
|
||||
// yield put(
|
||||
// routerRedux.push({
|
||||
// pathname: '/login',
|
||||
// search: stringify({
|
||||
// from: locationPathname,
|
||||
// }),
|
||||
// })
|
||||
// )
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="description" content="A admin dashboard application demo built upon Ant Design and Dva.js">
|
||||
<title>antd admin</title>
|
||||
<!--[if lte IE 10]>
|
||||
<script
|
||||
src="https://as.alipayobjects.com/g/component/??console-polyfill/0.2.2/index.js,media-match/2.0.2/media.match.min.js"></script>
|
||||
<![endif]-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -5,4 +5,11 @@ module.exports = {
|
||||
logoPath: '/logo.svg',
|
||||
apiPrefix: '/api/v1',
|
||||
openPages: ['/login'],
|
||||
layouts: [
|
||||
{
|
||||
name: 'primary',
|
||||
include: [/.*/],
|
||||
exlude: [/(\/(en|zh))*\/login/],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user