ant-design / pro-components

🏆 Use Ant Design like a Pro!
https://pro-components.antdigital.dev
MIT License
4.31k stars 1.36k forks source link

🐛[BUG]ProLayout - umi环境,登录后请求路由渲染,渲染失败 #499

Closed haoyinag closed 4 years ago

haoyinag commented 4 years ago

🐛 bug 描述

需求是用户登录,然后接口返回用户对应的路由表;

我是在umi3.2.x版本中用的,配置中开启了layout,然后在src/app.tsx的layout中配置menuDataRender,发现怎么处理都不行

📷 复现步骤

1、config中开启layout

......
  routes, 
  layout: {
    name: 'Design Pro', 
  },

2、app中配置

  menuDataRender: async (menuData: any[]) => {
    const res = await request('/api/router');

    menuData.push(res);
    console.log(menuData);
    return menuData;
  },

3、然后只要像上面那样配置了,路由就会空,如果是同步的就是正常

© 版本信息


 "dependencies": {
    "@ant-design/pro-layout": "^5.0.12",
    "@umijs/preset-react": "1.x",
    "@umijs/test": "^3.2.20",
    "antd-dayjs-webpack-plugin": "^1.0.1",
    "cross-env": "^7.0.2",
    "lint-staged": "^10.0.7",
    "prettier": "^1.19.1",
    "react": "^16.12.0",
    "react-dom": "^16.12.0",
    "ts-md5": "^1.2.7",
    "typescript": "^4.0.2",
    "umi": "^3.2.20",
    "yorkie": "^2.0.0"
  }
chenshuai2144 commented 4 years ago

menuDataRender不支持 async 的,你可以存到 state 里面

haoyinag commented 4 years ago

@chenshuai2144 我有尝试存到全局model中,但是咋取哦 我的运行时配置是这样的

// app.tsx
/** 库 */
import React from 'react';
import { history } from 'umi';

/** 国际化,不需要在config配置,否则需要在 src/locales 中配置对应路由等变量 */
import zhCN from 'antd/es/locale/zh_CN';
import { ConfigProvider } from 'antd';

// import { getMenuData } from "@ant-design/pro-layout";

/** 组件--antd优先 */
import { RightRender } from '@/layouts';

/** 本地utils、模块 */
// import router from "@/router";
import logo from '@/assets/logo-small.png';
import { getRoutePathMap } from '@/utils/getMap';
import defaultSettings, { DefaultSettings } from '../config/defaultSettings';

// const { breadcrumb } = getMenuData(router);

/** const/let声明 */
/** 运行时配置文件,可以在这里扩展运行时的能力,比如修改路由、修改 render 方法等。 */
/** layout配置/操作,需要在配置文件开启layout */
export let layout: any = {
  logo, // 产品 Logo
  name: '昂司配送', // 侧边栏头部产品名,默认值为包名
  siderWidth: 180,
  // locale: true,
  // pure: true, // 是否删除框架layout
  // menu: {
  /** 如果开启,需要有对应的src/locals/xxx.ts语言文件匹配 */
  //   locale: true,
  //   defaultOpenAll: false,
  // },
  settings: defaultSettings,
  iconfontUrl: defaultSettings.iconfontUrl,
  breadcrumbRender: (route: any) => {
    let str = '';
    route &&
      route.map((item: any) => {
        return (str += item.breadcrumbName + '/');
      });
    if (route.length === 1) {
      route = [];
    }

    return route;
  },
  // headerRender:(props: BasicLayoutProps) :ReactNode=> {
  // },
  /** 发生错误后的回调(可做一些错误日志上报,打点等) */
  onError: (e: any) => {
    console.log(e);
  },
  /** 发生错误后展示的组件 */
  ErrorComponent: (e: any) => {
    console.log(e);
  },

  /** 点击退出登录的处理逻辑,默认不做处理 */
  logout: (e: any) => {
    console.log(e);
  },
  /** 顶部栏开合 */
  rightRender: (initInfo: any) => {
    return <RightRender />;
  }, // return string || ReactNode;
};

export async function getInitialState(): Promise<{
  currentUser?: any;
  settings?: DefaultSettings;
}> {
  // 如果是登录页面,不执行
  if (history.location.pathname !== '/user/login') {
    try {
      // const currentUser = await queryCurrent();
      return {
        // currentUser,
        settings: defaultSettings,
      };
    } catch (error) {
      history.push('/user/login');
    }
  }
  return {
    settings: defaultSettings,
  };
}

/**
 * 因为config的routes是编译时执行,
 * patchRoutes是运行时执行,
 * 所以可以通过接口请求 router 匹配渲染。
 * 逻辑如下:
 */
// export function patchRoutes({ routes }: any) {
//   routes[0].routes.push({
//     // path: '/demo', // 如果一级路由有path字段,最好有对应的component负责渲染,否则面包屑导航不精确
//     // component: '@/pages/Demo',
//     icon: 'crown',
//     /** access--当 Layout 插件配合 @umijs/plugin-access 插件使用时生效
//      *  权限插件会将用户在这里配置的 access 字符串与当前用户所有权限做匹配,
//      *  如果找到相同的项,并当该权限的值为 false,则当用户访问该路由时,默认展示 403 页面
//      */
//     access: 'canDemo',
//     name: '使用Demo', // 兼容此写法
//     /** menu下字段会覆盖当前层级的同名字段,父级尽量不要使用meta字段,国际化不能正确匹配子级的name */
//     // menu: {
//     //   // icon: 'crown', // 当前icon字段不生效,估计是官方bug
//     //   // hideChildren: false, //  默认为false,在菜单中隐藏他的子项,只展示自己
//     //   // flatMenu: false, // 默认为false 在菜单中只隐藏此项,子项往上提,仍旧展示
//     // },
//     routes: [
//       {
//         path: '/demo/use-hooks',
//         name: '购物车性能优化',
//         title: '购物车性能优化',
//         icon: 'smile',
//         component: '@/pages/Demo/UseHooks',
//       },
//     ],
//   });
//   console.log(routes);
//   return routes;
// }

/** 在初始加载和路由切换时做一些事情--比如用于做埋点统计/比如用于设置标题 */
export function onRouteChange({
  matchedRoutes,
  location,
  routes,
  action,
}: {
  matchedRoutes: any;
  location: any;
  routes: any;
  action: any;
}) {
  // 页面title配置
  document.title = '昂司配送后台管理系统';
  // console.log(location, routes);
  try {
    const { pathname } = location;
    const patern = getRoutePathMap(routes).filter(
      (path: string) => path === pathname,
    );
    /** 地址不完全匹配跳转404 */
    if (patern.length === 0) {
      history.push('/404');
    }
  } catch (error) {
    console.log(error);
  }

  // if (matchedRoutes.length) {
  //   console.log(location.pathname);
  //   document.title = matchedRoutes[matchedRoutes.length - 1].route.title || "";
  //   console.log(document.title);
  // }
}

/** render覆写 render,会直接阻断所有的运行时 */
export function render(oldRender: any) {
  const userInfo = JSON.parse(sessionStorage.getItem('userInfo') || '{}');
  if (userInfo.token) {
    oldRender();
  } else {
    history.push('/user/login');
    oldRender();
  }
}

/** 修改交给 react-dom 渲染时的根组件 */
// export function rootContainer(container,args:{routes,全量路由配置,plugin,运行时插件机制,history,history 实例}) {
export function rootContainer(container: any) {
  // 比如用于在外面包一个 Provider
  const ThemeProvider = () => (
    <div id="root-out">
      <ConfigProvider locale={zhCN}>{container}</ConfigProvider>
    </div>
  );
  return React.createElement(ThemeProvider, null, container);
}