Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

实现antd pro v2的动态路由,基于人人的框架 #292

Open
hehongwei44 opened this issue Oct 18, 2018 · 1 comment
Open

实现antd pro v2的动态路由,基于人人的框架 #292

hehongwei44 opened this issue Oct 18, 2018 · 1 comment

Comments

@hehongwei44
Copy link
Owner

人人框架地址:https://www.renren.io/

@hehongwei44
Copy link
Owner Author

hehongwei44 commented Oct 18, 2018

实现脚本如下,修改文件为BasicLayout.js

import React from 'react';
import { Layout } from 'antd';
import DocumentTitle from 'react-document-title';
import isEqual from 'lodash/isEqual';
import arrayIncludes from 'lodash/_arrayIncludes';
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';
import Exception403 from '../pages/Exception/403';

const { Content } = Layout;

// Conversion router to menu.
function formatter(data, 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;
      }
      if (item.path) {
        const result = {
          ...item,
          locale,
          authority: item.authority || parentAuthority,
        };
        if (item.routes) {
          const children = formatter(item.routes, item.authority, locale);
          // Reduce memory usage
          result.children = children;
        }
        delete result.routes;
        return result;
      }

      return null;
    })
    .filter(item => item);
}

const memoizeOneFormatter = memoizeOne(formatter, 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);
    this.getBreadcrumbNameMap = memoizeOne(this.getBreadcrumbNameMap, isEqual);
    this.breadcrumbNameMap = this.getBreadcrumbNameMap();
    this.matchParamsPath = memoizeOne(this.matchParamsPath, isEqual);
  }

  state = {
    rendering: true,
    isMobile: false,
    menuData: this.getMenuData(),
  };

  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(preProps) {
    // After changing to phone mode,
    // if collapsed is true, you need to click twice to display
    this.breadcrumbNameMap = this.getBreadcrumbNameMap();
    const { isMobile } = this.state;
    const { collapsed } = this.props;
    if (isMobile && !preProps.isMobile && !collapsed) {
      this.handleMenuCollapse(false);
    }
  }

  componentWillUnmount() {
    cancelAnimationFrame(this.renderRef);
    unenquireScreen(this.enquireHandler);
  }

  getContext() {
    const { location } = this.props;
    return {
      location,
      breadcrumbNameMap: this.breadcrumbNameMap,
    };
  }

  getMenuData() {
    // remoteRouters 为模拟数据
    const remoteRouters = [
      {"menuId":1,"parentId":0,"parentName":null,"name":"系统管理","url":null,"perms":null,"type":0,"icon":"system","orderNum":0,"open":null,"list":null},
      {"menuId":2,"parentId":1,"parentName":"系统管理","name":"管理员列表","url":"/dashboard/workplace","perms":null,"type":1,"icon":"admin","orderNum":1,"open":null,"list":null},
      {"menuId":3,"parentId":1,"parentName":"系统管理","name":"角色管理","url":"/dashboard/monitor","perms":null,"type":1,"icon":"role","orderNum":2,"open":null,"list":null},
      {"menuId":4,"parentId":1,"parentName":"系统管理","name":"菜单管理","url":"/dashboard/analysis","perms":null,"type":1,"icon":"menu","orderNum":3,"open":null,"list":null},
      {"menuId":5,"parentId":1,"parentName":"系统管理","name":"SQL监控","url":"/form/basic-form","perms":null,"type":1,"icon":"sql","orderNum":4,"open":null,"list":null},
      {"menuId":6,"parentId":1,"parentName":"系统管理","name":"定时任务","url":"/form/step-form","perms":null,"type":1,"icon":"job","orderNum":5,"open":null,"list":null},
      {"menuId":7,"parentId":6,"parentName":"定时任务","name":"查看","url":"/form/advanced-form","perms":"sys:schedule:list,sys:schedule:info","type":2,"icon":null,"orderNum":0,"open":null,"list":null},
      {"menuId":8,"parentId":6,"parentName":"定时任务","name":"新增","url":"/list/table-list","perms":"sys:schedule:save","type":2,"icon":null,"orderNum":0,"open":null,"list":null},
      {"menuId":9,"parentId":6,"parentName":"定时任务","name":"修改","url":"/list/basic-list","perms":"sys:schedule:update","type":2,"icon":null,"orderNum":0,"open":null,"list":null},
      {"menuId":10,"parentId":6,"parentName":"定时任务","name":"删除","url":"/list/card-list","perms":"sys:schedule:delete","type":2,"icon":null,"orderNum":0,"open":null,"list":null},
      {"menuId":10,"parentId":6,"parentName":"定时任务","name":"删除","url":"/list/card-list2","perms":"sys:schedule:delete","type":2,"icon":null,"orderNum":0,"open":null,"list":null}
      ];
    const {
      route: { routes },
    } = this.props;

    // todo
    const routeHash = {};
    const nativeRouter = memoizeOneFormatter(routes); // 本地所有的路由数据
    remoteRouters.forEach((route) => {
      if (route.url) {
        for (let i = 0; i < nativeRouter.length; i+=1) {
          const target = nativeRouter[i];
          if (target.children) {
            const childRoutes = target.children;
            for(let j = 0; j < childRoutes.length; j+=1) {
              const keys = Object.keys(routeHash);
              const key = `${i},${j}`;
              const childRoute = childRoutes[j];

              if (!arrayIncludes(keys,key)) {
                if (route.url !== childRoute.path) {
                  childRoute.hideInMenu = true;
                } else {
                  childRoute.hideInMenu = false;
                  routeHash[key] = true;
                }
              }
            }
          }
        }
      }

    });
    // todo
    /*console.log(nativeRouter)
    console.log(routeHash)*/
    return nativeRouter;
  }

  /**
   * 获取面包屑映射
   * @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;
  }

  matchParamsPath = pathname => {
    const pathKey = Object.keys(this.breadcrumbNameMap).find(key =>
      pathToRegexp(key).test(pathname)
    );
    return this.breadcrumbNameMap[pathKey];
  };

  getPageTitle = pathname => {
    const currRouterData = this.matchParamsPath(pathname);

    if (!currRouterData) {
      return 'Ant Design Pro';
    }
    const message = formatMessage({
      id: currRouterData.locale || currRouterData.name,
      defaultMessage: currRouterData.name,
    });
    return `${message} - Ant Design Pro`;
  };

  getLayoutStyle = () => {
    const { isMobile } = this.state;
    const { fixSiderbar, collapsed, layout } = this.props;
    if (fixSiderbar && layout !== 'topmenu' && !isMobile) {
      return {
        paddingLeft: collapsed ? '80px' : '256px',
      };
    }
    return null;
  };

  getContentStyle = () => {
    const { fixedHeader } = this.props;
    return {
      margin: '24px 24px 0',
      paddingTop: fixedHeader ? 64 : 0,
    };
  };

  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
    const { rendering } = this.state;
    if ((rendering || process.env.NODE_ENV === 'production') && APP_TYPE !== 'site') {
      return null;
    }
    return <SettingDrawer />;
  }


  render() {
    const {
      navTheme,
      layout: PropsLayout,
      children,
      location: { pathname },
    } = this.props;
    const { isMobile, menuData } = this.state;
    const isTop = PropsLayout === 'topmenu';
    const routerConfig = this.matchParamsPath(pathname);
    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()}>
            <Authorized
              authority={routerConfig && routerConfig.authority}
              noMatch={<Exception403 />}
            >
              {children}
            </Authorized>
          </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>
        {this.renderSettingDrawer()}
      </React.Fragment>
    );
  }
}

export default connect(({ global, setting }) => ({
  collapsed: global.collapsed,
  layout: setting.layout,
  ...setting,
}))(BasicLayout);


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant