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属性,不然会报错
最后附上后端接口格式和菜单显示
好了,大家有问题或者想要详细代码可以留言
本文仅提供参考,是本人闲时所写笔记,如有错误,还请赐教,作者:阿蒙不萌,大家可以随意转载