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';
// TODO: should use this.props.routes
import routerConfig from '../../config/router.config';
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;
});
}
// get menu map data
const menuData = formatter(routerConfig[1].routes);
/**
* 获取面包屑映射
* @param {Object} menuData 菜单配置
*/
const getBreadcrumbNameMap = memoizeOne(menu => {
const routerMap = {};
const mergeMenuAndRouter = data => {
data.forEach(menuItem => {
if (menuItem.children) {
mergeMenuAndRouter(menuItem.children);
}
// Reduce memory usage
routerMap[menuItem.path] = menuItem;
});
};
mergeMenuAndRouter(menu);
return routerMap;
}, isEqual);
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);
// Because there are many places to be. So put it here
this.breadcrumbNameMap = getBreadcrumbNameMap(menuData);
}
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 = getBreadcrumbNameMap(menuData);
}
componentWillUnmount() {
cancelAnimationFrame(this.renderRef);
unenquireScreen(this.enquireHandler);
}
getContext() {
const { location } = this.props;
return {
location,
breadcrumbNameMap: this.breadcrumbNameMap,
};
}
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 {
silderTheme,
layout: PropsLayout,
children,
location: { pathname },
} = this.props;
const { rendering, isMobile } = this.state;
const isTop = PropsLayout === 'topmenu';
const layout = (
{isTop && !isMobile ? null : (
)}
{children}
);
return (
{params => (
{layout}
)}
{rendering && process.env.NODE_ENV === 'production' ? null : ( // Do show SettingDrawer in production
)}
);
}
}
export default connect(({ global, setting }) => ({
collapsed: global.collapsed,
layout: setting.layout,
...setting,
}))(BasicLayout);