当前位置:首页 » 《随便一记》 » 正文

ant design pro v5 - 03 动态菜单 动态路由(配置路由 动态登录路由 登录菜单)

26 人参与  2023年04月07日 12:29  分类 : 《随便一记》  评论

点击全文阅读


1 动态菜单

        技术思路:配置路由,用户登录后根据用户信息获取后台菜单。


2 动态路由+动态菜单

        技术思路: 使用umijs的运行时修改路由 patchRoutes({ routes })  UMIJS 参考文档 ,react umi 没有守护路由的功能 直接在 app.tsx  的 layout 下的 childrenRender 添加守护路由 实现登录后的菜单路由增加。登录后的菜单由登录接口 加个menu参数获取。 默认路由+动态登录路由+动态菜单

具体操作

1. 动态菜单:

文件:/src/app.tsx
找到 layout  插入 menu 

    menu: {      locale: false,      params: {        userId: initialState?.currentUser?.userid,//引起菜单请求的参数      },      request: async (params, defaultMenuData) => {        const { data } = await getAuthRoutes();        return loopMenuItem(data);      },    },

完全版: 

/** * 映射菜单对应的图标 * */const loopMenuItem = (menus: MenuDataItem[]): MenuDataItem[] =>  menus.map(({ icon, routes, ...item }) => ({    ...item,    icon: icon && <Icon component={icons[icon]} />,    routes: routes && loopMenuItem(routes),  }));// ProLayout 支持的api https://procomponents.ant.design/components/layoutexport const layout: RunTimeLayoutConfig = ({ initialState, setInitialState }) => {  const onCollapse = (collapsed: boolean): void => {    setInitialState({ ...initialState, collapsed }).then();  };  const onSettings = (settings: any): void => {    setInitialState({ ...initialState, settings }).then();  };  return {    // 自定义头内容的方法  我把 自定义侧边栏收缩按钮位置 方在这里    headerContentRender: () => (      <HeaderContent collapse={initialState?.collapsed} onCollapse={onCollapse} />    ),    rightContentRender: () => <RightContent onSettings={onSettings} />,    disableContentMargin: false,    waterMarkProps: {      content: initialState?.currentUser?.name,    },    // 去掉系统自带    collapsedButtonRender: false,    // 指定配置collapsed    collapsed: initialState?.collapsed,    footerRender: () => <Footer />,    onPageChange: () => {      const { location } = history;      // 如果没有登录,重定向到 login      if (!initialState?.currentUser && location.pathname !== loginPath) {        history.push(loginPath);      }    },    menu: {      locale: false,      params: {        userId: initialState?.currentUser?.userid,//引起菜单请求的参数      },      request: async (params, defaultMenuData) => {        const { data } = await getAuthRoutes();        return loopMenuItem(data);      },    },    links: isDev      ? [        <Link key="openapi" to="/umi/plugin/openapi" target="_blank">          <LinkOutlined />          <span>OpenAPI 文档</span>        </Link>,        <Link to="/~docs" key="docs">          <BookOutlined />          <span>业务组件文档</span>        </Link>,      ]      : [],    menuHeaderRender: undefined,    // 自定义 403 页面    // unAccessible: <div>unAccessible</div>,    // 增加一个 loading 的状态    childrenRender: (children, props) => {      // if (initialState?.loading) return <PageLoading />;      const { location, route } = props;      return (        <>          {children}          {/* {!props.location?.pathname?.includes('/login') && (            <SettingDrawer              disableUrlParams              enableDarkTheme              settings={initialState?.settings}              onSettingChange={(settings) => {                setInitialState((preInitialState) => ({                  ...preInitialState,                  settings,                }));              }}            />          )} */}        </>      );    },    ...initialState?.settings,  };};
2. 动态路由+动态菜单

技术思路:
       使用umijs的运行时修改路由 patchRoutes({ routes })  UMIJS 参考文档 ,react umi 没有守护路由的功能 直接在 app.tsx  的 layout 下的 childrenRender 添加守护路由 实现登录后的菜单路由增加。登录后的菜单由登录接口 加个menu参数获取。 默认路由+动态登录路由

文件:config/config.ts

// https://umijs.org/config/import { defineConfig } from 'umi';import { join } from 'path';import defaultSettings from './defaultSettings';import proxy from './proxy';import routes from './routes';const { REACT_APP_ENV } = process.env;export default defineConfig({  hash: true,  antd: {},  dva: {    hmr: true,  },  layout: {    // https://umijs.org/zh-CN/plugins/plugin-layout    locale: false,    siderWidth: 208,    ...defaultSettings,  },  // https://umijs.org/zh-CN/plugins/plugin-locale  locale: {    // default zh-CN    default: 'zh-CN',    antd: true,    // default true, when it is true, will use `navigator.language` overwrite default    baseNavigator: true,  },  dynamicImport: {    loading: '@ant-design/pro-layout/es/PageLoading',  },  targets: {    ie: 11,  },  // umi routes: https://umijs.org/docs/routing  routes,  access: {},  // Theme for antd: https://ant.design/docs/react/customize-theme-cn  theme: {    // 如果不想要 configProvide 动态设置主题需要把这个设置为 default    // 只有设置为 variable, 才能使用 configProvide 动态设置主色调    // https://ant.design/docs/react/customize-theme-variable-cn    'root-entry-name': 'variable',  },  // esbuild is father build tools  // https://umijs.org/plugins/plugin-esbuild  esbuild: {},  title: false,  ignoreMomentLocale: true,  proxy: proxy[REACT_APP_ENV || 'dev'],  manifest: {    basePath: '/',  },  // Fast Refresh 热更新  fastRefresh: {},  openAPI: [    {      requestLibPath: "import { request } from 'umi'",      // 或者使用在线的版本      // schemaPath: "https://gw.alipayobjects.com/os/antfincdn/M%24jrzTTYJN/oneapi.json"      schemaPath: join(__dirname, 'oneapi.json'),      mock: false,    },    {      requestLibPath: "import { request } from 'umi'",      schemaPath: 'https://gw.alipayobjects.com/os/antfincdn/CA1dOm%2631B/openapi.json',      projectName: 'swagger',    },  ],  nodeModulesTransform: {    type: 'none',  },  mfsu: {},  webpack5: {},  exportStatic: {},});

文件:config/routes.ts

import type { MenuDataItem } from '@ant-design/pro-layout';export default [  {    path: '/user',    layout: false,    routes: [      {        path: '/user/login',        layout: false,        name: 'login',        component: '@/pages/modules/user/Login',      },      {        path: '/user/openlogin',        layout: false,        name: 'login',        component: '@/pages/modules/user/openlogin',      },      {        path: '/user',        redirect: '@/pages/modules/user/login',      },      {        name: 'register-result',        icon: 'smile',        path: '/user/register-result',        component: '@/pages/modules/user/register-result',      },      {        name: 'register',        icon: 'smile',        path: '/user/register',        component: '@/pages/modules/user/register',      }    ]  },  {    path: '/',    component: './layouts/commonLayout',    flatMenu: true,    routes: [      // {      //   path: '/welcome',      //   name: '工作台',      //   component: '@/pages/modules/welcome',      // }    ]  },  {    component: '404',  },];

 app.tsx

let extraRoutes;export function patchRoutes({ routes }) {  if (extraRoutes) {    // extraRoutes.forEach((element: any) => {    routes.forEach((route: any) => {      if (route.path == "/") {        route.routes = mergeRoutes(extraRoutes);      }    });    // });  }  console.log("--------------------路由-------------------------", routes);}// export function render(oldRender) {//   getMenu().then((res: any) => {//     if (res.code == 200) {//       extraRoutes = res.result;//       oldRender();//     } else {//       history.push('/login');//       oldRender()//     }//   });// }// /**//  * 映射菜单对应的图标//  * */// const loopMenuItem = (menus: MenuDataItem[]): MenuDataItem[] =>//   menus.map(({ icon, routes, ...item }) => ({//     ...item,//     icon: icon && <Icon component={icons[icon]} />,//     routes: routes && loopMenuItem(routes),//   })//   );/** * @see  https://umijs.org/zh-CN/plugins/plugin-initial-state * */export async function getInitialState(): Promise<{  settings?: Partial<LayoutSettings>;  currentUser?: API.CurrentUser;  loading?: boolean;  collapsed?: boolean;  // menuData?: MenuDataItem[] | undefined;  fetchUserInfo?: () => Promise<API.CurrentUser | undefined>;}> {  const fetchUserInfo = async () => {    try {      const msg = await queryCurrentUser();      return msg.data;    } catch (error) {      // 跳转到指定路由      history.push(loginPath);    }    return undefined;  };  // 如果不是登录页面,执行  if (history.location.pathname !== loginPath) {    const currentUser = await fetchUserInfo();    return {      fetchUserInfo,      currentUser,      settings: defaultSettings,    };  }  return {    fetchUserInfo,    settings: defaultSettings,  };}// ProLayout 支持的api https://procomponents.ant.design/components/layoutexport const layout: RunTimeLayoutConfig = ({ initialState, setInitialState }) => {  const onCollapse = (collapsed: boolean): void => {    setInitialState({ ...initialState, collapsed }).then();  };  const onSettings = (settings: any): void => {    setInitialState({ ...initialState, settings }).then();  };  return {    // 自定义头内容的方法  我把 自定义侧边栏收缩按钮位置 方在这里    headerContentRender: () => (      <HeaderContent collapse={initialState?.collapsed} onCollapse={onCollapse} />    ),    rightContentRender: () => <RightContent onSettings={onSettings} />,    disableContentMargin: false,    waterMarkProps: {      content: initialState?.currentUser?.name,    },    // 去掉系统自带    collapsedButtonRender: false,    // 指定配置collapsed    collapsed: initialState?.collapsed,    footerRender: () => <Footer />,    onPageChange: () => {      const { location } = history;      // 如果没有登录,重定向到 login      if (!initialState?.currentUser && location.pathname !== loginPath) {        history.push(loginPath);      }    },    // menuDataRender: () => { return fixMenuItemIcon(initialState.menuData) },    menu: {      locale: false,      params: {        userId: initialState?.currentUser?.userid,//引起菜单请求的参数      },      request: async (params, defaultMenuData) => {        // const msg = await getMenu();        return fixMenuItemIcon(initialState?.currentUser?.menu);      },    },    links: isDev      ? [        <Link key="openapi" to="/umi/plugin/openapi" target="_blank">          <LinkOutlined />          <span>OpenAPI 文档</span>        </Link>,        <Link to="/~docs" key="docs">          <BookOutlined />          <span>业务组件文档</span>        </Link>,      ]      : [],    menuHeaderRender: undefined,    // 自定义 403 页面    // unAccessible: <div>unAccessible</div>,    // 增加一个 loading 的状态    childrenRender: (children, props) => {      // if (initialState?.loading) return <PageLoading />;      const { location, route } = props;      if (history.location.pathname !== loginPath && initialState?.currentUser?.menu) {        // 路由守卫        // const msg = await getMenu();        extraRoutes = initialState?.currentUser?.menu;        console.log("--------------------路由01-------------------------", extraRoutes);        patchRoutes(route);      }      return (        <>          {children}          {/* {!props.location?.pathname?.includes('/login') && (            <SettingDrawer              disableUrlParams              enableDarkTheme              settings={initialState?.settings}              onSettingChange={(settings) => {                setInitialState((preInitialState) => ({                  ...preInitialState,                  settings,                }));              }}            />          )} */}        </>      );    },    ...initialState?.settings,  };};

 src/utils/fixMenuItemIcon.tsx

import React from 'react';import type { MenuDataItem } from '@ant-design/pro-layout';import * as allIcons from '@ant-design/icons';const fixMenuItemIcon = (menus: MenuDataItem[], iconType = 'Outlined'): MenuDataItem[] => {   menus.forEach((item) => {      const { icon, routes } = item      if (typeof icon === 'string' && icon != null) {         const fixIconName = icon.slice(0, 1).toLocaleUpperCase() + icon.slice(1) + iconType         // eslint-disable-next-line no-param-reassign         item.icon = React.createElement(allIcons[fixIconName] || allIcons[icon])      }      // eslint-disable-next-line no-param-reassign,@typescript-eslint/no-unused-expressions      routes && routes.length > 0 ? item.routes = fixMenuItemIcon(routes) : null   });   return menus};export default fixMenuItemIcon;

 src/utils/roles.tsx
 

// import { getUserPerm } from '@/services/menu';// import router from 'umi/router';import React from 'react';import Icon from '@ant-design/icons';import * as icons from '@ant-design/icons';import { dynamic, RunTimeLayoutConfig } from 'umi';import { SmileOutlined, HeartOutlined } from '@ant-design/icons'export function mergeRoutes(routes) {    if (!Array.isArray(routes)) return [];    return routes.map(route => {        let module = route.component;        if (route.component) {            route.component = (component => {                if (typeof component === 'object') {                    return component;                }                // 封装一个异步组件                return dynamic({                    loader: () => import(`../pages/${module}/index.tsx`)                });            })(route.component);        }        if (route.routes) {            route.routes = mergeRoutes(route.routes);        }        return route;    });}

注意: 登陆子模块要 符合 /src/pages/  模块名称 /index.tsx 的组合


点击全文阅读


本文链接:http://m.zhangshiyu.com/post/58734.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1