-
用户需求场景
- 一些软件产品,设计时就规划好了系统的操作角色,这些操作角色相对固定,例如:客户,管理员,这种场景完全只需要前端的路由与菜单配置方案,无需要动态菜单与权限分配功能。
- 一些软件产品,系统的操作角色是动态创建的,需要给角色灵活分配能使用哪些菜单项,这时候只能采用动态菜单的方案,即菜单的生成是根据后端的api生成的(通常根据用户的分配角色取得这些菜单项,并且菜单项去重后的集合)。
前端菜单与权限方案
- 只需要利用ant design pro V5现有的routes.ts文件和src/utils/access.ts配合即可完成菜单项及权限的控制。
- 这时候可以按前面篇章说的可以配上给二级菜单支持图标的显示。
后端菜单与权限方案
- 利用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,
};
复制代码
- 自行设计菜单项的配置模块功能,以及给角色分配菜单项权限的模块功能,同时编写后端api取得菜单项的方法,返回的json格式,参考routes.ts的格式。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END