import React, { Suspense } from 'react'; import { Layout } from 'antd'; import DocumentTitle from 'react-document-title'; import { connect } from 'dva'; import { ContainerQuery } from 'react-container-query'; import classNames from 'classnames'; import pathToRegexp from 'path-to-regexp'; import Media from 'react-media'; import Authorized from '@/utils/Authorized'; import logo from '../assets/logo.svg'; import Footer from './Footer'; import Header from './Header'; import Context from './MenuContext'; import UrlsContext from './UrlsContext'; import Exception403 from '../pages/Exception/403'; import PageLoading from '@/components/PageLoading'; import SiderMenu from '@/components/SiderMenu'; import getPageTitle from '@/utils/getPageTitle'; import DictionaryContext from '@/components/Dictionary/DictionaryContext'; import styles from './BasicLayout.less'; // lazy load SettingDrawer const SettingDrawer = React.lazy(() => import('@/components/SettingDrawer')); const { Content } = Layout; const query = { 'screen-xs': { maxWidth: 575, }, 'screen-sm': { minWidth: 576, maxWidth: 767, }, 'screen-md': { minWidth: 768, maxWidth: 991, }, 'screen-lg': { minWidth: 992, maxWidth: 1199, }, 'screen-xl': { minWidth: 1200, maxWidth: 1599, }, 'screen-xxl': { minWidth: 1600, }, }; class BasicLayout extends React.Component { componentDidMount() { const { dispatch, route: { routes, authority }, } = this.props; dispatch({ type: 'user/fetchCurrent', }); dispatch({ type: 'setting/getSetting', }); dispatch({ type: 'menu/getUrlsData', payload: { routes, authority }, }); dispatch({ type: 'menu/getMenuData', payload: { routes, authority }, }); dispatch({ type: 'dictionaryContext/tree', payload: {}, }); } getContext() { const { location, breadcrumbNameMap } = this.props; return { location, breadcrumbNameMap, }; } getUrlsContext() { const { urlsData } = this.props; return { urls: { ...urlsData, }, }; } getDictionaryContext() { const { dicTreeMap } = this.props; return dicTreeMap; } getRouteAuthority = (pathname, routeData) => { const routes = routeData.slice(); // clone const getAuthority = (routeDatas, path) => { let authorities; routeDatas.forEach(route => { // check partial route if (pathToRegexp(`${route.path}(.*)`).test(path)) { if (route.authority) { authorities = route.authority; } // is exact route? if (!pathToRegexp(route.path).test(path) && route.routes) { authorities = getAuthority(route.routes, path); } } }); return authorities; }; return getAuthority(routes, pathname); }; getLayoutStyle = () => { const { fixSiderbar, isMobile, collapsed, layout } = this.props; if (fixSiderbar && layout !== 'topmenu' && !isMobile) { return { paddingLeft: collapsed ? '80px' : '256px', }; } return null; }; handleMenuCollapse = collapsed => { const { dispatch } = this.props; dispatch({ type: 'global/changeLayoutCollapsed', payload: collapsed, }); }; renderSettingDrawer = () => { // Do not render SettingDrawer in production // unless it is deployed in preview.pro.ant.design as demo if (process.env.NODE_ENV === 'production' && APP_TYPE !== 'site') { return null; } return <SettingDrawer />; }; render() { const { navTheme, layout: PropsLayout, children, location: { pathname }, isMobile, menuData, breadcrumbNameMap, route: { routes }, fixedHeader, } = this.props; const isTop = PropsLayout === 'topmenu'; const routerConfig = this.getRouteAuthority(pathname, routes); const contentStyle = !fixedHeader ? { paddingTop: 0 } : {}; const layout = ( <Layout> {isTop && !isMobile ? null : ( <SiderMenu logo={logo} theme={navTheme} onCollapse={this.handleMenuCollapse} menuData={menuData} isMobile={isMobile} {...this.props} /> )} <Layout style={{ ...this.getLayoutStyle(), minHeight: '100vh', }} > <Header menuData={menuData} handleMenuCollapse={this.handleMenuCollapse} logo={logo} isMobile={isMobile} {...this.props} /> <Content className={styles.content} style={contentStyle}> <Authorized authority={routerConfig} noMatch={<Exception403 />}> {children} </Authorized> </Content> <Footer /> </Layout> </Layout> ); return ( <React.Fragment> <DocumentTitle title={getPageTitle(pathname, breadcrumbNameMap)}> <ContainerQuery query={query}> {params => ( <Context.Provider value={this.getContext()}> <UrlsContext.Provider value={this.getUrlsContext()}> <DictionaryContext.Provider value={this.getDictionaryContext()}> <div className={classNames(params)}>{layout}</div> </DictionaryContext.Provider> </UrlsContext.Provider> </Context.Provider> )} </ContainerQuery> </DocumentTitle> <Suspense fallback={<PageLoading />}>{this.renderSettingDrawer()}</Suspense> </React.Fragment> ); } } export default connect(({ global, setting, dictionaryContext, menu: menuModel }) => ({ collapsed: global.collapsed, layout: setting.layout, menuData: menuModel.menuData, urlsData: menuModel.urlsData, breadcrumbNameMap: menuModel.breadcrumbNameMap, dicTreeMap: dictionaryContext.dicTreeMap, ...setting, }))(props => ( <Media query="(max-width: 599px)"> {isMobile => <BasicLayout {...props} isMobile={isMobile} />} </Media> ));