react + umi + antd pro动态获取菜单
前言:
当用户输入账号密码后登录通过不同的账号获取不同的菜单
正文:
1. 添加获取菜单请求
export async function fetchMenuData(){
return request<Models.FRestResult>('/api/xxx/xxx/xxx');
}
2. 修改app.tsx
1) 修改getInitialState方法
/**
* @see https://umijs.org/zh-CN/plugins/plugin-initial-state
* */
export async function getInitialState(): Promise<{
settings?: Partial<LayoutSettings>;
currentUser?: Models.CurrentUser;
loading?: boolean;
fetchUserInfo?: () => Promise<Models.CurrentUser | undefined>;
menuRouteInfo?: () => Promise<Models.FRestResult | undefined>;
menuList?: Models.FRestResult;
}> {
// 获取个人信息
const fetchUserInfo = async () => {
try {
const msg = await queryCurrentUser();
return msg.dataEntry;
} catch (error) {
history.push(loginPath);
}
return undefined;
};
// 获取菜单
const menuRouteInfo = async () => {
try {
const msg = await fetchMenuData();
return msg;
} catch (error) {
history.push(loginPath);
}
return undefined;
};
// 如果不是登录页面,执行
if (history.location.pathname !== loginPath) {
const currentUser = await fetchUserInfo();
const menuList = await menuRouteInfo();
return {
fetchUserInfo,
currentUser,
settings: defaultSettings,
menuRouteInfo,
menuList,
};
}
return {
fetchUserInfo,
menuRouteInfo,
settings: defaultSettings,
};
}
2) 修改layout方法
// ProLayout 支持的api https://procomponents.ant.design/components/layout
export const layout: RunTimeLayoutConfig = ({ initialState, setInitialState }: any) => {
return {
// 遍历菜单
menuDataRender: () => loopMenuItem(initialState?.menuList?.data),
rightContentRender: () => <RightContent />,
disableContentMargin: false,
footerRender: () => <Footer />,
onPageChange: () => {
const { location } = history;
// 如果没有登录,重定向到 login
if (!initialState?.currentUser && location.pathname !== loginPath) {
history.push(loginPath);
}
},
// 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: any, props: any) => {
return (
<>
{children}
</>
);
},
...initialState?.settings,
};
};
3) 添加loopMenuItem方法
// 默认路由
const loginPath = '/user/login';
// 菜单的展示,我这里的icon是按需引入
const IconMap = {
smile: <SmileOutlined />,
heart: <HeartOutlined />,
};
const loopMenuItem = (menus: any[]): MenuDataItem[] => {
if (menus) {
return menus.map(({ icon, routes, ...item }) => ({
...item,
icon: icon && IconMap[icon as string],
// children: routes && loopMenuItem(routes),
routes: loopMenuItem(routes),
}));
}
return [];
}
3. 登录模块
user/login
const [userLoginState, setUserLoginState] = useState<API.LoginResult>({});
const [type, setType] = useState<string>('account');
const { initialState, setInitialState } = useModel('@@initialState');
const intl = useIntl();
// 调用getInitialState内的获取个人信息
const fetchUserInfo = async () => {
const userInfo = await initialState?.fetchUserInfo?.();
if (userInfo) {
await setInitialState((s: any) => ({
...s,
currentUser: userInfo,
}));
}
};
// 调用getInitialState内的菜单方法
const menuRouteInfo = async () => {
const menuInfo = await initialState?.menuRouteInfo?.();
if (menuInfo) {
await setInitialState((s: any) => ({
...s,
menuList: menuInfo,
}));
}
};
const handleSubmit = async (values: API.LoginParams) => {
try {
// 登录
const msg = await userLogin({ ...values, type });
if (msg.status === 'ok') {
const defaultLoginSuccessMessage = intl.formatMessage({
id: 'pages.login.success',
defaultMessage: '登录成功!',
});
if(msg.token!=''||msg.token!=null||msg.token!=undefined){
await localStorage.setItem('token',msg.token);
}else{
message.error("未获取到Token!");
return;
}
message.success(defaultLoginSuccessMessage);
await fetchUserInfo();
await menuRouteInfo();
/** 此方法会跳转到 redirect 参数所在的位置 */
if (!history) return;
const { query } = history.location;
const { redirect } = query as { redirect: string };
history.push(redirect || '/');
return;
}
console.log(msg);
// 如果失败去设置用户错误信息
setUserLoginState(msg);
message.error(msg.message);
} catch (error) {
const defaultLoginFailureMessage = intl.formatMessage({
id: 'pages.login.failure',
defaultMessage: '登录失败,请重试!',
});
message.error(defaultLoginFailureMessage);
}
};
4. 路由文件routes.tsx
路由文件内容最好要和获取菜单内容的对应。
但还是有bug:当menu没有而routes内有时,在浏览器中修改url时也会跳转,两种方式
- 一种是使用antd pro的权限路路由权限管理 - Ant Design Pro
- 二时通过登录进行动态路由,下章讲解