[web教学] ant design pro v5 - 03 动态菜单 动态路由(设置路由 动态登录路由 登录菜

[复制链接]
查看1138 | 回复0 | 2023-8-23 12:14:16 | 显示全部楼层 |阅读模式 来自 中国北京
1 动态菜单

        技能思绪:设置路由,用户登录后根据用户信息获取背景菜单。

2 动态路由+动态菜单


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

详细使用




  • 1. 动态菜单:
文件:/src/app.tsx
找到 layout  插入 menu 
  1.     menu: {
  2.       locale: false,
  3.       params: {
  4.         userId: initialState?.currentUser?.userid,//引起菜单请求的参数
  5.       },
  6.       request: async (params, defaultMenuData) => {
  7.         const { data } = await getAuthRoutes();
  8.         return loopMenuItem(data);
  9.       },
  10.     },
复制代码
完全版: 
  1. /** * 映射菜单对应的图标 * */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: {
  2.       locale: false,
  3.       params: {
  4.         userId: initialState?.currentUser?.userid,//引起菜单请求的参数
  5.       },
  6.       request: async (params, defaultMenuData) => {
  7.         const { data } = await getAuthRoutes();
  8.         return loopMenuItem(data);
  9.       },
  10.     },    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
  1. // https://umijs.org/config/
  2. import { defineConfig } from 'umi';
  3. import { join } from 'path';
  4. import defaultSettings from './defaultSettings';
  5. import proxy from './proxy';
  6. import routes from './routes';
  7. const { REACT_APP_ENV } = process.env;
  8. export default defineConfig({
  9.   hash: true,
  10.   antd: {},
  11.   dva: {
  12.     hmr: true,
  13.   },
  14.   layout: {
  15.     // https://umijs.org/zh-CN/plugins/plugin-layout
  16.     locale: false,
  17.     siderWidth: 208,
  18.     ...defaultSettings,
  19.   },
  20.   // https://umijs.org/zh-CN/plugins/plugin-locale
  21.   locale: {
  22.     // default zh-CN
  23.     default: 'zh-CN',
  24.     antd: true,
  25.     // default true, when it is true, will use `navigator.language` overwrite default
  26.     baseNavigator: true,
  27.   },
  28.   dynamicImport: {
  29.     loading: '@ant-design/pro-layout/es/PageLoading',
  30.   },
  31.   targets: {
  32.     ie: 11,
  33.   },
  34.   // umi routes: https://umijs.org/docs/routing
  35.   routes,
  36.   access: {},
  37.   // Theme for antd: https://ant.design/docs/react/customize-theme-cn
  38.   theme: {
  39.     // 如果不想要 configProvide 动态设置主题需要把这个设置为 default
  40.     // 只有设置为 variable, 才能使用 configProvide 动态设置主色调
  41.     // https://ant.design/docs/react/customize-theme-variable-cn
  42.     'root-entry-name': 'variable',
  43.   },
  44.   // esbuild is father build tools
  45.   // https://umijs.org/plugins/plugin-esbuild
  46.   esbuild: {},
  47.   title: false,
  48.   ignoreMomentLocale: true,
  49.   proxy: proxy[REACT_APP_ENV || 'dev'],
  50.   manifest: {
  51.     basePath: '/',
  52.   },
  53.   // Fast Refresh 热更新
  54.   fastRefresh: {},
  55.   openAPI: [
  56.     {
  57.       requestLibPath: "import { request } from 'umi'",
  58.       // 或者使用在线的版本
  59.       // schemaPath: "https://gw.alipayobjects.com/os/antfincdn/M%24jrzTTYJN/oneapi.json"
  60.       schemaPath: join(__dirname, 'oneapi.json'),
  61.       mock: false,
  62.     },
  63.     {
  64.       requestLibPath: "import { request } from 'umi'",
  65.       schemaPath: 'https://gw.alipayobjects.com/os/antfincdn/CA1dOm%2631B/openapi.json',
  66.       projectName: 'swagger',
  67.     },
  68.   ],
  69.   nodeModulesTransform: {
  70.     type: 'none',
  71.   },
  72.   mfsu: {},
  73.   webpack5: {},
  74.   exportStatic: {},
  75. });
复制代码
文件:config/routes.ts
  1. import type { MenuDataItem } from '@ant-design/pro-layout';
  2. export default [
  3.   {
  4.     path: '/user',
  5.     layout: false,
  6.     routes: [
  7.       {
  8.         path: '/user/login',
  9.         layout: false,
  10.         name: 'login',
  11.         component: '@/pages/modules/user/Login',
  12.       },
  13.       {
  14.         path: '/user/openlogin',
  15.         layout: false,
  16.         name: 'login',
  17.         component: '@/pages/modules/user/openlogin',
  18.       },
  19.       {
  20.         path: '/user',
  21.         redirect: '@/pages/modules/user/login',
  22.       },
  23.       {
  24.         name: 'register-result',
  25.         icon: 'smile',
  26.         path: '/user/register-result',
  27.         component: '@/pages/modules/user/register-result',
  28.       },
  29.       {
  30.         name: 'register',
  31.         icon: 'smile',
  32.         path: '/user/register',
  33.         component: '@/pages/modules/user/register',
  34.       }
  35.     ]
  36.   },
  37.   {
  38.     path: '/',
  39.     component: './layouts/commonLayout',
  40.     flatMenu: true,
  41.     routes: [
  42.       // {
  43.       //   path: '/welcome',
  44.       //   name: '工作台',
  45.       //   component: '@/pages/modules/welcome',
  46.       // }
  47.     ]
  48.   },
  49.   {
  50.     component: '404',
  51.   },
  52. ];
复制代码
 app.tsx
  1. let extraRoutes;
  2. export function patchRoutes({ routes }) {
  3.   if (extraRoutes) {
  4.     // extraRoutes.forEach((element: any) => {
  5.     routes.forEach((route: any) => {
  6.       if (route.path == "/") {
  7.         route.routes = mergeRoutes(extraRoutes);
  8.       }
  9.     });
  10.     // });
  11.   }
  12.   console.log("--------------------路由-------------------------", routes);
  13. }
  14. // export function render(oldRender) {
  15. //   getMenu().then((res: any) => {
  16. //     if (res.code == 200) {
  17. //       extraRoutes = res.result;
  18. //       oldRender();
  19. //     } else {
  20. //       history.push('/login');
  21. //       oldRender()
  22. //     }
  23. //   });
  24. // }
  25. // /**
  26. //  * 映射菜单对应的图标
  27. //  * */
  28. // const loopMenuItem = (menus: MenuDataItem[]): MenuDataItem[] =>
  29. //   menus.map(({ icon, routes, ...item }) => ({
  30. //     ...item,
  31. //     icon: icon && <Icon component={icons[icon]} />,
  32. //     routes: routes && loopMenuItem(routes),
  33. //   })
  34. //   );
  35. /**
  36. * @see  https://umijs.org/zh-CN/plugins/plugin-initial-state
  37. * */
  38. export async function getInitialState(): Promise<{
  39.   settings?: Partial<LayoutSettings>;
  40.   currentUser?: API.CurrentUser;
  41.   loading?: boolean;
  42.   collapsed?: boolean;
  43.   // menuData?: MenuDataItem[] | undefined;
  44.   fetchUserInfo?: () => Promise<API.CurrentUser | undefined>;
  45. }> {
  46.   const fetchUserInfo = async () => {
  47.     try {
  48.       const msg = await queryCurrentUser();
  49.       return msg.data;
  50.     } catch (error) {
  51.       // 跳转到指定路由
  52.       history.push(loginPath);
  53.     }
  54.     return undefined;
  55.   };
  56.   // 如果不是登录页面,执行
  57.   if (history.location.pathname !== loginPath) {
  58.     const currentUser = await fetchUserInfo();
  59.     return {
  60.       fetchUserInfo,
  61.       currentUser,
  62.       settings: defaultSettings,
  63.     };
  64.   }
  65.   return {
  66.     fetchUserInfo,
  67.     settings: defaultSettings,
  68.   };
  69. }
  70. // ProLayout 支持的api https://procomponents.ant.design/components/layout
  71. export const layout: RunTimeLayoutConfig = ({ initialState, setInitialState }) => {
  72.   const onCollapse = (collapsed: boolean): void => {
  73.     setInitialState({ ...initialState, collapsed }).then();
  74.   };
  75.   const onSettings = (settings: any): void => {
  76.     setInitialState({ ...initialState, settings }).then();
  77.   };
  78.   return {
  79.     // 自定义头内容的方法  我把 自定义侧边栏收缩按钮位置 方在这里
  80.     headerContentRender: () => (
  81.       <HeaderContent collapse={initialState?.collapsed} onCollapse={onCollapse} />
  82.     ),
  83.     rightContentRender: () => <RightContent onSettings={onSettings} />,
  84.     disableContentMargin: false,
  85.     waterMarkProps: {
  86.       content: initialState?.currentUser?.name,
  87.     },
  88.     // 去掉系统自带
  89.     collapsedButtonRender: false,
  90.     // 指定配置collapsed
  91.     collapsed: initialState?.collapsed,
  92.     footerRender: () => <Footer />,
  93.     onPageChange: () => {
  94.       const { location } = history;
  95.       // 如果没有登录,重定向到 login
  96.       if (!initialState?.currentUser && location.pathname !== loginPath) {
  97.         history.push(loginPath);
  98.       }
  99.     },
  100.     // menuDataRender: () => { return fixMenuItemIcon(initialState.menuData) },
  101.     menu: {
  102.       locale: false,
  103.       params: {
  104.         userId: initialState?.currentUser?.userid,//引起菜单请求的参数
  105.       },
  106.       request: async (params, defaultMenuData) => {
  107.         // const msg = await getMenu();
  108.         return fixMenuItemIcon(initialState?.currentUser?.menu);
  109.       },
  110.     },
  111.     links: isDev
  112.       ? [
  113.         <Link key="openapi" to="/umi/plugin/openapi" target="_blank">
  114.           <LinkOutlined />
  115.           <span>OpenAPI 文档</span>
  116.         </Link>,
  117.         <Link to="/~docs" key="docs">
  118.           <BookOutlined />
  119.           <span>业务组件文档</span>
  120.         </Link>,
  121.       ]
  122.       : [],
  123.     menuHeaderRender: undefined,
  124.     // 自定义 403 页面
  125.     // unAccessible: <div>unAccessible</div>,
  126.     // 增加一个 loading 的状态
  127.     childrenRender: (children, props) => {
  128.       // if (initialState?.loading) return <PageLoading />;
  129.       const { location, route } = props;
  130.       if (history.location.pathname !== loginPath && initialState?.currentUser?.menu) {
  131.         // 路由守卫
  132.         // const msg = await getMenu();
  133.         extraRoutes = initialState?.currentUser?.menu;
  134.         console.log("--------------------路由01-------------------------", extraRoutes);
  135.         patchRoutes(route);
  136.       }
  137.       return (
  138.         <>
  139.           {children}
  140.           {/* {!props.location?.pathname?.includes('/login') && (
  141.             <SettingDrawer
  142.               disableUrlParams
  143.               enableDarkTheme
  144.               settings={initialState?.settings}
  145.               onSettingChange={(settings) => {
  146.                 setInitialState((preInitialState) => ({
  147.                   ...preInitialState,
  148.                   settings,
  149.                 }));
  150.               }}
  151.             />
  152.           )} */}
  153.         </>
  154.       );
  155.     },
  156.     ...initialState?.settings,
  157.   };
  158. };
复制代码
 src/utils/fixMenuItemIcon.tsx
  1. import React from 'react';
  2. import type { MenuDataItem } from '@ant-design/pro-layout';
  3. import * as allIcons from '@ant-design/icons';
  4. const fixMenuItemIcon = (menus: MenuDataItem[], iconType = 'Outlined'): MenuDataItem[] => {
  5.    menus.forEach((item) => {
  6.       const { icon, routes } = item
  7.       if (typeof icon === 'string' && icon != null) {
  8.          const fixIconName = icon.slice(0, 1).toLocaleUpperCase() + icon.slice(1) + iconType
  9.          // eslint-disable-next-line no-param-reassign
  10.          item.icon = React.createElement(allIcons[fixIconName] || allIcons[icon])
  11.       }
  12.       // eslint-disable-next-line no-param-reassign,@typescript-eslint/no-unused-expressions
  13.       routes && routes.length > 0 ? item.routes = fixMenuItemIcon(routes) : null
  14.    });
  15.    return menus
  16. };
  17. export default fixMenuItemIcon;
复制代码
 src/utils/roles.tsx
 
  1. // import { getUserPerm } from '@/services/menu';
  2. // import router from 'umi/router';
  3. import React from 'react';
  4. import Icon from '@ant-design/icons';
  5. import * as icons from '@ant-design/icons';
  6. import { dynamic, RunTimeLayoutConfig } from 'umi';
  7. import { SmileOutlined, HeartOutlined } from '@ant-design/icons'
  8. export function mergeRoutes(routes) {
  9.     if (!Array.isArray(routes)) return [];
  10.     return routes.map(route => {
  11.         let module = route.component;
  12.         if (route.component) {
  13.             route.component = (component => {
  14.                 if (typeof component === 'object') {
  15.                     return component;
  16.                 }
  17.                 // 封装一个异步组件
  18.                 return dynamic({
  19.                     loader: () => import(`../pages/${module}/index.tsx`)
  20.                 });
  21.             })(route.component);
  22.         }
  23.         if (route.routes) {
  24.             route.routes = mergeRoutes(route.routes);
  25.         }
  26.         return route;
  27.     });
  28. }
复制代码
留意: 登岸子模块要 符合 /src/pages/  模块名称 /index.tsx 的组合

来源:https://blog.csdn.net/haiyabtx/article/details/127363731
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则