🐛[BUG] 从服务端请求菜单时 icon 和 access 不生效
Closed this issue · 41 comments
🐛 bug 描述
https://beta-pro.ant.design/docs/advanced-menu-cn
根据该文档的方式实现从服务端请求菜单,返回的菜单中包含 icon 信息和 accss 信息,icon 显示错误,access 不生效。
access无效。
📷 复现步骤
返回的菜单信息:
path: '/system',
name: 'system',
icon: 'setting',
access: 'noPermission',
🏞 期望结果
显示设置的icon,access 生效
💻 复现代码
© 版本信息
- Ant Design Pro 版本: 5.0.0-beta.2
- umi 版本
- 浏览器环境
- 开发环境 [e.g. mac OS]
🚑 其他信息
icon 只能用 dom,access 必须要触发页面的重新渲染才有用
服务器请求回来应该自己处理,是不会走到插件的逻辑的。
权限的请求reload 可以实现,但是菜单不行
能不能给个详细的icon使用dom渲染的方法,我尝试了下,没成功
icon 只能用 dom,access 必须要触发页面的重新渲染才有用
能否给点儿详细的代码提示,我将menu数据中的icon字段直接替换成 dom,不生效
在utils中封装了一个方法;
`import React from 'react';
import { MenuDataItem } from '@ant-design/pro-layout';
import * as allIcons from '@ant-design/icons';
// FIX从接口获取菜单时icon为string类型
const fixMenuItemIcon = (menus: MenuDataItem[], iconType = 'Outlined'): MenuDataItem[] => {
menus.forEach((item) => {
const { icon, children } = item;
if (typeof icon === 'string') {
let fixIconName = icon.slice(0, 1).toLocaleUpperCase() + icon.slice(1) + iconType;
item.icon = React.createElement(allIcons[fixIconName] || allIcons[icon]);
}
children && children.length > 0 ? (item.children = fixMenuItemIcon(children)) : null;
});
return menus;
};
export default fixMenuItemIcon;`
在layout组件中直接调用
`menuDataRender={() => fixMenuItemIcon(menus.menusData)}`
就能解决了
你的这个代码会导致 js 大小增加 2m 左右,要慎重
请问怎么更优雅的解决呢?
菜单access怎么解决呢
有啥好的解决办法么,解决服务器请求菜单设置icon图标
@chenshuai2144 同问该怎么优雅的解决icon
用了个死办法,枚举
import {
BookOutlined,
LinkOutlined,
AppstoreOutlined,
SmileOutlined,
TableOutlined,
SoundOutlined,
CrownOutlined,
} from '@ant-design/icons';
// 利用对象进行图标映射
const iconMapping = {
AppstoreOutlined: <AppstoreOutlined />,
SmileOutlined: <SmileOutlined />,
TableOutlined: <TableOutlined />,
SoundOutlined: <SoundOutlined />,
CrownOutlined: <CrownOutlined />
}
menu: {
params: initialState,
// 这里从后端请求动态菜单
request: async (params, defaultMenuData) => {
// 这里返回后端的菜单配置 forEach? 把icon字符串 转化为 render图标
params?.currentUser?.reqMemu.forEach((item: any) => item.icon = iconMapping[item.icon])
return params?.currentUser?.reqMemu;
},
},
用了个死办法,枚举
import { BookOutlined, LinkOutlined, AppstoreOutlined, SmileOutlined, TableOutlined, SoundOutlined, CrownOutlined, } from '@ant-design/icons'; // 利用对象进行图标映射 const iconMapping = { AppstoreOutlined: <AppstoreOutlined />, SmileOutlined: <SmileOutlined />, TableOutlined: <TableOutlined />, SoundOutlined: <SoundOutlined />, CrownOutlined: <CrownOutlined /> } menu: { params: initialState, // 这里从后端请求动态菜单 request: async (params, defaultMenuData) => { // 这里返回后端的菜单配置 forEach? 把icon字符串 转化为 render图标 params?.currentUser?.reqMemu.forEach((item: any) => item.icon = iconMapping[item.icon]) return params?.currentUser?.reqMemu; }, },
得枚举很大一部分... @chengzi-code
所以这么问题优雅的解决了吗
icon 不显示的问题,可以自定义菜单项渲染方法,并引用自定义的图标库。
看到这个问题被关闭,并标记为Feature Request,不知道以后是否有官方解决方案。
我的方案如下:
// MenuItemRender.tsx,`app.tsx`直接引用即可
import { MenuDataItem } from '@ant-design/pro-layout';
import { createFromIconfontCN } from '@ant-design/icons';
import { Link, useIntl } from 'umi';
let IconFont = createFromIconfontCN({
// 以下是默认值,也可以按需要指定
// scriptUrl: defaultSettings.iconfontUrl,
});
const getIcon = (
icon?: string | React.ReactNode,
iconPrefixes: string = 'icon-',
): React.ReactNode => {
if (typeof icon === 'string' && icon !== '') {
// 可加入多种图标类型的兼容写法,此处省略
if (icon.startsWith(iconPrefixes)) {
return <IconFont type={icon} className={icon} />;
}
}
return icon;
};
const MenuItem: React.FC<MenuDataItem> = (menuItemProps) => {
const { formatMessage } = useIntl();
const { isUrl: isLink, path, icon, locale } = menuItemProps;
const localeStr = locale as string; // 和 `formatMessage中` 的 `id` 类型不一致,只好断言一下
const itemContent = (
<span className="ant-pro-menu-item">
{getIcon(icon)}
<span className="ant-pro-menu-item-title">{formatMessage({ id: localeStr })}</span>
</span>
);
return isLink || !path || location.pathname === path ? (
itemContent
) : (
<Link to={path}>{itemContent}</Link>
);
};
export default MenuItem;
还是想用 Ant Design 的图标库?拿去吧:https://www.iconfont.cn/collections/detail?cid=9402
// app.tsx
export const layout: RunTimeLayoutConfig = ({ initialState }) => {
return {
rightContentRender: () => <RightContent />,
disableContentMargin: false,
onPageChange: () => {
const { location } = history;
// 如果没有登录,重定向到 login
if (!initialState?.currentUser && location.pathname !== loginPath) {
history.push(loginPath);
}
},
...initialState?.settings,
menuDataRender: () => {
if (initialState?.menus) {
return initialState.menus;
}
return [];
},
menuItemRender: (menuItemProps: MenuDataItem) => {
return <MenuItemRender {...menuItemProps} />;
}
}
};
// 菜单
{
path: '/welcome',
name: 'welcome',
icon: 'smile',
component: './Welcome'
},
{
path: '/admin',
name: 'admin',
icon: 'crown',
access: 'canAdmin',
component: './Admin',
routes: [
{
path: '/admin/sub-page',
name: 'sub-page',
icon: 'smile',
component: './Welcome'
},
{
component: './404'
}
]
},
{
name: 'list.table-list',
icon: 'table',
path: '/list',
component: './TableList'
},
@kevinsheep 是写法问题吗?iconPrefixes 的判断也去掉了,菜单加载出来一直是一个路径...
@kevinsheep 是写法问题吗?iconPrefixes 的判断也去掉了,菜单加载出来一直是一个路径...
确认一下,icon中的名称,与你的图标库中对应图标命名完全一致(大小写敏感)
@kevinsheep 是写法问题吗?iconPrefixes 的判断也去掉了,菜单加载出来一直是一个路径...
确认一下,icon中的名称,与你的图标库中对应图标命名完全一致(大小写敏感)
引用的就是Ant Design 的图标库
@kevinsheep 是写法问题吗?iconPrefixes 的判断也去掉了,菜单加载出来一直是一个路径...
确认一下,icon中的名称,与你的图标库中对应图标命名完全一致(大小写敏感)
引用的就是Ant Design 的图标库
~\config\defaultSettings.ts iconfontUrl 怎么设置的?完整点的代码贴一下?
defaultSettings
scriptUrl: '//at.alicdn.com/t/font_8d5l8fzk5b87iudi.js' 。
基本上的代码就是楼上贴的了呀,主要是app.tsx里面调用menuItemRender
scriptUrl: '//at.alicdn.com/t/font_8d5l8fzk5b87iudi.js' 。
对比你的字库文件和菜单设置中的icon名称,问题应该是你的图标字库文件内容:
那些叫smile,crown的图标,一个都找不到,并且留意到你的图标名称是以icon为前缀的
scriptUrl: '//at.alicdn.com/t/font_8d5l8fzk5b87iudi.js' 。
用的是 Ant Design 的图标库,加上icon- 也不行
你在这个js中搜索一下,有没有一个叫’icon-smile‘的图标名?
scriptUrl: '//at.alicdn.com/t/font_8d5l8fzk5b87iudi.js' 。
用的是 Ant Design 的图标库,加上icon- 也不行你在这个js中搜索一下,有没有一个叫’icon-smile‘的图标名?
我以为这个链接时 antd 的图标库...里面没有smile的图标
在utils中封装了一个方法;
`import React from 'react'; import { MenuDataItem } from '@ant-design/pro-layout'; import * as allIcons from '@ant-design/icons'; // FIX从接口获取菜单时icon为string类型 const fixMenuItemIcon = (menus: MenuDataItem[], iconType = 'Outlined'): MenuDataItem[] => { menus.forEach((item) => { const { icon, children } = item; if (typeof icon === 'string') { let fixIconName = icon.slice(0, 1).toLocaleUpperCase() + icon.slice(1) + iconType; item.icon = React.createElement(allIcons[fixIconName] || allIcons[icon]); } children && children.length > 0 ? (item.children = fixMenuItemIcon(children)) : null; }); return menus; }; export default fixMenuItemIcon;` 在layout组件中直接调用 `menuDataRender={() => fixMenuItemIcon(menus.menusData)}`
就能解决了
scriptUrl: '//at.alicdn.com/t/font_8d5l8fzk5b87iudi.js' 。
用的是 Ant Design 的图标库,加上icon- 也不行你在这个js中搜索一下,有没有一个叫’icon-smile‘的图标名?
我以为这个链接时 antd 的图标库...里面没有smile的图标
在utils中封装了一个方法;
`import React from 'react'; import { MenuDataItem } from '@ant-design/pro-layout'; import * as allIcons from '@ant-design/icons'; // FIX从接口获取菜单时icon为string类型 const fixMenuItemIcon = (menus: MenuDataItem[], iconType = 'Outlined'): MenuDataItem[] => { menus.forEach((item) => { const { icon, children } = item; if (typeof icon === 'string') { let fixIconName = icon.slice(0, 1).toLocaleUpperCase() + icon.slice(1) + iconType; item.icon = React.createElement(allIcons[fixIconName] || allIcons[icon]); } children && children.length > 0 ? (item.children = fixMenuItemIcon(children)) : null; }); return menus; }; export default fixMenuItemIcon;` 在layout组件中直接调用 `menuDataRender={() => fixMenuItemIcon(menus.menusData)}`
就能解决了
scriptUrl: '//at.alicdn.com/t/font_8d5l8fzk5b87iudi.js' 。
用的是 Ant Design 的图标库,加上icon- 也不行你在这个js中搜索一下,有没有一个叫’icon-smile‘的图标名?
我以为这个链接时 antd 的图标库...里面没有smile的图标
这个子菜单的icon好像渲染不出来
这里有 ant-design-pro
的使用示例
这里有
ant-design-pro
的使用示例
这是写死的icon
这么久了还有挖坟的
不改代码,直接在nginx里面加一行rewrite规则即可:
location ~* ^/(.*)/logo\.svg$ { rewrite ^/(.*)/(logo\.svg)$ https://a.com/logo.svg permanent; }
动态菜单的渲染 subMenuItemRender 和 menuItemRender 两个渲染API要配合使用。menuItemRender可以参考 #8101 (comment)
您好 请问解决了吗
icon 不显示的问题,可以自定义菜单项渲染方法,并引用自定义的图标库。 看到这个问题被关闭,并标记为Feature Request,不知道以后是否有官方解决方案。 我的方案如下:
// MenuItemRender.tsx,`app.tsx`直接引用即可 import { MenuDataItem } from '@ant-design/pro-layout'; import { createFromIconfontCN } from '@ant-design/icons'; import { Link, useIntl } from 'umi'; let IconFont = createFromIconfontCN({ // 以下是默认值,也可以按需要指定 // scriptUrl: defaultSettings.iconfontUrl, }); const getIcon = ( icon?: string | React.ReactNode, iconPrefixes: string = 'icon-', ): React.ReactNode => { if (typeof icon === 'string' && icon !== '') { // 可加入多种图标类型的兼容写法,此处省略 if (icon.startsWith(iconPrefixes)) { return <IconFont type={icon} className={icon} />; } } return icon; }; const MenuItem: React.FC<MenuDataItem> = (menuItemProps) => { const { formatMessage } = useIntl(); const { isUrl: isLink, path, icon, locale } = menuItemProps; const localeStr = locale as string; // 和 `formatMessage中` 的 `id` 类型不一致,只好断言一下 const itemContent = ( <span className="ant-pro-menu-item"> {getIcon(icon)} <span className="ant-pro-menu-item-title">{formatMessage({ id: localeStr })}</span> </span> ); return isLink || !path || location.pathname === path ? ( itemContent ) : ( <Link to={path}>{itemContent}</Link> ); }; export default MenuItem;还是想用 Ant Design 的图标库?拿去吧:https://www.iconfont.cn/collections/detail?cid=9402
icon 不显示的问题,可以自定义菜单项渲染方法,并引用自定义的图标库。 看到这个问题被关闭,并标记为Feature Request,不知道以后是否有官方解决方案。 我的方案如下:
// MenuItemRender.tsx,`app.tsx`直接引用即可 import { MenuDataItem } from '@ant-design/pro-layout'; import { createFromIconfontCN } from '@ant-design/icons'; import { Link, useIntl } from 'umi'; let IconFont = createFromIconfontCN({ // 以下是默认值,也可以按需要指定 // scriptUrl: defaultSettings.iconfontUrl, }); const getIcon = ( icon?: string | React.ReactNode, iconPrefixes: string = 'icon-', ): React.ReactNode => { if (typeof icon === 'string' && icon !== '') { // 可加入多种图标类型的兼容写法,此处省略 if (icon.startsWith(iconPrefixes)) { return <IconFont type={icon} className={icon} />; } } return icon; }; const MenuItem: React.FC<MenuDataItem> = (menuItemProps) => { const { formatMessage } = useIntl(); const { isUrl: isLink, path, icon, locale } = menuItemProps; const localeStr = locale as string; // 和 `formatMessage中` 的 `id` 类型不一致,只好断言一下 const itemContent = ( <span className="ant-pro-menu-item"> {getIcon(icon)} <span className="ant-pro-menu-item-title">{formatMessage({ id: localeStr })}</span> </span> ); return isLink || !path || location.pathname === path ? ( itemContent ) : ( <Link to={path}>{itemContent}</Link> ); }; export default MenuItem;还是想用 Ant Design 的图标库?拿去吧:https://www.iconfont.cn/collections/detail?cid=9402
从服务器加载 menu 并且使用 icon
我也是这个思路
击掌(✧∇✧)╯╰(✧∇✧)̣
根据图标字符串创建图标元素,除了使用上文提到的这个方法:
item.icon = React.createElement(allIcons[fixIconName] || allIcons[icon]);
也可以使用
<Icon component={allIcons['fixIconName']} />
(其中Icon标签来源:import Icon from '@ant-design/icons';
)
不知道哪个性能好一些?
icon 只能用 dom,access 必须要触发页面的重新渲染才有用
如何重新渲染呢! 贴个示例悄悄呢,大神