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

2023-10-26

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/layout
export 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/layout
export 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 的组合

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ant design pro v5 - 03 动态菜单 动态路由(配置路由 动态登录路由 登录菜单) 的相关文章

随机推荐

  • 全球及中国芯片产业研发方向与投资规模预测报告2022版

    全球及中国芯片产业研发方向与投资规模预测报告2022版 HS HS HS HS HS HS HS HS HS HS HS HS 修订日期 2021年11月 搜索鸿晟信合研究院查看官网更多内容 第一章 芯片相关概念介绍 1 1 芯片的概念 1
  • QML实现Label的文字选择与右键各操作

    在QML中 原生的Label是不能够进行鼠标的选中 复制 全选等操作的 仅仅只能用于简单的展示文字 但是在实际开发中 往往我们需要给用户展示一些信息 而且要支持可以用鼠标进行选择文字 并进行复制操作 所以 用QML中的Label控件显然是不
  • 信息安全意识主题分享-数据安全

    微盟删库 事件沸沸扬扬 这次重大的数据违规行为 导致微盟的股市市值暴跌12亿港币 影响巨大 本文主要为大家介绍针对数据安全的威胁和风险 有哪些安全防护措施 一 数据简介 1 数据的形态 数据主要可以分为两种形态 也就是平时常见的数据的表现方
  • coco游戏android.mk

    LOCAL PATH call my dir include CLEAR VARS LOCAL MODULE game shared LOCAL MODULE FILENAME libgame LOCAL CPP EXTENSION cc
  • 4G版本云音响设置教程阿里云平台版本

    4G版本云音响设置教程介绍 第一章 介绍了在阿里云物联网平台生一个设备使用的三元素 第二章 转换阿里云三元素 为MQTT参数 并下载到设备中 第三章 阿里云物联网套件协议使用说明 如何发送数据至设备并播放 本文目录引导 目录 4G版本云音响
  • egg-jwt的使用

    安装 npm install egg jwt save 配置 config config default js config jwt secret zidingyi 自定义 token 的加密条件字符串 config plugin js j
  • RPC答疑篇

    声明 本篇文章及代码仅供学习交流 严禁用于商业用途 否则由此产生的一切后果均与作者无关 上一篇的rpc发出来后 虽然我觉得我已经写得很细致了 但还是收到了很多私信说是跑不成功 所以再发 水 一篇 关于如何操作的文章 鉴于很多人没有某店的账号
  • Spring-Cloud-Alibaba之Dubbo

    在微服务构架中 不可避免的要遇到服务间的调用 目前的方式是通过RPC或者是rest的http接口调用 spring cloud中很多都使用的feign来做服务调用 在spring cloud alibaba的套装中我们使用dubbo来替换掉
  • 登录认证功能的统一拦截技术(拦截器)

    目录 1 说明 2 使用方法 1 定义拦截器 2 注册配置拦截器 3 示例 3 interceptor详细说明 1 拦截路径 2 执行流程 3 过滤器和拦截器的区别 4 登录校验的拦截器实现 5 全局异常处理 补充说明 1 说明 拦截器是一
  • Redis的三种模式——主从复制、哨兵、集群

    目录 一 Redis模式 二 Redis主从复制 2 1 主从复制概述 2 2 主从复制 2 3 Redis主从复制流程 2 4 搭建Redis主从复制 2 4 1 安装Redis 2 4 2 修改Master节点配置文件 192 168
  • 软件测试实战项目【电商、银行、商城、金融、医药、电商】

    最近 不少读者托我找一个能实际练手的测试项目 开始 我觉得这是很简单的一件事 但当我付诸行动时 却发现 要找到一个对新手友好的练手项目 着实困难 这是博主收集了很久才弄到的 希望可以帮助到你 由于项目太多了就不一一介绍了 只介绍这一个 电商
  • MySQL适合与不适合建索引的情况

    适合建索引情况 主键自动建立唯一索引 频繁作为查询条件的字段应该创建索引 查询中与其它表关联的字段 外键关系建立索引 Where条件里用不到的字段不创建索引 单键 组合索引的选择问题 在高并发下倾向创建组合索引 查询中排序的字段 排序字段若
  • 三个例题教会你二分法

    二分法 二分法 例题 数组查找 错误版本号 插入位置 二分法 先看一个很有意思的段子 有一天小明到图书馆借了 N 本书 出图书馆的时候 警报响了 于是保安把小明拦下 要检查一下哪本书没有登记出借 小明正准备把每一本书在报警器下过一下 以找出
  • 【毕设选题】深度学习人体语义分割在弹幕防遮挡上的实现 - python

    文章目录 1 课题背景 2 技术原理和方法 2 1基本原理 2 2 技术选型和方法 3 实例分割 4 实现效果 5 最后 1 前言 深度学习人体语义分割在弹幕防遮挡上的应用 学长这里给一个题目综合评分 每项满分5分 难度系数 3分 工作量
  • ad取消覆铜_【学院推荐】PCB工程师不得不看:超级实用AD常用快捷键总结

    快捷键的实用 极大的提高了大家工作中的效率 因此小编我特意帮大家搜集整理很多关于AD方面的常用快捷键 希望对大家有所帮助 一 PCB中常用快捷键 R L 输出PCB中所有网络的布线长度 Ctrl 左键点击 对正在布的线完成自动布线连接 M
  • python之word文档生成

    python之word文档生成 python docx官方文档 python docx操作word文档 python文件读取操作 excel文件操作 python docx官方文档 Document objects python docx
  • angular html原理,angular数据双向绑定的原理是什么?

    Angular是通过脏检测来进行双向数据绑定 所谓的双向绑定 无非是从界面的操作能实时反映到数据 数据的变更能实时展现到界面 angular数据双向绑定的原理 页面中每绑定一个数据或者事件时 就会向 watch队列中加入一条 watch 当
  • 几款Android 应用自动化测试工具

    简述 本文介绍几款流行的 Android应用自动化测试工具 Monkey测试 随机测试 压力测试 运行在模拟器或实际设备中 MonkeyRunner测试 操作简单 可录制测试脚本 可视化操作 主要生成坐标的自动化操作 移植性不强 Robot
  • keil编译问题error C100: unprintable character... 和WARNING L16: UNCALLED SEGMENT...

    keil编译出现问题如下 问题 error C100 unprintable character 0xA3 skipped 原因 keil里面出现了中文输入法时写的标点 程序中不能出现全角的任何字符 包括空格在内的任何标点符号和空格都只能在
  • ant design pro v5 - 03 动态菜单 动态路由(配置路由 动态登录路由 登录菜单)

    1 动态菜单 技术思路 配置路由 用户登录后根据用户信息获取后台菜单 2 动态路由 动态菜单 技术思路 使用umijs的运行时修改路由 patchRoutes routes UMIJS 参考文档 react umi 没有守护路由的功能 直接