vue-动态菜单 带本地动态路由结合
思路:
方法1.按照以往的动态菜单来做,就是根据权限调取后端接口,获取相应的菜单数据,对数据进行处理,添加到路由对象中
方法2.自己在router.js中写全部的路由,然后设置一个标识,可以是id可以是编码,通过调取后端的接口,对数据进行标识匹配然后进行处理和添加
方法3.写一点共用的不用权限控制路由的公用路由,然后再写一些因为业务需要展示的父级目录,然后把其他的父级和子级都拆分成一级的,写上主要的component,再根据标识进行匹配和数据处理
方法4.在路由js中写共用路由和异步路由,异步路由中的meta属性中增加roles权限数组,在登录的时候拿到用户的权限数组信息,然后进行方法比对,如果是匹配成功就抽离出来,新保存一个数组当做路由数组弄上去。也可以使用引入的js进行比对
方法5.写出完整的路由信息,然后登录的时候拿到用户的权限信息,在渲染的时候根据引入的js来比对进行渲染页面(haspeimi权限生成自定义指令)
参考:https://www.cnblogs.com/fqh123/p/11094296.html
业务:根据sso登录返回的菜单目录和数据进行动态显示,其中有一些公用的路由,还有一些是属于动态菜单下动态数据路由展示的路由和子级菜单
比如:(父级为固定展示,子级因为数据不确定要调取接口,用数据作为子级菜单动态展示的)
{
name: "Mdme",
path: "/mdme",
hidden: false,
component: Layout,
alwaysShow: true,
menuCode: "mxcs_1000",
meta: {
title: "我的模型",
icon: "fa fa-gear",
noCache: false,
link: "mdme"
},
children: [
{
name: "MakeType",
path: ":type",
component: () => import('@/views/models/list/index'),
meta: {
title: "模型类型",
modelType: 'me',
importComponent: true,
},
}
]
}
路由对象js文件
import {
createWebHistory,
createRouter,
createWebHashHistory
} from 'vue-router'
import Layout from '@/layout'
// 公共路由
export const constantRoutes = [{
path: '/redirect',
component: Layout,
hidden: true,
children: [{
path: '/redirect/:path(.*)',
component: () => import('@/views/redirect/index.vue')
}]
},
{
path: '/mdindex',
component: () => import('@/views/models/index'),
hidden: false
},
{
path: '/resources',
component: () => import('@/views/resources/index'),
hidden: true,
meta: {
title: '资源目录',
}
},
{
path: '/login',
component: () => import('@/views/login'),
hidden: true
},
{
path: "/:pathMatch(.*)*",
component: () => import('@/views/error/404'),
hidden: true
},
{
path: '/401',
component: () => import('@/views/error/401'),
hidden: true
},
{
path: '',
redirect: '/home/index',
},
{
path: '/apply',
component: () => import('@/views/apply/index'),
hidden: true,
meta: {
title: '模型申请',
}
},
{
path: '/report',
hidden: true,
component: () => import('@/views/report/index'),
meta: {
title: '模型分析',
}
},
];
/**
* @description: 本地路由 (获取到接口路由后 ,补充component信息)
* @return {*}
*/
export const localMenuList = [
{
name: "Home",
path: "/home",
hidden: true,
redirect: "index",
component: Layout,
children: [{
name: "HomeIndex",
path: "index",
hidden: true,
component: () => import('@/views/home/index'),
meta: {
title: "首页",
importComponent: true,
noCache: false,
link: "mdme"
},
}]
},
{
name: "Mdme",
path: "/mdme",
hidden: false,
component: Layout,
alwaysShow: true,
menuCode: "mxcs_1000",
meta: {
title: "我的模型",
icon: "fa fa-gear",
noCache: false,
link: "mdme"
},
children: [
{
name: "MakeType",
path: ":type",
component: () => import('@/views/models/list/index'),
meta: {
title: "模型类型",
modelType: 'me',
importComponent: true,
},
}
]
}, {
name: "Mdapply",
path: "/mdapply",
hidden: false,
component: Layout,
alwaysShow: true,
menuCode: "mxcs_2000",
meta: {
title: "申请的模型",
icon: "fa fa-gear",
noCache: false,
link: "mdapply"
},
children: [
{
name: "MdApplyType",
path: ":type",
component: () => import('@/views/models/list/index'),
meta: {
title: "模型类型",
modelType: 'apply',
importComponent: true,
},
},
]
},
{
menuCode: "mxcs_1001",
component: () => import('@/views/models/create/index'),
title: "模型创建",
},
{
component: Layout,
menuCode: "mxcs_3000",
title: "管理员审批",
},
{
menuCode: "mxcs_3002",
component: () => import('@/views/audit/task/index'),
title: "模型审批",
},
{
menuCode: "mxcs_3001",
component: () => import('@/views/audit/comments/index'),
title: "评论审批",
},
{
component: Layout,
menuCode: "mxcs_4000",
title: "系统管理",
},
{
menuCode: "mxcs_4001",
component: () => import('@/views/system/category/index'),
title: "分类管理",
},
{
menuCode: "mxcs_4002",
component: () => import('@/views/system/user/index'),
title: "用户管理",
},
{
menuCode: "mxcs_4003",
component: () => import('@/views/system/role/index'),
title: "角色管理",
},
{
menuCode: "mxcs_4004",
component: () => import('@/views/system/message/index'),
title: "消息管理",
},
{
menuCode: "mxcs_4005",
component: () => import('@/views/system/operlog/index'),
title: "日志管理",
},
]
const router = createRouter({
history: createWebHashHistory(),
routes: constantRoutes,
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
return {
top: 0
}
}
},
});
export default router;
VUEX中action方法
import {getMenuCodeList,buildMenu,filterAsyncRoutes } from '@/utils/tools'
//生成路由-sso动态
GenerateRoutes({
commit
}) {
return new Promise((resolve) => {
// 向后端请求路由数据
getSSORouters().then(res => {
// console.log(res,"_________________动态路由")
let menuList = res.data
let menuCodeList = getMenuCodeList(menuList, [])
commit("SET_MENUPERMISSIONS", menuList);
let remoteMenuCodeList = menuCodeList; //获取远程菜单code数组
let accessedRoutes = [];
let remoteMenuList = menuList;//从sessionsstoreage里获取数据库里存放的菜单数据
accessedRoutes = buildMenu(remoteMenuList, localMenuList)// 生成完整的路由
accessedRoutes = filterAsyncRoutes(accessedRoutes, remoteMenuCodeList)//根据远程获取的菜单code 过滤菜单。
accessedRoutes = accessedRoutes.concat(constantRoutes)//共用的路由
commit('SET_ROUTES', accessedRoutes)
/*设置左侧菜单栏,获取用户我的模型和申请的模型数量,决定展示哪些子菜单,以及主菜单的数量*/
initSidebarRoutes(accessedRoutes, commit)
resolve(accessedRoutes);
})
})
}
//设置侧边栏菜单
const initSidebarRoutes = async (sidebarRoutes, commit) => {
let routerList = _lodash.cloneDeep(sidebarRoutes);
let {
applyClassifyList,
applyCount,
modelClassifyList,
modelCount
} = await getMyModelClassify();
// console.log(applyClassifyList,"-=-=-=-=-=-=-")
let myModelRouter = (modelClassifyList || []).map(item => ({
path: `${item.firstClassifyId}`,
meta: {
title: item.firstClassifyName
},
}))
let applyModelRouter = (applyClassifyList || []).map(item => ({
path: `${item.firstClassifyId}`,
meta: {
title: item.firstClassifyName
},
}))
let applyModelIndex = _lodash.findIndex(routerList, {
name: 'Mdapply'
})
let myModelIndex = _lodash.findIndex(routerList, {
name: 'Mdme'
})
// console.log(routerList[applyModelIndex],"applyModelIndex")
//根据请求菜单数据进行添加
if (applyModelIndex > -1) {
routerList[applyModelIndex].alwaysShow = true //当只有一个子栏目数据时,保证父级菜单显示
routerList[applyModelIndex].meta.title = `${routerList[applyModelIndex].meta.title}(${applyCount})`
routerList[applyModelIndex].children = applyModelRouter
routerList[applyModelIndex].hidden = !routerList[applyModelIndex].children.length
}
if (myModelIndex > -1) {
routerList[myModelIndex].meta.title = `${routerList[myModelIndex].meta.title}(${modelCount})`
routerList[myModelIndex].children = [routerList[myModelIndex].children[0],...myModelRouter]
// routerList[myModelIndex].children = myModelRouter
// routerList[myModelIndex].hidden = !routerList[myModelIndex].children.length
}
commit('SET_MY_MODEL_CLASSIFY', {
applyClassifyList,
applyCount,
modelClassifyList,
modelCount
})
//设置侧边栏菜单
commit('SET_SIDEBAR_ROUTERS', routerList)
}
路由守卫 beforeEach
store.dispatch('GetInfo').then(() => {
store.dispatch('GenerateRoutes').then(accessRoutes => {
// 根据roles权限生成可访问的路由表
// console.log(accessRoutes,"要添加的路由信息")
accessRoutes.forEach(route => {
router.addRoute(route) // 动态添加可访问路由表
})
// console.log(router.getRoutes(),"打印全部路由")
next({
...to,
replace: true
})
// hack方法 确保addRoutes已完成
})
})
工具js
/**
* @description: 提取菜单menuCode
* @param {*} list
* @param {*} res
* @return {*}
*/
export function getMenuCodeList(list, res = []) {
list.forEach(v => {
const tmp = v.menuCode;
if (v.childList) {
getMenuCodeList(v.childList, res)
}
res.push(tmp)
})
return res;
}
/**
* Use meta.role to determine if the current user has permission
* @param roles
* @param route
*/
function hasPermission(menuCodeList, temp) {
if (temp.meta && temp.meta.menuCode) {
return menuCodeList.includes(temp.meta.menuCode)
} else {
return true
}
}
/**
* Filter asynchronous routing tables by recursion
* @param routes asyncRoutes
* @param roles
*/
export function filterAsyncRoutes(routes, menuCodeList) {
const res = []
routes.forEach(route => {
const tmp = {
...route
}
if (hasPermission(menuCodeList, tmp)) {
if (tmp.children) {
tmp.children = filterAsyncRoutes(tmp.children, menuCodeList)
}
res.push(tmp)
}
})
return res;
}
/**
* @description: 生成完整的route菜单
* @param {*} remoteRouteList 数据库返回的菜单结构
* @param {*} routeMenu 本地存放的菜单数据 (component) menuCode关联
* @return {*}
*/
export function buildMenu(remoteRouteList, routeMenu) {
let res = []
remoteRouteList.forEach(item =>{
let gongyong = routeMenu.find(z =>z.menuCode === item.menuCode)
let tmp = {
children:item.childList,
...item
}
if (tmp.children.length) {
tmp.children = buildMenu(tmp.children, routeMenu)
}
res.push({
name:gongyong.name || "",
path: item.menuPath,
component: gongyong.component,
hidden: item.hidden || false,
meta: {
title: item.menuName,
menuCode: item.menuCode,
},
children: (tmp.children || []).concat(gongyong.children || [])
})
})
return res
}
ps:
踩坑1:vue3只有addRoute方法 没有addRoutes
踩坑2:后端返回的路径中 父级path为/xx,子级为zz,切记子级没有/ ,需要注意后端给你返回的属性是children吗
踩坑3:父级下是一个子菜单的父级菜单不显示,需要手动添加alwaysShow: true属性
踩坑4:如果自己是需要一点自己写父级和子级,需要注意name属性,不然会报错
最后附上后端接口格式和菜单显示


好了,大家有问题或者想要详细代码可以留言
本文仅提供参考,是本人闲时所写笔记,如有错误,还请赐教,作者:阿蒙不萌,大家可以随意转载

浙公网安备 33010602011771号