| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282 |
- import React from 'react';
- import { Layout } from 'antd';
- import DocumentTitle from 'react-document-title';
- import isEqual from 'lodash/isEqual';
- import memoizeOne from 'memoize-one';
- import { connect } from 'dva';
- import { ContainerQuery } from 'react-container-query';
- import classNames from 'classnames';
- import pathToRegexp from 'path-to-regexp';
- import { enquireScreen, unenquireScreen } from 'enquire-js';
- import { formatMessage } from 'umi/locale';
- import SiderMenu from '@/components/SiderMenu';
- import Authorized from '@/utils/Authorized';
- import SettingDrawer from '@/components/SettingDrawer';
- import logo from '../assets/logo.svg';
- import Footer from './Footer';
- import Header from './Header';
- import Context from './MenuContext';
- const { Content } = Layout;
- const { check } = Authorized;
- // Conversion router to menu.
- function formatter(data, parentPath = '', parentAuthority, parentName) {
- return data.map(item => {
- let locale = 'menu';
- if (parentName && item.name) {
- locale = `${parentName}.${item.name}`;
- } else if (item.name) {
- locale = `menu.${item.name}`;
- } else if (parentName) {
- locale = parentName;
- }
- const result = {
- ...item,
- locale,
- authority: item.authority || parentAuthority,
- };
- if (item.routes) {
- const children = formatter(item.routes, `${parentPath}${item.path}/`, item.authority, locale);
- // Reduce memory usage
- result.children = children;
- }
- delete result.routes;
- return result;
- });
- }
- 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.PureComponent {
- constructor(props) {
- super(props);
- this.getPageTitle = memoizeOne(this.getPageTitle);
- this.getBreadcrumbNameMap = memoizeOne(this.getBreadcrumbNameMap, isEqual);
- this.breadcrumbNameMap = this.getBreadcrumbNameMap();
- }
- state = {
- rendering: true,
- isMobile: false,
- };
- componentDidMount() {
- const { dispatch } = this.props;
- dispatch({
- type: 'user/fetchCurrent',
- });
- dispatch({
- type: 'setting/getSetting',
- });
- this.renderRef = requestAnimationFrame(() => {
- this.setState({
- rendering: false,
- });
- });
- this.enquireHandler = enquireScreen(mobile => {
- const { isMobile } = this.state;
- if (isMobile !== mobile) {
- this.setState({
- isMobile: mobile,
- });
- }
- });
- }
- componentDidUpdate() {
- this.breadcrumbNameMap = this.getBreadcrumbNameMap();
- }
- componentWillUnmount() {
- cancelAnimationFrame(this.renderRef);
- unenquireScreen(this.enquireHandler);
- }
- getContext() {
- const { location } = this.props;
- return {
- location,
- breadcrumbNameMap: this.breadcrumbNameMap,
- };
- }
- getMenuData() {
- const {
- route: { routes },
- } = this.props;
- return formatter(routes);
- }
- /**
- * 获取面包屑映射
- * @param {Object} menuData 菜单配置
- */
- getBreadcrumbNameMap() {
- const routerMap = {};
- const mergeMenuAndRouter = data => {
- data.forEach(menuItem => {
- if (menuItem.children) {
- mergeMenuAndRouter(menuItem.children);
- }
- // Reduce memory usage
- routerMap[menuItem.path] = menuItem;
- });
- };
- mergeMenuAndRouter(this.getMenuData());
- return routerMap;
- }
- getPageTitle = pathname => {
- let currRouterData = null;
- // match params path
- Object.keys(this.breadcrumbNameMap).forEach(key => {
- if (pathToRegexp(key).test(pathname)) {
- currRouterData = this.breadcrumbNameMap[key];
- }
- });
- if (!currRouterData) {
- return 'Ant Design Pro';
- }
- const message = formatMessage({
- id: currRouterData.locale || currRouterData.name,
- defaultMessage: currRouterData.name,
- });
- return `${message} - Ant Design Pro`;
- };
- getLayoutStyle = () => {
- const { fixSiderbar, collapsed, layout } = this.props;
- if (fixSiderbar && layout !== 'topmenu') {
- return {
- paddingLeft: collapsed ? '80px' : '256px',
- };
- }
- return null;
- };
- getContentStyle = () => {
- const { fixedHeader } = this.props;
- return {
- margin: '24px 24px 0',
- paddingTop: fixedHeader ? 64 : 0,
- };
- };
- getBashRedirect = () => {
- // According to the url parameter to redirect
- // 这里是重定向的,重定向到 url 的 redirect 参数所示地址
- const urlParams = new URL(window.location.href);
- const redirect = urlParams.searchParams.get('redirect');
- // Remove the parameters in the url
- if (redirect) {
- urlParams.searchParams.delete('redirect');
- window.history.replaceState(null, 'redirect', urlParams.href);
- } else {
- const { routerData } = this.props;
- // get the first authorized route path in routerData
- const authorizedPath = Object.keys(routerData).find(
- item => check(routerData[item].authority, item) && item !== '/'
- );
- return authorizedPath;
- }
- return redirect;
- };
- handleMenuCollapse = collapsed => {
- const { dispatch } = this.props;
- dispatch({
- type: 'global/changeLayoutCollapsed',
- payload: collapsed,
- });
- };
- render() {
- const {
- navTheme,
- layout: PropsLayout,
- children,
- location: { pathname },
- } = this.props;
- const { rendering, isMobile } = this.state;
- const isTop = PropsLayout === 'topmenu';
- const menuData = this.getMenuData();
- const layout = (
- <Layout>
- {isTop && !isMobile ? null : (
- <SiderMenu
- logo={logo}
- Authorized={Authorized}
- 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 style={this.getContentStyle()}>{children}</Content>
- <Footer />
- </Layout>
- </Layout>
- );
- return (
- <React.Fragment>
- <DocumentTitle title={this.getPageTitle(pathname)}>
- <ContainerQuery query={query}>
- {params => (
- <Context.Provider value={this.getContext()}>
- <div className={classNames(params)}>{layout}</div>
- </Context.Provider>
- )}
- </ContainerQuery>
- </DocumentTitle>
- {rendering && process.env.NODE_ENV === 'production' ? null : ( // Do show SettingDrawer in production
- <SettingDrawer />
- )}
- </React.Fragment>
- );
- }
- }
- export default connect(({ global, setting }) => ({
- collapsed: global.collapsed,
- layout: setting.layout,
- ...setting,
- }))(BasicLayout);
|