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属性,不然会报错

 

最后附上后端接口格式和菜单显示

 

 

 

 

好了,大家有问题或者想要详细代码可以留言

posted @ 2022-10-11 16:14  阿蒙不萌  阅读(1038)  评论(0编辑  收藏  举报