vue动态加载导航栏-从数据中读取导航数据

本文详细介绍了如何在Vue项目中实现从数据库动态获取导航栏,并进行权限管理。通过修改user.js、router/index.js和store/modules/permission.js,实现了根据用户角色动态加载路由和匹配权限。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

说明:

近期编写vue项目从数据库中获取导航栏,记录下。

项目

vue-element-admin vue项目是根据 大佬 上传结构的这个简单修改的,搜索
vue-element-admin 第一个就是,附上地址:
vue-element-admin
可以仔细研究下这个项目,很强。

所有修改文件说明

一,需要改写的js(整理):
在这里插入图片描述
根据项目结构需要修改:
【api】包下的 user.js:获取后台传的树形结构 json 方法。
【router】包下 index.js 就是我们的加载的路由设置为空数组就好啦。
【store】包下 【modules】包下 permission.js: 这里面有获取后台json
处理,还有原来的项目匹配权限等等。

开始修改

1,修改user.js:
加上一个方法getAuthMenu,向后台获取请求 ,在premission.js会调用这个方法匹配。
【代码】:

export function getAuthMenu(token) {
  return request({
    url: 'menu/getMenus',
    method: 'post',
    params: { token }
  })
}

【图片说明】
在这里插入图片描述
到此 user.js 修改完毕。。。。。。-----------手动分割----------。。。。。

二,修改 router 包下 index.js
修改index只需把以前配置的路由清除就ok啦,保留404(或者写一个)其他都不需要动。
【代码】

/**
 * asyncRoutes
 * 需要通过角色判断是否显示的菜单
 */
export const asyncRoutes = [
  { path: '*', redirect: '/404', hidden: true }
]

【图片说明】

在这里插入图片描述
-----------手动分割---------------修改完毕。
三,前两步修改完毕剩下最后一步啦,就是修改store下modules下的permission.js啦
这里只需要修改二个地方即可。
需要修改的地方:const actions = {}
【代码】
修改的地方,调用user.js里写的getAuthMenu方法 获取后台传入的json字符串。然后获取 index.js 的asuncRoutes(也就是我们修改的地方) 把数据放进去(注意:404是放在最后的)
代码中也有注释:

const actions = {
  generateRoutes({ state, commit, rootState }, { authList, name }) {
    return new Promise(resolve => {
    
      // 先查询后台并返回左侧菜单数据并把数据添加到路由(user.js)
      getAuthMenu(state.token).then(response => {
      
        // 获取router下index.js中的asyncRoutes
        const tempAsyncRoutes = Object.assign([], asyncRoutes)
        
        // 动态循环路由信息 (此方法下面有说明)
        generaMenu(tempAsyncRoutes, response.menus)


        let allRoutes = deepClone(state.allRoutes) // 深拷贝一份 asyncRoutes,不影响vuex中保存的 asyncRoutes
        
        // 如果 allRoutes 长度为0 说明是刷新了页面,导致 vuex 被清空
        if (allRoutes.length === 0) {
          // 直接使用 asyncRoutes
          allRoutes = tempAsyncRoutes
        }
        
        // 先在vuex里存放一份完整的路由信息,深拷贝,vuex里保存的asyncRoutes 与此处的asyncRoutes无关,此处的asyncRoutes会改变
        commit('SET_allRoutes', deepClone(allRoutes))
        
        let accessedRoutes = []
        
        if (name === 'admin') {
          accessedRoutes = allRoutes || [] // 全部显示 管理员
        } else {
          //如果不是管理员 匹配权限信息,filterAsyncRoutes 方法是vue-element-admin写好			的,不需要修改
          accessedRoutes = filterAsyncRoutes(allRoutes, authList)
        }
        
        commit('SET_ROUTES', accessedRoutes)
        commit('SET_AUTH', authList)
        resolve(accessedRoutes)
      }).catch(error => {
        console.log(error)
      })
    })
  },
  clearRoutes({ commit }) {
    commit('CLEAR_ROUTES')
  },
  setAllRoutes({ commit }, allRoutes) {
    commit('SET_allRoutes', allRoutes)
  }
}

** getAuthMenu(state.token).then(response =>方法,说明:**
该方法是查询后台数据 返回json,tempAsyncRoutes是在index.js修改的asyncRoutes方法,response.menus 是后台返回的字符串。
generateRoutes({ state, commit, rootState }, { authList, name }) 说明:
这里面的自行修改,原来的是generateRoutes({ commit }, roles) ,因为根据业务需求修改的,可以认为roles是authList,name就是登录时获取的登录人。
generaMenu(tempAsyncRoutes, response.menus)
此处是根据后台传回来的数据 递归分配一些参数等等(因为后台返回时只确认啦子父级关系。)代码如下:

export function generaMenu(routes, menuList) {
  let temp = []
  for (let i = 0; i < menuList.length; i++) {
    if (menuList[i].childNodes && menuList[i].childNodes.length >= 1) {
      temp = temp.concat(menuList[i].childNodes)
    } else {
      temp = []
    }
    if (menuList[i].menuPath && /\S/.test(menuList[i].menuPath)) {
      const menu = {
        id: menuList[i].menuId,
        path: menuList[i].menuPath === '#' ? menuList[i].menuId + '_key' : menuList[i].menuPath,
        component: menuList[i].component === '#' ? Layout : () => import(`@/views${menuList[i].component}`),
        name: 'menu_' + menuList[i].menuName,
        meta: {
          title: menuList[i].title,
          icon: menuList[i].menuIcon,
          auth: menuList[i].permission === '#' ? null : ['' + menuList[i].permission + '']
        }
      }
      if (temp.length >= 1) {
      menu.alwaysShow = true
        const child = []
        generaMenu(child, temp)
        menu.children = child
        temp = []
      }
      routes.push(menu)
    }
  }
}

component: menuList[i].component === ‘#’ ? Layout : () => import(@/views${menuList[i].component}),
这一行代码是懒加载,动态引入的。所有的业务页面在
views下面,数据库存的试试路径,如:/comment/materList。
加载时先加载views下,等点击左侧导航时在加载相应页面路径。
也就等于:
component: menuList[i].component === ‘#’ ? Layout : () => import(‘@/views/comment/materList’),
---------------------------手动分割-------------------------------
到此结束------------------------------------------------------。

附上 permission.js 所有代码

import { asyncRoutes, constantRoutes } from '@/router'
import { getAuthMenu } from '@/api/user'
import Layout from '@/layout'
import { deepClone } from '@/utils' // 深拷贝
/**
 * 后台查询的菜单数据拼装成路由格式的数据
 * @param routes
 * @param menuList 后台得到集合
 */
export function generaMenu(routes, menuList) {
  let temp = []
  for (let i = 0; i < menuList.length; i++) {
    if (menuList[i].childNodes && menuList[i].childNodes.length >= 1) {
      temp = temp.concat(menuList[i].childNodes)
    } else {
      temp = []
    }
    if (menuList[i].menuPath && /\S/.test(menuList[i].menuPath)) {
      const menu = {
        id: menuList[i].menuId,
        path: menuList[i].menuPath === '#' ? menuList[i].menuId + '_key' : menuList[i].menuPath,
        component: menuList[i].component === '#' ? Layout : () => import(`@/views${menuList[i].component}`),
        name: 'menu_' + menuList[i].menuName,
        meta: {
          title: menuList[i].title,
          icon: menuList[i].menuIcon,
          auth: menuList[i].permission === '#' ? null : ['' + menuList[i].permission + '']
        }
      }
      if (temp.length >= 1) {
      menu.alwaysShow = true
        const child = []
        generaMenu(child, temp)
        menu.children = child
        temp = []
      }
      routes.push(menu)
    }
  }
}
/**
 * 判断权限
 * @param authList
 * @param route
 */
function hasAuthorization(authList, route) {
  console.log(route)
  if (route.meta && route.meta.auth) { // 当前路由有meta 并且 meta下有auth
    return authList.some(auth => { //   .some :数组中的元素是否满足指定条件
      return route.meta.auth.some(routeAuth => {
        return routeAuth === auth
      })
    })
  } else { // 当前路由无meta 或 auth(1:无需鉴权;2:有children)
    return true
  }
}
// 路由与用户权限集合匹配的方法
export function filterAsyncRoutes(routes, authList) {
  const res = []
  routes.forEach(route => {
    const tmp = route
    if (hasAuthorization(authList, tmp)) { // 先判断当前路由是否有权限
      if (tmp.children) { // tmp下有children时,用children递归
        tmp.children = filterAsyncRoutes(tmp.children, authList)
      }
      // children等于undefined:当前路由没有下级路由,且有访问权限
      // children.length > 0:当前路由有下级路由,且下级路由有访问权限,则把当前路由添加到返回值
      if (typeof (tmp.children) === 'undefined' || tmp.children.length > 0) {
        res.push(tmp)
      }
    }
  })
  return res
}

const state = {
  allRoutes: [], // 侧边栏生成前,保存一份完整的路由信息
  routes: [],
  addRoutes: [],
  authList: []
}
const getters = {
  hasAuthorizations: (state, getters, rootState) => authorization => {
    // 如果用户名为superadmin,显示所有按钮
    if (rootState.user.name === 'superadmin') {
      return true
    }
    return state.authList.some(auth => {
      return auth === authorization
    })
  }
}
const mutations = {
  SET_allRoutes: (state, allRoutes) => {
    state.allRoutes = allRoutes
  },
  SET_ROUTES: (state, routes) => {
    state.addRoutes = routes
    state.routes = constantRoutes.concat(routes)
  },
  SET_AUTH: (state, authList) => {
    state.authList = authList
  },
  CLEAR_ROUTES: (state) => {
    state.routes = []
    state.addRoutes = []
    state.authList = []
  }
}

const actions = {
  generateRoutes({ state, commit, rootState }, { authList, name }) {
    return new Promise(resolve => {
      // 先查询后台并返回左侧菜单数据并把数据添加到路由
      getAuthMenu(state.token).then(response => {
        // 获取router下index.js中的asyncRoutes
        const tempAsyncRoutes = Object.assign([], asyncRoutes)
        // 动态循环路由信息
        generaMenu(tempAsyncRoutes, response.menus)
        let allRoutes = deepClone(state.allRoutes) // 深拷贝一份 asyncRoutes,不影响vuex中保存的 asyncRoutes
        // 如果 allRoutes 长度为0 说明是刷新了页面,导致 vuex 被清空
        if (allRoutes.length === 0) {
          // 直接使用 asyncRoutes
          allRoutes = tempAsyncRoutes
        }
        // 先在vuex里存放一份完整的路由信息,深拷贝,vuex里保存的asyncRoutes 与此处的asyncRoutes无关,此处的asyncRoutes会改变
        commit('SET_allRoutes', deepClone(allRoutes))
        let accessedRoutes = []
        if (name === 'superadmin') {
          accessedRoutes = allRoutes || [] // 全部显示
        } else {
          accessedRoutes = filterAsyncRoutes(allRoutes, authList)
        }
        commit('SET_ROUTES', accessedRoutes)
        commit('SET_AUTH', authList)
        resolve(accessedRoutes)
      }).catch(error => {
        console.log(error)
      })
    })
  },
  clearRoutes({ commit }) {
    commit('CLEAR_ROUTES')
  },
  setAllRoutes({ commit }, allRoutes) {
    commit('SET_allRoutes', allRoutes)
  }
}
export default {
  namespaced: true,
  state,
  mutations,
  actions,
  getters
}

其他说明

这边获取的路由信息,{authList,name} 也可以改成 roles 。这个看需求啦。
在这里插入图片描述

后台代码

这里在说下后台吧
bean
在这里插入图片描述

controller:
在这里插入图片描述
Service
Menu menu = get(“1”)是因为在数据库中写一个【功能菜单】作为第一级,后面匹配其他子级。
在这里插入图片描述
Utils
在这里插入图片描述
mapper
在这里插入图片描述

测试demo算是写完啦。
有不正确的地方欢迎指正。感谢。。。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值