react+umi+antd pro动态路由/动态菜单的配置
公司项目需要配置动态菜单,即从后端获取权限菜单,然后显示在左边菜单栏里。而我们现在的菜单是从静态配置文件config/routes.js中获得的。我一开始试过用umi封装的方法patchRoutes和render这两个方法结合来改变routes.js中的routes:routes.js中留下必要的几个路由:export default [{path: '/user',component: '@/l
·
公司项目需要配置动态菜单,即从后端获取权限菜单,然后显示在左边菜单栏里。而我们现在的菜单是从静态配置文件config/routes.js中获得的。我一开始试过用umi封装的方法patchRoutes和render这两个方法结合来改变routes.js中的routes:
routes.js中留下必要的几个路由:
export default [
{
path: '/user',
component: '@/layouts/UserLayout',
routes: [
{
name: 'login',
path: '/user/login',
component: '@/pages/user/login',
},
],
},
{
path: '/',
component: '@/layouts/SecurityLayout',
routes: [
{
path: '/',
component: '@/layouts/BasicLayout',
routes: [
// 动态菜单
],
},
],
},
{
component: './404',
},
]
- 在src目录下新建一个app.js文件
app.js
import { getRouterData } from '@/services/permission'
let routerData = []
export function patchRoutes({ routes }) {
console.log("patchRoutes", routes)
routerData.forEach(item => routes[1].routes[0].routes.push(item))
}
export function render(oldRoutes) {
console.log("render")
getRouterData().then(res => {
if (res.code === 0) {
routerData = res.data[0].children
}
// 必要操作,否则界面一直处于loading状态
oldRoutes()
})
}
但是这个方法有个问题,就是点击路由,界面不会显示相应的组件(有知道的解决方法的大神可以给大家参考下)。为此我也是找了好多文章,终于找到一个解决办法。就是在BasicLayout.jsx中直接修改route.这个方法我就没用到umi的patchRoutes方法了。
- 首先routes.js中也是保留以上内容
- 在BasicLayout.jsx中获取后端的菜单数据
import ProLayout, { DefaultFooter } from '@ant-design/pro-layout'
import React, { useEffect, useState } from 'react'
import RightContent from '@/components/GlobalHeader/RightContent'
import { Link, connect, useDispatch, useSelector, history } from 'umi'
import { Result, Button, Icon } from 'antd'
const BasicLayout = props => {
const {
children,
settings,
location = {
pathname: '/',
},
routerData // 动态菜单
} = props
const dispatch = useDispatch()
useEffect(() => {
// 获取动态菜单
if (dispatch) {
dispatch({
type: 'permission/queryRouterData'
})
}
}, [])
/**
* init variables
*/
const handleMenuCollapse = payload => {
if (dispatch) {
dispatch({
type: 'global/changeLayoutCollapsed',
payload,
})
}
}
return (
<ProLayout
logo={logo}
{...props}
{...settings}
onCollapse={handleMenuCollapse}
menuItemRender={(menuItemProps, defaultDom) => {
if (menuItemProps.isUrl || !menuItemProps.path) {
return defaultDom
}
return <Link to={menuItemProps.path}>{defaultDom}</Link>
}}
breadcrumbRender={(routers = []) => [
{
path: '/',
breadcrumbName: '首页',
},
...routers,
]}
route={{ routes: getRouterMenu(asyncRouters) }}
rightContentRender={() => <RightContent />}
>
{children}
</ProLayout>
)
}
export default connect(({ global, settings, permission }) => ({
collapsed: global.collapsed,
settings,
routerData: permission.routerData
}))(BasicLayout)
- 得到后端的菜单数据routerData后需要对数据格式处理一下
// 后端给的格式
[{
component: "RouteView",
hidden: false,
hideChildrenInMenu: true,
icon: "dashboard",
id: "3502511398862003",
name: "dashboard",
parentId: "0",
path: "/dashboard",
redirect: "/dashboard/workplace",
title: "驾驶舱",
children: []
}]
所以我们可以封装一个方法来对菜单进行格式化,我是专门在config下新建了一个router.js文件,来存放这几个方法了
router.js
// 存放组件
const constantRouterComponents = {
WorkbenchDd: require('@/pages/index.jsx').default
}
import { SmileOutlined, HeartOutlined } from '@ant-design/icons'
// 菜单的icon
const IconMap = {
dashboard: <SmileOutlined />,
heart: <HeartOutlined />,
}
export const generateMenu = (item) => {
const menu = {
// 路由地址 动态拼接生成如 /dashboard/workplace
path: item.path || '',
// 路由名称,建议唯一
name: item.title || '',
// 控制路由是否显示在 sidebar
hideInMenu: item.hidden,
// 该路由对应页面的 组件
component: constantRouterComponents[item.component] || null,
// meta: 页面标题, 菜单图标, 页面权限(供指令权限用,可去掉)
icon: item.icon && IconMap[item.icon],
}
// 为了防止出现后端返回结果不规范,处理有可能出现拼接出两个 反斜杠
if (menu.path.indexOf('http') === -1) {
menu.path = menu.path.replace('//', '/')
}
// 重定向
item.redirect && (menu.redirect = item.redirect)
// 隐藏子菜单
item.hideChildrenInMenu && (menu.hideChildrenInMenu = item.hideChildrenInMenu)
// 菜单跳转到外部地址时使用
item.target && (menu.meta.target = item.target)
// 嵌套外部链接url
item.url && (menu.meta.url = item.url)
return menu
}
export const generateRouter = (item) => {
const router = {
path: item.path,
name: item.name,
exact: true,
};
if (item.redirect) {
router.redirect = item.redirect
}
router.component = constantRouterComponents[item.component]
return router
}
export const getAsyncRouter = (asyncRouterMap) => {
let Routers = []
if (asyncRouterMap && asyncRouterMap.length > 0) {
asyncRouterMap.forEach((item) => {
if (item.children) {
Routers.push(generateRouter(item))
Routers = Routers.concat(getAsyncRouter(item.children))
} else {
Routers.push(generateRouter(item))
}
})
}
return Routers
}
export const getRouterMenu = (asyncRouterMap) => {
let RouterMenus = []
if (asyncRouterMap && asyncRouterMap.length > 0) {
asyncRouterMap.forEach((item) => {
const parent = generateMenu(item)
let children = []
if (item.children) {
children = getRouterMenu(item.children)
}
if (children.length > 0) {
parent.children = children
}
RouterMenus.push(parent)
})
}
return RouterMenus
}
- 然后在BasicLayout.jsx中引入这里的方法,对菜单处理后直接修改props.route.routes的值即可
import { getRouterMenu, getAsyncRouter } from '../../config/router'
let asyncRouters = []
if (routerData.length > 0) {
asyncRouters = routerData[0].children // 取菜单的值
}
const reactRouters = getAsyncRouter(asyncRouters)
useEffect(() => {
reactRouters.forEach((item) => {
props.route.routes.push(item) // 直接修改props.route.routes值
})
history.push(window.location.pathname) // 避免界面不刷新到相应的组件
}, [reactRouters.length])
这里还需要注意一个小问题:就是在ProLayout组件中props和route的顺序:
<ProLayout
logo={logo}
{...props} // 旧值放在前面
{...settings}
onCollapse={handleMenuCollapse}
menuItemRender={(menuItemProps, defaultDom) => {
if (menuItemProps.isUrl || !menuItemProps.path) {
return defaultDom
}
return <Link to={menuItemProps.path}>{defaultDom}</Link>
}}
breadcrumbRender={(routers = []) => [
{
path: '/',
breadcrumbName: '首页',
},
...routers,
]}
footerRender={() => defaultFooterDom}
route={{ routes: getRouterMenu(asyncRouters) }} // 新值放后面
rightContentRender={() => <RightContent />}
>
{children}
</ProLayout>
至此,我们的动态菜单就能在左边菜单栏中显示出来,并且没有组件不显示的问题。
本文的方法参考:https://github.com/ant-design/ant-design-pro/issues/8121
更多推荐
已为社区贡献1条内容
所有评论(0)