本项目采用的模板为vue-element-admin i18n分支

在网上看了很多教程,发现都写的很复杂(本人比较菜) , 用尽千辛万苦终于实现了这个功能

首先修改src/store/modules目录下的permission.js文件

为了区分,下面先附上原文件代码

import { asyncRoutes, constantRoutes } from '@/router'

/**
 * Use meta.role to determine if the current user has permission
 * @param roles
 * @param route
 */
function hasPermission(roles, route) {
  if (route.meta && route.meta.roles) {
    return roles.some(role => route.meta.roles.includes(role))
  } else {
    return true
  }
}

/**
 * Filter asynchronous routing tables by recursion
 * @param routes asyncRoutes
 * @param roles
 */
export function filterAsyncRoutes(routes, roles) {
  const res = []

  routes.forEach(route => {
    const tmp = { ...route }
    if (hasPermission(roles, tmp)) {
      if (tmp.children) {
        tmp.children = filterAsyncRoutes(tmp.children, roles)
      }
      res.push(tmp)
    }
  })

  return res
}

const state = {
  routes: [],
  addRoutes: []
}

const mutations = {
  SET_ROUTES: (state, routes) => {
    state.addRoutes = routes
    state.routes = constantRoutes.concat(routes)
  }
}

const actions = {
  generateRoutes({ commit }, roles) {
    return new Promise(resolve => {
      let accessedRoutes
      if (roles.includes('admin')) {
        accessedRoutes = asyncRoutes || []
      } else {
        accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
      }
      commit('SET_ROUTES', accessedRoutes)
      resolve(accessedRoutes)
    })
  }
}

export default {
  namespaced: true,
  state,
  mutations,
  actions
}

下面附上修改后的代码

import { asyncRoutes, constantRoutes } from '@/router'
import { getMenu } from '@/api/menu'
import Layout from '@/layout' 
/**
 * Use meta.role to determine if the current user has permission
 * @param roles
 * @param route
 */
function hasPermission(roles, route) {
  if (route.meta && route.meta.roles) {
    return roles.some(role => route.meta.roles.includes(role))
  } else {
    return true
  }
}

/**
 * 后台查询的菜单数据拼装成路由格式的数据
 * @param routes
 * @param data
 */
export function generaMenu(routes, data) {
  const len = data.length
  // eslint-disable-next-line no-const-assign
  for (let i = 0; i < len; i++) {
    /*    alert(data[i].path)
    alert(data[i].meta.title)*/
    const menu = {
      path: data[i].path + '',
      // component: () => import(`${item.component}`),
      component: Layout,
      alwaysShow: data[i].alwaysShow,
      redirect: data[i].redirect + '',
      name: data[i].name + '',
      // hidden: true,
      meta: { title: data[i].meta.title + '', icon: data[i].meta.icon },
      children: []
    }
    // alert('data[i].children' + data[i].children)
    if (data[i].children) {
      const children = data[i].children
      for (let j = 0; j < children.length; j++) {
        const viewPath = children[j].component
        const childrenList = {
          path: children[j].path + '',
          component: (resolve) => require([`@/views/${viewPath}`], resolve),
          name: children[j].name + '',
          // hidden: true,
          // component: () => import(`@/views/${viewPath}`),
          meta: { title: children[j].meta.title + '', icon: children[j].meta.icon }
        }
        menu.children.push(childrenList)
      }
    }
    routes.push(menu)
    /* data.forEach(item => {
      alert(JSON.stringify(item))
      const menu = {
        path: item.path + '',
        // component: () => import(`${item.component}`),
        component: Layout,
        name: item.name + '',
        // hidden: true,
        meta: { title: item.title + '', icon: item.icon, roles: ['admin'] },
        children: []
      }
      if (item.children) {
        const children = data.children
        children.forEach(item1 => {
          const childrenList = {
            path: item1.path + '',
            component: () => import(`${item1.component}`),
            name: item1.name + '',
            // hidden: true,
            meta: { title: item1.title + '', icon: item1.icon, roles: ['admin'] }
          }
          menu.children.push(childrenList)
        })
      }
      routes.push(menu)
    })*/
  }
}
/**
 * Filter asynchronous routing tables by recursion
 * @param routes asyncRoutes
 * @param roles
 */
export function filterAsyncRoutes(routes, roles) {
  const res = []

  routes.forEach(route => {
    const tmp = { ...route }
    if (hasPermission(roles, tmp)) {
      if (tmp.children) {
        tmp.children = filterAsyncRoutes(tmp.children, roles)
      }
      res.push(tmp)
    }
  })

  return res
}

const state = {
  routes: [],
  addRoutes: []
}

const mutations = {
  SET_ROUTES: (state, routes) => {
    state.addRoutes = routes
    state.routes = constantRoutes.concat(routes)
  }
}

const actions = {
  generateRoutes({ commit }, roles) {
    return new Promise(resolve => {
      // 先查询后台并返回左侧菜单数据并把数据添加到路由
      getMenu().then(response => {
        const data = response.data
        // Object.assign(loadMenuData, data)
        generaMenu(asyncRoutes, data)
        let accessedRoutes
        if (roles.includes('admin')) {
          // alert(JSON.stringify(asyncRoutes))
          accessedRoutes = asyncRoutes || []
        } else {
          accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
        }
        commit('SET_ROUTES', accessedRoutes)
        resolve(accessedRoutes)
        // generaMenu(asyncRoutes, data)
      }).catch(error => {
        console.log(error)
      })
    })
  }
}

/* const actions = {
  generateRoutes({ commit }, roles) {
    return new Promise(resolve => {
      let accessedRoutes
      if (roles.includes('admin')) {
        console.log('asyncRoutes' + asyncRoutes)
        accessedRoutes = asyncRoutes || []
        console.log('accessedRoutes' + accessedRoutes)
      } else {
        accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
      }
      commit('SET_ROUTES', accessedRoutes)
      resolve(accessedRoutes)
    })
  }
}*/

export default {
  namespaced: true,
  state,
  mutations,
  actions
}

代码详细介绍

  1. 引入后台接口的方法(也就是封装好的axios,然后把接口统一写在一个js文件里,用的时候直接引入,方便后期维护)
    首先在@/api目录下(也就是src/api目录)新建menu.js文件,定义一个方法用来获取数据
import request from '@/utils/request'

export function getMenu() {
  return request({
    url: '/menu/selectMenu',
    method: 'post',
  })
}

然后在@/store/moudles下的permission.js中引入

import { getMenu } from '@/api/menu'
  1. 改写actions.generateRoutes方法
    思路: 首先调用接口getMenu从后台获取数据,利generaMenu(asyncRoutes, data) 方法组织数据结构为routes的数据结构(generaMenu为自定义的方法)
const actions = {
  generateRoutes({ commit }, roles) {
    return new Promise(resolve => {
      // 先查询后台并返回左侧菜单数据并把数据添加到路由
      getMenu().then(response => {
        const data = response.data
        // Object.assign(loadMenuData, data)
        generaMenu(asyncRoutes, data)
        let accessedRoutes
        if (roles.includes('admin')) {
          // alert(JSON.stringify(asyncRoutes))
          accessedRoutes = asyncRoutes || []
        } else {
          accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
        }
        commit('SET_ROUTES', accessedRoutes)
        resolve(accessedRoutes)
        // generaMenu(asyncRoutes, data)
      }).catch(error => {
        console.log(error)
      })
    })
  }
}

3.编写generaMenu方法
思路: 就是把获取的数据转换为routes的格式,如下,更加详细的请看目录@/router/index.js下的数据结构
这里只给出示例,因为我的数据当时在编写的时候就在后台进行转换了,所以我的转换方法并不适用你们自己的!
请自行编写转换方法思路!!!!
请自行编写转换方法思路!!!!
请自行编写转换方法思路!!!!

数据结构:

{
    path: '/documentation',
    component: Layout,
    children: [
      {
        path: 'index',
        component: () => import('@/views/documentation/index'),
        name: 'Documentation',
        meta: { title: 'documentation', icon: 'documentation', affix: true }
      }
    ]
  }

generaMenu方法代码

export function generaMenu(routes, data) {
  const len = data.length
  // eslint-disable-next-line no-const-assign
  for (let i = 0; i < len; i++) {
    /*    alert(data[i].path)
    alert(data[i].meta.title)*/
    const menu = {
      path: data[i].path + '',
      // component: () => import(`${item.component}`),
      component: Layout,
      alwaysShow: data[i].alwaysShow,
      redirect: data[i].redirect + '',
      name: data[i].name + '',
      // hidden: true,
      meta: { title: data[i].meta.title + '', icon: data[i].meta.icon },
      children: []
    }
    // alert('data[i].children' + data[i].children)
    if (data[i].children) {
      const children = data[i].children
      for (let j = 0; j < children.length; j++) {
        const viewPath = children[j].component
        const childrenList = {
          path: children[j].path + '',
          component: (resolve) => require([`@/views/${viewPath}`], resolve),
          name: children[j].name + '',
          // hidden: true,
          // component: () => import(`@/views/${viewPath}`),
          meta: { title: children[j].meta.title + '', icon: children[j].meta.icon }
        }
        menu.children.push(childrenList)
      }
    }
    routes.push(menu)
  }
}

对应的数据库表结构

element动态路由菜单 vue-element-admin动态菜单_vue-admin


请自行编写转换方法思路!!!!

请自行编写转换方法思路!!!!

请自行编写转换方法思路!!!!

需要引入Layoutimport Layout from '@/layout'

只要可以把数据转换为规定的结构即可!!

根据角色权限动态生成路由菜单

在以上基础上只需要修改接口接收role参数,然后把参数传入后台,后台根据角色信息返回该角色可访问的菜单列表即可

export function getMenu(data) {
  return request({
    url: '/menu/selectMenu',
    method: 'post',
    params: data
  })
}
const roleName = roles[0]
      getMenu({ roleName: roleName }).then(response => {