1. 添加获取菜单请求(services/api.ts)
export async function currentUserMenus() {
return request<MenuDataItem[]>('/api/org/getUserMenus', {
method: 'GET',
});
}
2. 修改app.tsx文件
2.1 修改getInitialState方法
export async function getInitialState(): Promise<{
settings?: Partial<LayoutSettings>;
currentUser?: API.CurrentUser;
menuData?: MenuDataItem[] | undefined;
fetchUserInfo?: () => Promise<API.CurrentUser | undefined>;
fetchUserMenus?: () => Promise<MenuDataItem[] | undefined>;
}> {
const fetchUserInfo = async () => {
try {
const currentUser = await queryCurrentUser();
return currentUser;
} catch (error) {
history.push(loginPath);
}
return undefined;
};
const fetchUserMenus = async () => {
try {
const menuData = await queryCurrentUserMenus();
return menuData;
} catch (error) {
history.push(loginPath);
}
return undefined;
};
// 如果是登录页面,不执行
if (history.location.pathname !== loginPath) {
const token = localStorage.getItem('userToken');
if (!token) {
history.push(loginPath);
return {
fetchUserInfo,
fetchUserMenus,
menuData: [],
settings: {},
};
}
const currentUser = await fetchUserInfo();
const menuData = await fetchUserMenus();
return {
fetchUserInfo,
fetchUserMenus,
currentUser,
menuData,
settings: {},
};
}
return {
fetchUserInfo,
fetchUserMenus,
menuData: [],
settings: {},
};
}
2.2 修改layout方法
export const layout: RunTimeLayoutConfig = ({ initialState }) => {
return {
rightContentRender: () => <RightContent />,
disableContentMargin: false,
waterMarkProps: {
content: initialState?.currentUser?.name,
},
footerRender: () => <Footer />,
onPageChange: () => {
console.log('onPageChange');
const { location } = history;
// 如果没有登录,重定向到 login
const token = localStorage.getItem('userToken');
if (!token && location.pathname !== loginPath) {
history.push(loginPath);
}
},
links: isDev
? [
<Link to="/umi/plugin/openapi" target="_blank">
<LinkOutlined />
<span>openAPI 文档</span>
</Link>,
<Link to="/~docs">
<BookOutlined />
<span>业务组件文档</span>
</Link>,
]
: [],
menuHeaderRender: undefined,
// menuDataRender: (menuData) => initialState?.menuData || menuData,
// 显示菜单(带icon)
menuDataRender: () => {
return fixMenuItemIcon(initialState?.menuData);
},
// menuDataRender: () => menuDataRender(),
// 自定义 403 页面
// unAccessible: <div>unAccessible</div>,
...initialState?.settings,
};
};
3. 解决菜单icon显示问题
创建fixMenuItemIcon.ts组件
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, children } = item;
if (typeof icon === 'string') {
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
children && children.length > 0 ? (item.children = fixMenuItemIcon(children)) : null;
});
return menus;
};
export default fixMenuItemIcon;
4. 登录模块
4.1 登录,登录成功后获取用户信息并保存token
const handleSubmit = async (values: API.LoginParams) => {
setSubmitting(true);
try {
// 登录
const msg = await login({ ...values });
if (msg.data.state === 'ok') {
message.success('登录成功!');
localStorage.setItem('userToken', msg.data.token);
await fetchUserInfo();
goto();
return;
}
// 如果失败去设置用户错误信息
setUserLoginState(msg);
} catch (error) {
message.error('登录失败,请重试!');
}
setSubmitting(false);
};
4.2 获取用户信息和用户菜单
const fetchUserInfo = async () => {
const userInfo = await initialState?.fetchUserInfo?.();
// 有用户信息 设置初始参数
if (userInfo) {
const menus = await initialState?.fetchUserMenus?.();
setInitialState({
...initialState,
currentUser: userInfo,
menuData: menus,
});
}
};
5. 注意事项
- 菜单返回json格式,字段只需要有path和name就可以了,path地址需要在路由里面有对应的地址:
[
{
"icon": "smile",
"path": "/system",
"name": "system",
"children": [
{
"icon": null,
"path": "/system/empty",
"name": "empty"
},
{
"icon": "smile",
"path": "/system/rolelist",
"name": "rolemanage"
}
]
}
]
- 路由文件routes.tsx
export default [
{
path: '/user',
layout: false,
routes: [
{
path: '/user',
routes: [
{
name: 'login',
path: '/user/login',
component: './user/Login',
},
],
},
],
},
{
path: '/welcome',
name: 'welcome',
icon: 'smile',
component: './welcome',
},
{
path: '/system',
routes: [
{
icon: 'smile',
path: '/system/empty',
component: './sysManage/Empty',
},
{
icon: 'smile',
path: '/system/rolelist',
component: './sysManage/RoleManage',
},
],
},
{
path: '/',
redirect: '/welcome',
},
{
component: './404',
},
];
如果config/routes.ts 中配置了的菜单,但Menu中没有,是可以通过输入理由名称访问页面的,可以在app.ts中onPageChange方法加上一句:
onPageChange: () => {
const { location } = umi.history
// 如果没有登录,重定向到 login
if (!initialState?.currentUser?.name && location.pathname !== loginPath) {
umi.history.push(loginPath)
}
// 如果訪問頁面不在menuData中。重定向至404.防止在地址欄輸入路由名稱進入頁面
if (initialState?.menuData?.length && location.pathname !== loginPath) {
const isInMenu = initialState.menuData.find(item => item.path === location.pathname)
if (!isInMenu) umi.history.push('/404')
}
}
// 由于菜单配置一般采用中文 关闭菜单国际化
menu: {
locale: false // 关闭左侧菜单国际化
},