当前位置: 首页> 娱乐> 明星 > ant design pro 如何实现动态菜单带上 icon 的

ant design pro 如何实现动态菜单带上 icon 的

时间:2025/7/12 22:05:13来源:https://blog.csdn.net/weixin_39637597/article/details/141359719 浏览次数:0次

在这里插入图片描述

  • ant design pro 如何去保存颜色
  • ant design pro v6 如何做好角色管理
  • ant design 的 tree 如何作为角色中的权限选择之一
  • ant design 的 tree 如何作为角色中的权限选择之二
  • ant design pro access.ts 是如何控制多角色的权限的
  • ant design pro 中用户的表单如何控制多个角色

如上图所示,这里的菜单是从后端动态得到的

在这里插入图片描述
我们看下后端菜单的响应数据:

{"success": true,"data": [{"_id": "66b6cd18b9ad87dfa985f190","name": "认证管理","path": "/auth","permission": {"_id": "66b9ad528554e602536acc84","name": "授权管理菜单","path": "/auth","action": "GET","permissionGroup": "66b9ad348554e602536acc67","createdAt": "2024-08-12T06:36:02.754Z","updatedAt": "2024-08-12T06:36:02.754Z","__v": 0},"createdAt": "2024-08-10T02:14:48.819Z","updatedAt": "2024-08-12T10:30:50.749Z","__v": 0,"icon": "SecurityScanOutlined","children": [{"_id": "66b6cdbbb9ad87dfa985f1f9","name": "用户管理","path": "/auth/users","parent": {"_id": "66b6cd18b9ad87dfa985f190","name": "认证管理","path": "/auth","permission": "66b9ad528554e602536acc84","createdAt": "2024-08-10T02:14:48.819Z","updatedAt": "2024-08-12T10:30:50.749Z","__v": 0,"icon": "SecurityScanOutlined"},"permission": {"_id": "66b6d352b9ad87dfa985f3f0","name": "查看用户","path": "/users","action": "GET","permissionGroup": "66b6d2c9b9ad87dfa985f34f","createdAt": "2024-08-10T02:41:22.895Z","updatedAt": "2024-08-10T08:03:22.477Z","__v": 0},"createdAt": "2024-08-10T02:17:31.227Z","updatedAt": "2024-08-12T10:23:32.641Z","__v": 0,"icon": "HeartOutlined","children": []},{"_id": "66b6cdcfb9ad87dfa985f210","name": "角色管理","path": "/auth/roles","parent": {"_id": "66b6cd18b9ad87dfa985f190","name": "认证管理","path": "/auth","permission": "66b9ad528554e602536acc84","createdAt": "2024-08-10T02:14:48.819Z","updatedAt": "2024-08-12T10:30:50.749Z","__v": 0,"icon": "SecurityScanOutlined"},"permission": {"_id": "66b6d40db9ad87dfa985f475","name": "查看角色","path": "/roles","action": "GET","permissionGroup": "66b6d2e9b9ad87dfa985f377","createdAt": "2024-08-10T02:44:29.797Z","updatedAt": "2024-08-10T08:03:18.669Z","__v": 0},"createdAt": "2024-08-10T02:17:51.754Z","updatedAt": "2024-08-12T10:11:47.776Z","__v": 0,"icon": "MenuFoldOutlined","children": []},{"_id": "66b6cde2b9ad87dfa985f229","name": "菜单管理","path": "/auth/menus","parent": {"_id": "66b6cd18b9ad87dfa985f190","name": "认证管理","path": "/auth","permission": "66b9ad528554e602536acc84","createdAt": "2024-08-10T02:14:48.819Z","updatedAt": "2024-08-12T10:30:50.749Z","__v": 0,"icon": "SecurityScanOutlined"},"permission": {"_id": "66b6d48bb9ad87dfa985f4e7","name": "查看菜单","path": "/menus","action": "GET","permissionGroup": "66b6d2ddb9ad87dfa985f362","createdAt": "2024-08-10T02:46:35.896Z","updatedAt": "2024-08-10T08:03:13.698Z","__v": 0},"createdAt": "2024-08-10T02:18:10.776Z","updatedAt": "2024-08-12T10:30:56.693Z","__v": 0,"icon": "MenuFoldOutlined","children": []},{"_id": "66b6cdfcb9ad87dfa985f244","name": "权限管理","path": "/auth/permissions","parent": {"_id": "66b6cd18b9ad87dfa985f190","name": "认证管理","path": "/auth","permission": "66b9ad528554e602536acc84","createdAt": "2024-08-10T02:14:48.819Z","updatedAt": "2024-08-12T10:30:50.749Z","__v": 0,"icon": "SecurityScanOutlined"},"permission": {"_id": "66b1c55141364c27c464f858","name": "查看权限","path": "/permissions","action": "GET","permissionGroup": "66b1b00bb5d937a0aef34034","createdAt": "2024-08-06T06:40:17.991Z","updatedAt": "2024-08-10T08:03:27.245Z","__v": 0},"createdAt": "2024-08-10T02:18:36.390Z","updatedAt": "2024-08-10T02:18:36.390Z","__v": 0,"children": []},{"_id": "66b6ce29b9ad87dfa985f261","name": "权限组管理","path": "/auth/permission-groups","parent": {"_id": "66b6cd18b9ad87dfa985f190","name": "认证管理","path": "/auth","permission": "66b9ad528554e602536acc84","createdAt": "2024-08-10T02:14:48.819Z","updatedAt": "2024-08-12T10:30:50.749Z","__v": 0,"icon": "SecurityScanOutlined"},"permission": {"_id": "66b6d52cb9ad87dfa985f546","name": "查看权限组","path": "/permission-groups","action": "GET","permissionGroup": "66b6d314b9ad87dfa985f3a7","createdAt": "2024-08-10T02:49:16.624Z","updatedAt": "2024-08-10T08:03:09.517Z","__v": 0},"createdAt": "2024-08-10T02:19:21.424Z","updatedAt": "2024-08-12T06:37:05.295Z","__v": 0,"children": []},{"_id": "66b6ce41b9ad87dfa985f280","name": "数据权限管理","path": "/auth/data-permissions","parent": {"_id": "66b6cd18b9ad87dfa985f190","name": "认证管理","path": "/auth","permission": "66b9ad528554e602536acc84","createdAt": "2024-08-10T02:14:48.819Z","updatedAt": "2024-08-12T10:30:50.749Z","__v": 0,"icon": "SecurityScanOutlined"},"permission": {"_id": "66b6d586b9ad87dfa985f592","name": "查看数据权限","path": "/data-permissions","action": "GET","permissionGroup": "66b6d2fdb9ad87dfa985f38e","createdAt": "2024-08-10T02:50:46.780Z","updatedAt": "2024-08-10T08:03:04.925Z","__v": 0},"createdAt": "2024-08-10T02:19:45.054Z","updatedAt": "2024-08-12T08:23:19.027Z","__v": 0,"children": []}]},{"_id": "66b6ce76b9ad87dfa985f2a1","name": "材料类目","path": "/material-categories","permission": {"_id": "66b6d7d0b9ad87dfa985f782","name": "查看材料类目","path": "/material-categories","action": "GET","permissionGroup": "66adec30d647a4fde5546b1c","createdAt": "2024-08-10T03:00:32.932Z","updatedAt": "2024-08-10T08:02:59.634Z","__v": 0},"createdAt": "2024-08-10T02:20:38.550Z","updatedAt": "2024-08-12T09:58:32.426Z","__v": 0,"icon": "UsergroupAddOutlined","children": []}]
}

看到这里的数据结构了吗:

主要是 name children 这些比较重要,name 是显示出来的,children 是层级结构

当然还有 icon 之类的 还有 path ,也是要的,毕竟菜单要有路径的。

那后端如何实现呢:

// @desc Get permission menus
// @route GET /api/menus/fetch
// @access Private
const fetchMenus = handleAsync(async (req: RequestCustom, res: Response) => {const query = buildQuery(req.query);const menus = await Menu.find(query).populate('parent').populate('permission');const menusWithChildren = await Promise.all(menus.map(async (menu) => {const menuWithChildren = menu.toObject();menuWithChildren.children = await getChildren(menu._id);return menuWithChildren;}),);res.json({success: true,data: checkMenu(menusWithChildren, req.user),});
});

只要你的后端能返回出这样的数据就行。

那前端呢:

app.tsx

menu: {// 每当 initialState?.currentUser?.userid 发生修改时重新执行 requestparams: {userId: initialState?.currentUser?._id,},request: async () => {// initialState.currentUser 中包含了所有用户信息const { data, success } = await fetchMenuData();console.log('data', data);if (success) {console.log('loopMenuItem(data)', loopMenuItem(data));return loopMenuItem(data);} else {return [];}},},

这里还有 icon 的使用:

const iconEnum: { [key: string]: ReactElement<any, any> } = {UsergroupAddOutlined: <UsergroupAddOutlined />,SmileOutlined: <SmileOutlined />,HeartOutlined: <HeartOutlined />,GlobalOutlined: <GlobalOutlined />,MenuFoldOutlined: <MenuFoldOutlined />,TeamOutlined: <TeamOutlined />,DatabaseOutlined: <DatabaseOutlined />,GatewayOutlined: <GatewayOutlined />,SecurityScanOutlined: <SecurityScanOutlined />,
};console.log('iconEnum', iconEnum);const loopMenuItem = (menus: MenuDataItem[]): MenuDataItem[] =>menus.map(({ icon, children, ...item }) => {return {...item,icon: icon && iconEnum[icon as string],children: children && loopMenuItem(children),};});

完整代码是这样的:

import { Footer, SelectLang, AvatarDropdown, AvatarName } from '@/components';
import {DatabaseOutlined,GatewayOutlined,GlobalOutlined,HeartOutlined,LinkOutlined,MenuFoldOutlined,SecurityScanOutlined,SmileOutlined,TeamOutlined,UsergroupAddOutlined,
} from '@ant-design/icons';
import type { Settings as LayoutSettings, MenuDataItem } from '@ant-design/pro-components';
import { SettingDrawer } from '@ant-design/pro-components';
import type { RunTimeLayoutConfig } from '@umijs/max';
import { history, Link } from '@umijs/max';
import defaultSettings from '../config/defaultSettings';
import { errorConfig } from './requestErrorConfig';
import { fetchMenuData, currentUser as queryCurrentUser } from '@/services/ant-design-pro/api';
import React, { ReactElement } from 'react';
const isDev = process.env.NODE_ENV === 'development';
const loginPath = '/user/login';const iconEnum: { [key: string]: ReactElement<any, any> } = {UsergroupAddOutlined: <UsergroupAddOutlined />,SmileOutlined: <SmileOutlined />,HeartOutlined: <HeartOutlined />,GlobalOutlined: <GlobalOutlined />,MenuFoldOutlined: <MenuFoldOutlined />,TeamOutlined: <TeamOutlined />,DatabaseOutlined: <DatabaseOutlined />,GatewayOutlined: <GatewayOutlined />,SecurityScanOutlined: <SecurityScanOutlined />,
};console.log('iconEnum', iconEnum);const loopMenuItem = (menus: MenuDataItem[]): MenuDataItem[] =>menus.map(({ icon, children, ...item }) => {return {...item,icon: icon && iconEnum[icon as string],children: children && loopMenuItem(children),};});/*** @see  https://umijs.org/zh-CN/plugins/plugin-initial-state* */
export async function getInitialState(): Promise<{settings?: Partial<LayoutSettings>;currentUser?: API.CurrentUser;loading?: boolean;fetchUserInfo?: () => Promise<API.CurrentUser | undefined>;
}> {const fetchUserInfo = async () => {try {const response = await queryCurrentUser({skipErrorHandler: true,});return response.data;} catch (error) {history.push(loginPath);}return undefined;};// 如果不是登录页面,执行const { location } = history;if (location.pathname !== loginPath) {const currentUser = await fetchUserInfo();return {fetchUserInfo,currentUser,settings: defaultSettings as Partial<LayoutSettings>,};}return {fetchUserInfo,settings: defaultSettings as Partial<LayoutSettings>,};
}// ProLayout 支持的api https://procomponents.ant.design/components/layout
export const layout: RunTimeLayoutConfig = ({ initialState, setInitialState }) => {return {actionsRender: () => [<SelectLang key="SelectLang" />],avatarProps: {src: initialState?.currentUser?.avatar,title: <AvatarName />,render: (_, avatarChildren) => {return <AvatarDropdown>{avatarChildren}</AvatarDropdown>;},},menu: {// 每当 initialState?.currentUser?.userid 发生修改时重新执行 requestparams: {userId: initialState?.currentUser?._id,},request: async () => {// initialState.currentUser 中包含了所有用户信息const { data, success } = await fetchMenuData();console.log('data', data);if (success) {console.log('loopMenuItem(data)', loopMenuItem(data));return loopMenuItem(data);} else {return [];}},},waterMarkProps: {content: '',},footerRender: () => <Footer />,onPageChange: () => {const { location } = history;// 如果没有登录,重定向到 loginif (!initialState?.currentUser && location.pathname !== loginPath) {history.push(loginPath);}},links: isDev? [<Link key="openapi" to="/umi/plugin/openapi" target="_blank"><LinkOutlined /><span>OpenAPI 文档</span></Link>,]: [],menuHeaderRender: undefined,// 自定义 403 页面// unAccessible: <div>unAccessible</div>,// 增加一个 loading 的状态childrenRender: (children) => {// if (initialState?.loading) return <PageLoading />;return (<>{children}{isDev && (<SettingDrawerdisableUrlParamsenableDarkThemesettings={initialState?.settings}onSettingChange={(settings) => {setInitialState((preInitialState) => ({...preInitialState,settings,}));}}/>)}</>);},...initialState?.settings,};
};/*** @name request 配置,可以配置错误处理* 它基于 axios 和 ahooks 的 useRequest 提供了一套统一的网络请求和错误处理方案。* @doc https://umijs.org/docs/max/request#配置*/
export const request = {baseURL: `${process.env.UMI_APP_API_URL}/api`,...errorConfig,
};
export async function fetchMenuData() {return request<menuResponse>('/menus/fetch', {method: 'GET',});
}

这样就可以弄好动态菜单的:

我们拥有 12 年建站编程经验

  1. 虚拟产品交易平台定制开发
  2. WordPress 外贸电商独立站建站

我的网站

关键字:ant design pro 如何实现动态菜单带上 icon 的

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: