当前位置:   article > 正文

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

ant design pro 动态菜单

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. /**
  2. * 映射菜单对应的图标
  3. * */
  4. const loopMenuItem = (menus: MenuDataItem[]): MenuDataItem[] =>
  5. menus.map(({ icon, routes, ...item }) => ({
  6. ...item,
  7. icon: icon && <Icon component={icons[icon]} />,
  8. routes: routes && loopMenuItem(routes),
  9. })
  10. );
  11. // ProLayout 支持的api https://procomponents.ant.design/components/layout
  12. export const layout: RunTimeLayoutConfig = ({ initialState, setInitialState }) => {
  13. const onCollapse = (collapsed: boolean): void => {
  14. setInitialState({ ...initialState, collapsed }).then();
  15. };
  16. const onSettings = (settings: any): void => {
  17. setInitialState({ ...initialState, settings }).then();
  18. };
  19. return {
  20. // 自定义头内容的方法 我把 自定义侧边栏收缩按钮位置 方在这里
  21. headerContentRender: () => (
  22. <HeaderContent collapse={initialState?.collapsed} onCollapse={onCollapse} />
  23. ),
  24. rightContentRender: () => <RightContent onSettings={onSettings} />,
  25. disableContentMargin: false,
  26. waterMarkProps: {
  27. content: initialState?.currentUser?.name,
  28. },
  29. // 去掉系统自带
  30. collapsedButtonRender: false,
  31. // 指定配置collapsed
  32. collapsed: initialState?.collapsed,
  33. footerRender: () => <Footer />,
  34. onPageChange: () => {
  35. const { location } = history;
  36. // 如果没有登录,重定向到 login
  37. if (!initialState?.currentUser && location.pathname !== loginPath) {
  38. history.push(loginPath);
  39. }
  40. },
  41. menu: {
  42. locale: false,
  43. params: {
  44. userId: initialState?.currentUser?.userid,//引起菜单请求的参数
  45. },
  46. request: async (params, defaultMenuData) => {
  47. const { data } = await getAuthRoutes();
  48. return loopMenuItem(data);
  49. },
  50. },
  51. links: isDev
  52. ? [
  53. <Link key="openapi" to="/umi/plugin/openapi" target="_blank">
  54. <LinkOutlined />
  55. <span>OpenAPI 文档</span>
  56. </Link>,
  57. <Link to="/~docs" key="docs">
  58. <BookOutlined />
  59. <span>业务组件文档</span>
  60. </Link>,
  61. ]
  62. : [],
  63. menuHeaderRender: undefined,
  64. // 自定义 403 页面
  65. // unAccessible: <div>unAccessible</div>,
  66. // 增加一个 loading 的状态
  67. childrenRender: (children, props) => {
  68. // if (initialState?.loading) return <PageLoading />;
  69. const { location, route } = props;
  70. return (
  71. <>
  72. {children}
  73. {/* {!props.location?.pathname?.includes('/login') && (
  74. <SettingDrawer
  75. disableUrlParams
  76. enableDarkTheme
  77. settings={initialState?.settings}
  78. onSettingChange={(settings) => {
  79. setInitialState((preInitialState) => ({
  80. ...preInitialState,
  81. settings,
  82. }));
  83. }}
  84. />
  85. )} */}
  86. </>
  87. );
  88. },
  89. ...initialState?.settings,
  90. };
  91. };
  • 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 的组合

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/392051
推荐阅读
相关标签
  

闽ICP备14008679号