阅读 578

Ant Design Pro V5精讲(实践篇三):动态菜单及权限控制

  • 用户需求场景

  • 一些软件产品,设计时就规划好了系统的操作角色,这些操作角色相对固定,例如:客户,管理员,这种场景完全只需要前端的路由与菜单配置方案,无需要动态菜单与权限分配功能。

  • 一些软件产品,系统的操作角色是动态创建的,需要给角色灵活分配能使用哪些菜单项,这时候只能采用动态菜单的方案,即菜单的生成是根据后端的api生成的(通常根据用户的分配角色取得这些菜单项,并且菜单项去重后的集合)。

前端菜单与权限方案

  1. 只需要利用ant design pro V5现有的routes.ts文件和src/utils/access.ts配合即可完成菜单项及权限的控制。

  2. 这时候可以按前面篇章说的可以配上给二级菜单支持图标的显示。

后端菜单与权限方案

  1. 利用ProLayout的menuDataRender方法,可以动态取得后端api的菜单项信息,通常根据登录用户的拥有的角色,把这些角色的所有菜单项权限(不允许重复,即去重后)的集合取回来,把这个数组配置给menuDataRender。

详见以下代码,重点看app.tsx新增的menuDataRender方法:

重点说明一:此方案二级菜单的图标不能显示,所以要注释掉原来配置二级菜单显示图标的功能。

重点说明二:routes.ts中同时也要配置菜单项的内容,即菜单项还需要路由的配置,否则无法使用。

重点说明三:避免把图标库全部引入的方案,会增大工程3M的大小文件,采用只引入用到的图标,以下方案做了推荐。

重点说明四:注意loopMenuItem函数的功能,它把图标字符串转换为图标节点对象,否则一级菜单无法显示图标。

// 南极客 增加动态菜单支持 2021.7.8 import type { Settings as LayoutSettings, BasicLayoutProps, MenuDataItem } from '@ant-design/pro-layout'; import { PageLoading } from '@ant-design/pro-layout'; import { notification } from 'antd'; import type { RequestConfig, RunTimeLayoutConfig } from 'umi'; import { history, Link } from 'umi'; import RightContent from '@/components/RightContent'; import Footer from '@/components/Footer'; import type { ResponseError } from 'umi-request'; import { currentUser as queryCurrentUser } from './services/ant-design-pro/api'; // import { BookOutlined, LinkOutlined } from '@ant-design/icons'; /* 以下内容 南极客 add 2021.7.8 */ import { BookOutlined, LinkOutlined, SmileOutlined, HeartOutlined } from '@ant-design/icons'; import * as Icon from '@ant-design/icons'; import React from 'react'; const isDev = process.env.NODE_ENV === 'development'; const loginPath = '/user/login'; // 南极客 add 2021.7.8 动态菜单 const iconEnum  = {   SmileOutlined: <SmileOutlined />,   HeartOutlined: <HeartOutlined />, }; /** 获取用户信息比较慢的时候会展示一个 loading */ export const initialStateConfig = {   loading: <PageLoading />, }; /**  * @see  https://umijs.org/zh-CN/plugins/plugin-initial-state  * */ export async function getInitialState(): Promise<{   settings?: Partial<LayoutSettings>;   currentUser?: API.CurrentUser;   fetchUserInfo?: () => Promise<API.CurrentUser | undefined>;   // 南极客 add 2021.7.8 动态菜单   menu: MenuDataItem[]; }> {   const fetchUserInfo = async () => {     try {       const currentUser = await queryCurrentUser();       return currentUser;     } catch (error) {       history.push(loginPath);     }     return undefined;   };   // 如果是登录页面,不执行   if (history.location.pathname !== loginPath) {     const currentUser = await fetchUserInfo();     // 南极客 2021.7.8 add     // const menuData = await getMenuList(currentUser?.userid || '');     // 模拟后台取得的api 数据     const menuData = [       {         path: '/admin',         name: 'admin',         icon: 'HeartOutlined',         component: './Admin',         routes: [           {             path: '/admin/sub-page',             name: 'sub-page',             icon: 'HeartOutlined',             component: './Welcome',           },         ],       },       {         name: 'ebyte.sys.userlist',         icon: 'SmileOutlined',         path: '/userlist',         component: './UserList',       },       {         name: 'list.table-list',         icon: 'HeartOutlined',         path: '/list',         component: './TableList',       },     ];     return {       fetchUserInfo,       currentUser,       settings: {},       // 南极客 增加动态菜单2021.7.8       menu: menuData,     };   }   return {     fetchUserInfo,     settings: {},     // 南极客 2021.7.8 add     menu: [],   }; } // 南极客 2021.7.8 add 递归生成菜单 const loopMenuItem = (menus: MenuDataItem[]): MenuDataItem[] => (   menus.map(({ icon, children, ...item }) => {     return {       ...item,       //  icon: icon && IconMap[icon as string],       // 方案一: 此用法会把所有的图标库引入,造成增加工程3M大小,请谨用       // icon: icon && React.createElement(Icon[icon]),       // 方案二: 建议用此方案,只引入用到的图标,避免图标库全部引入       icon: icon && iconEnum [icon],       children: children && loopMenuItem(children),     }   }) ); // https://umijs.org/zh-CN/plugins/plugin-layout // 南极客 fixed 2021.7.8 // export const layout: RunTimeLayoutConfig = ({ initialState }) =>  { export const layout = ({ initialState, }: {   initialState: { settings?: LayoutSettings; currentUser?: API.CurrentUser; menu: MenuDataItem[]; }; }): BasicLayoutProps => {   return {     // 南极客 2021.7.8 add 动态菜单支持     menuDataRender: () => loopMenuItem(initialState.menu),     // 南极客 修补:二级图标正常显示2021.7.8,动态菜单下不管用,只会显示图标的名称     /*      menuItemRender: (menuItemProps, defaultDom) => {       if (         menuItemProps.isUrl || !menuItemProps.path) {         return defaultDom;       }       // 支持二级菜单显示icon       return (        <Link to={menuItemProps.path}>           {menuItemProps.pro_layout_parentKeys            && menuItemProps.pro_layout_parentKeys.length > 0 &&           menuItemProps.icon}{defaultDom}         </Link>     );   },   */     rightContentRender: () => <RightContent />,     disableContentMargin: false,     /* 去掉水印功能 南极客 2021.4.22     waterMarkProps: {       content: initialState?.currentUser?.name,     },     */     footerRender: () => <Footer />,     onPageChange: () => {       const { location } = history;       // 如果没有登录,重定向到 login       if (!initialState?.currentUser && location.pathname !== loginPath) {         history.push(loginPath);       }     },     links: isDev       ? [         /* 去掉菜单底部链接  南极客 2021.4.22           <Link to="/umi/plugin/openapi" target="_blank">             <LinkOutlined />             <span>openAPI 文档</span>           </Link>,           <Link to="/~docs">             <BookOutlined />             <span>业务组件文档</span>           </Link>,           */       ]       : [],     menuHeaderRender: undefined,     // 自定义 403 页面     // unAccessible: <div>unAccessible</div>,     ...initialState?.settings,   }; }; const codeMessage = {   200: '服务器成功返回请求的数据。',   201: '新建或修改数据成功。',   202: '一个请求已经进入后台排队(异步任务)。',   204: '删除数据成功。',   400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。',   401: '用户没有权限(令牌、用户名、密码错误)。',   403: '用户得到授权,但是访问是被禁止的。',   404: '发出的请求针对的是不存在的记录,服务器没有进行操作。',   405: '请求方法不被允许。',   406: '请求的格式不可得。',   410: '请求的资源被永久删除,且不会再得到的。',   422: '当创建一个对象时,发生一个验证错误。',   500: '服务器发生错误,请检查服务器。',   502: '网关错误。',   503: '服务不可用,服务器暂时过载或维护。',   504: '网关超时。', }; /** 异常处理程序  * @see https://beta-pro.ant.design/docs/request-cn  */ const errorHandler = (error: ResponseError) => {   const { response } = error;   if (response && response.status) {     const errorText = codeMessage[response.status] || response.statusText;     const { status, url } = response;     notification.error({       message: `请求错误 ${status}: ${url}`,       description: errorText,     });   }   if (!response) {     notification.error({       description: '您的网络发生异常,无法连接服务器',       message: '网络异常',     });   }   throw error; }; // https://umijs.org/zh-CN/plugins/plugin-request export const request: RequestConfig = {   errorHandler, }; 复制代码

  1. 自行设计菜单项的配置模块功能,以及给角色分配菜单项权限的模块功能,同时编写后端api取得菜单项的方法,返回的json格式,参考routes.ts的格式。


作者:南极客
链接:https://juejin.cn/post/6982616430277558309

文章分类
代码人生
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐