动态菜单和动态路由的 antd pro

BasicLayout.js 8.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. import React, { Fragment } from 'react';
  2. import PropTypes from 'prop-types';
  3. import { Layout, Icon, message } from 'antd';
  4. import DocumentTitle from 'react-document-title';
  5. import { connect } from 'dva';
  6. import { Route, Redirect, Switch, routerRedux } from 'dva/router';
  7. import { ContainerQuery } from 'react-container-query';
  8. import classNames from 'classnames';
  9. import { enquireScreen, unenquireScreen } from 'enquire-js';
  10. import GlobalHeader from '../components/GlobalHeader';
  11. import GlobalFooter from '../components/GlobalFooter';
  12. import SiderMenu from '../components/SiderMenu';
  13. import NotFound from '../routes/Exception/404';
  14. import { getRoutes } from '../utils/utils';
  15. import Authorized from '../utils/Authorized';
  16. // import { getMenuData } from '../common/menu';
  17. import logo from '../assets/logo.svg';
  18. const { Content, Header, Footer } = Layout;
  19. const { AuthorizedRoute, check } = Authorized;
  20. /**
  21. * 根据菜单取得重定向地址.
  22. */
  23. const redirectData = [];
  24. // const getRedirect = item => {
  25. // if (item && item.children) {
  26. // if (item.children[0] && item.children[0].path) {
  27. // redirectData.push({
  28. // from: `${item.path}`,
  29. // to: `${item.children[0].path}`,
  30. // });
  31. // item.children.forEach(children => {
  32. // getRedirect(children);
  33. // });
  34. // }
  35. // }
  36. // };
  37. // getMenuData().forEach(getRedirect);
  38. /**
  39. * 获取面包屑映射
  40. * @param {Object} menuData 菜单配置
  41. * @param {Object} routerData 路由配置
  42. */
  43. const getBreadcrumbNameMap = (menuData, routerData) => {
  44. const result = {};
  45. const childResult = {};
  46. for (const i of menuData) {
  47. if (!routerData[i.path]) {
  48. result[i.path] = i;
  49. }
  50. if (i.children) {
  51. Object.assign(childResult, getBreadcrumbNameMap(i.children, routerData));
  52. }
  53. }
  54. return Object.assign({}, routerData, result, childResult);
  55. };
  56. const query = {
  57. 'screen-xs': {
  58. maxWidth: 575,
  59. },
  60. 'screen-sm': {
  61. minWidth: 576,
  62. maxWidth: 767,
  63. },
  64. 'screen-md': {
  65. minWidth: 768,
  66. maxWidth: 991,
  67. },
  68. 'screen-lg': {
  69. minWidth: 992,
  70. maxWidth: 1199,
  71. },
  72. 'screen-xl': {
  73. minWidth: 1200,
  74. },
  75. };
  76. let isMobile;
  77. enquireScreen(b => {
  78. isMobile = b;
  79. });
  80. // @App
  81. class BasicLayout extends React.PureComponent {
  82. static childContextTypes = {
  83. location: PropTypes.object,
  84. breadcrumbNameMap: PropTypes.object,
  85. };
  86. state = {
  87. isMobile,
  88. };
  89. getChildContext() {
  90. // const { location, routerData } = this.props;
  91. const { location, routerData, menuData } = this.props;
  92. return {
  93. location,
  94. // breadcrumbNameMap: getBreadcrumbNameMap(getMenuData(), routerData),
  95. breadcrumbNameMap: getBreadcrumbNameMap(menuData, routerData),
  96. };
  97. }
  98. componentDidMount() {
  99. this.enquireHandler = enquireScreen(mobile => {
  100. this.setState({
  101. isMobile: mobile,
  102. });
  103. });
  104. this.props.dispatch({
  105. type: 'user/fetchCurrent',
  106. });
  107. this.props.dispatch({
  108. type: 'user/fetchMenus',
  109. });
  110. }
  111. componentWillUnmount(){
  112. unenquireScreen(this.enquireHandler);
  113. }
  114. getPageTitle() {
  115. const { routerData, location } = this.props;
  116. const { pathname } = location;
  117. let title = 'Ant Design Pro';
  118. if (routerData[pathname] && routerData[pathname].name) {
  119. title = `${routerData[pathname].name} - Ant Design Pro`;
  120. }
  121. return title;
  122. }
  123. getRedirect = item => {
  124. const me = this
  125. if (item && item.children) {
  126. if (item.children[0] && item.children[0].path) {
  127. redirectData.push({
  128. from: `${item.path}`,
  129. to: `${item.children[0].path}`,
  130. });
  131. item.children.forEach(children => {
  132. me.getRedirect(children);
  133. });
  134. }
  135. }
  136. }
  137. getBashRedirect = () => {
  138. // According to the url parameter to redirect
  139. // 这里是重定向的,重定向到 url 的 redirect 参数所示地址
  140. const urlParams = new URL(window.location.href);
  141. const redirect = urlParams.searchParams.get('redirect');
  142. // Remove the parameters in the url
  143. if (redirect) {
  144. urlParams.searchParams.delete('redirect');
  145. window.history.replaceState(null, 'redirect', urlParams.href);
  146. } else {
  147. const { routerData } = this.props;
  148. // get the first authorized route path in routerData
  149. const authorizedPath = Object.keys(routerData).find(
  150. item => check(routerData[item].authority, item) && item !== '/'
  151. );
  152. return authorizedPath;
  153. }
  154. return redirect;
  155. };
  156. handleMenuCollapse = collapsed => {
  157. this.props.dispatch({
  158. type: 'global/changeLayoutCollapsed',
  159. payload: collapsed,
  160. });
  161. };
  162. handleNoticeClear = type => {
  163. message.success(`清空了${type}`);
  164. this.props.dispatch({
  165. type: 'global/clearNotices',
  166. payload: type,
  167. });
  168. };
  169. handleMenuClick = ({ key }) => {
  170. if (key === 'triggerError') {
  171. this.props.dispatch(routerRedux.push('/exception/trigger'));
  172. return;
  173. }
  174. if (key === 'logout') {
  175. this.props.dispatch({
  176. type: 'login/logout',
  177. });
  178. }
  179. };
  180. handleNoticeVisibleChange = visible => {
  181. if (visible) {
  182. this.props.dispatch({
  183. type: 'global/fetchNotices',
  184. });
  185. }
  186. };
  187. render() {
  188. const {
  189. menuData,
  190. currentUser,
  191. collapsed,
  192. fetchingNotices,
  193. notices,
  194. routerData,
  195. match,
  196. location,
  197. } = this.props;
  198. const bashRedirect = this.getBashRedirect();
  199. menuData.forEach(this.getRedirect);
  200. console.log(menuData);
  201. const layout = (
  202. <Layout>
  203. <SiderMenu
  204. logo={logo}
  205. // 不带Authorized参数的情况下如果没有权限,会强制跳到403界面
  206. // If you do not have the Authorized parameter
  207. // you will be forced to jump to the 403 interface without permission
  208. Authorized={Authorized}
  209. // menuData={getMenuData()}
  210. menuData={menuData}
  211. collapsed={collapsed}
  212. location={location}
  213. isMobile={this.state.isMobile}
  214. onCollapse={this.handleMenuCollapse}
  215. />
  216. <Layout>
  217. <Header style={{ padding: 0 }}>
  218. <GlobalHeader
  219. logo={logo}
  220. currentUser={currentUser}
  221. fetchingNotices={fetchingNotices}
  222. notices={notices}
  223. collapsed={collapsed}
  224. isMobile={this.state.isMobile}
  225. onNoticeClear={this.handleNoticeClear}
  226. onCollapse={this.handleMenuCollapse}
  227. onMenuClick={this.handleMenuClick}
  228. onNoticeVisibleChange={this.handleNoticeVisibleChange}
  229. />
  230. </Header>
  231. <Content style={{ margin: '24px 24px 0', height: '100%' }}>
  232. <Switch>
  233. {redirectData.map(item => (
  234. <Redirect key={item.from} exact from={item.from} to={item.to} />
  235. ))}
  236. {getRoutes(match.path, routerData).map(item => (
  237. <AuthorizedRoute
  238. key={item.key}
  239. path={item.path}
  240. component={item.component}
  241. exact={item.exact}
  242. authority={item.authority}
  243. redirectPath="/exception/403"
  244. />
  245. ))}
  246. <Redirect exact from="/" to={bashRedirect} />
  247. <Route render={NotFound} />
  248. </Switch>
  249. </Content>
  250. <Footer style={{ padding: 0 }}>
  251. <GlobalFooter
  252. links={[
  253. {
  254. key: 'Pro 首页',
  255. title: 'Pro 首页',
  256. href: 'http://pro.ant.design',
  257. blankTarget: true,
  258. },
  259. {
  260. key: 'github',
  261. title: <Icon type="github" />,
  262. href: 'https://github.com/ant-design/ant-design-pro',
  263. blankTarget: true,
  264. },
  265. {
  266. key: 'Ant Design',
  267. title: 'Ant Design',
  268. href: 'http://ant.design',
  269. blankTarget: true,
  270. },
  271. ]}
  272. copyright={
  273. <Fragment>
  274. Copyright <Icon type="copyright" /> 2018 蚂蚁金服体验技术部出品
  275. </Fragment>
  276. }
  277. />
  278. </Footer>
  279. </Layout>
  280. </Layout>
  281. );
  282. return (
  283. <DocumentTitle title={this.getPageTitle()}>
  284. <ContainerQuery query={query}>
  285. {params => <div className={classNames(params)}>{layout}</div>}
  286. </ContainerQuery>
  287. </DocumentTitle>
  288. );
  289. }
  290. }
  291. export default connect(({ user, global, loading }) => ({
  292. menuData: user.menuData,
  293. currentUser: user.currentUser,
  294. collapsed: global.collapsed,
  295. fetchingNotices: loading.effects['global/fetchNotices'],
  296. notices: global.notices,
  297. }))(BasicLayout);