vue-element-admin 动态路由 vue-element-admin 配置 动态路由 vue-element-admin 实现 动态路由 vue-element-admin自定义路由

1、实现思路

  1. 前端调用 登录接口 成功登录,后端返回 当前登录 当前登录用户 所拥有的菜单信息、token等
  2. 前端拿到 用户 菜单信息 动态存储到 VUEX
  3. permission.js 权限js文件 实现动态路由

2、具体实现

2.1 后端返回的 菜单信息json格式

json中 除了 :id、parent 字段 以外其他字段都是 vue-router字段,和 router.js 文件json几乎一致
建议 刚开始开发先别 实现这个功能,因为这个功能后续实现 很简单很快,等全部功能开发好,直接把 路由 router.js 路由json 信息保存到数据库直接就可以用了

{
    
    
    "code": 0,
    "msg": "请求成功",
    "data": {
    
    
        "menu": [
            {
    
    
                "id": 1,// 这个字段是 数据库表ID
                "parent": 0, // 父级菜单ID,0 表示为一级菜单,没有父级
                "children": [
                    {
    
    
                        "id": 2,
                        "parent": 1,
                        "children": [],
                        "name": "SN管理-SN字典管理",
                        "path": "/dict/index",
                        "component": "use/sn-manage/dict/index",
                        "redirect": "",
                        "meta": {
    
    
                            "icon": "edit",
                            "title": "字典管理"
                        },
                        "hidden": false
                    },
                    {
    
    
                        "id": 3,
                        "parent": 1,
                        "children": [],
                        "name": "SN管理-序列号生成",
                        "path": "/generate/index",
                        "component": "use/sn-manage/generate/index",
                        "redirect": "",
                        "meta": {
    
    
                            "icon": "guide",
                            "title": "序列号生成"
                        },
                        "hidden": false,
                    },
                    {
    
    
                        "id": 8,
                        "parent": 1,
                        "children": [],
                        "name": "SN管理-序列号列表",
                        "path": "/generate/record",
                        "component": "use/sn-manage/generate/record",
                        "meta": {
    
    
                            "icon": "user",
                            "title": "序列号列表"
                        },
                        "hidden": false
                    }
                ],
                "name": "SN管理444",
                "path": "/sn-manage",
                "component": "Layout",
                "redirect": "/sn-manage",
                "meta": {
    
    
                    "icon": "dashboard",
                    "title": "SN管理"
                },
                "hidden": false
            },
            {
    
    
                "id": 4,
                "parent": 0,
                "children": [
                    {
    
    
                        "id": 5,
                        "parent": 4,
                        "children": [],
                        "name": "管理员列表",
                        "path": "/pmsApi/admin/index",
                        "component": "use/admin/index",
                        "meta": {
    
    
                            "icon": "edit",
                            "title": "管理员列表"
                        },
                        "hidden": false
                    }
                ],
                "name": "管理员管理",
                "path": "/admin-manage",
                "component": "Layout",
                "redirect": "/admin-manage",
                "meta": {
    
    
                    "icon": "user",
                    "title": "管理员管理"
                },
                "hidden": false,
            },
            {
    
    
                "id": 9,
                "parent": 0,
                "children": [
                    {
    
    
                        "id": 11,
                        "parent": 9,
                        "children": [],
                        "name": "菜单管理",
                        "path": "/system/menu/index",
                        "component": "use/system-manage/menu/index",
                        "meta": {
    
    
                            "icon": "tree-table",
                            "title": "菜单管理"
                        },
                        "hidden": false
                    },
                    {
    
    
                        "id": 12,
                        "parent": 9,
                        "children": [],
                        "name": "操作人员管理",
                        "path": "/system/operator/index",
                        "component": "use/system-manage/operator/index",
                        "meta": {
    
    
                            "icon": "list",
                            "title": "操作人员管理"
                        },
                        "hidden": false
                    }
                ],
                "name": "系统管理",
                "path": "/system",
                "component": "Layout",
                "meta": {
    
    
                    "icon": "tree",
                    "title": "系统管理"
                },
                "hidden": false,
            }
        ]
    }
}

2.2 修改 vue-element-admin 登陆成功后的逻辑代码

这一步作用是: 把 用户的菜单 json信息保存在 vuex中

修改 user.js, 路径:src/store/modules/user.js
里面有两个方法
方法一:actions 调用的是 login接口

在这里插入图片描述
方法二:getInfo 调用的是 info接口
在这里插入图片描述
登录成功后,逻辑代码 写在哪个方法都可以,这两个方法都是处理登录成功后的操作

我这边就写在 getinfo 里面

user.js 代码

import {
    
     login, logout, getInfo } from '@/api/user'
import {
    
     getToken, setToken, removeToken } from '@/utils/auth'
import router, {
    
     resetRouter } from '@/router'
import {
    
     MessageBox, Message } from 'element-ui'
import request from "@/utils/request.js";

const state = {
    
    
  token: getToken(),
  name: '',
  avatar: '',
  introduction: '',
  roles: [],
  isSuper: false, // 是否为超级管理员,
  menu: [], // 
}

const mutations = {
    
    
  SET_MENU: (state, i) =>{
    
    
    // 管理员菜单
    state.menu = i
  },
  SET_IS_SUPER: (state, i) =>{
    
    
    // 设置是否为超级管理员
    state.isSuper = i
  },
  SET_TOKEN: (state, token) => {
    
    
    state.token = token
  },
  SET_INTRODUCTION: (state, introduction) => {
    
    
    state.introduction = introduction
  },
  SET_NAME: (state, name) => {
    
    
    state.name = name
  },
  SET_AVATAR: (state, avatar) => {
    
    
    state.avatar = avatar
  },
  SET_ROLES: (state, roles) => {
    
    
    state.roles = roles
  }
}

const actions = {
    
    
  // user login
  login({
     
      commit }, userInfo) {
    
    
    const {
    
     username, password, code } = userInfo
    return new Promise((resolve, reject) => {
    
    
      login({
    
     username: username.trim(), password: password, code: code }).then(response => {
    
    
        // 验证登录逻辑
        console.info('data', response)
        if(response.code !== 0){
    
    
          Message({
    
    
            message: response.msg,
            type: 'error',
            duration: 5 * 1000
          })
          reject({
    
    })
        }
        let token = response.data.token;
        commit('SET_TOKEN', token)
        setToken(token)
        resolve()

      }).catch(error => {
    
    
        console.info('登录异常')
        reject(error)
      })
      console.info('完成请求登录')
    })
  },

  getInfo({
     
      commit, state }) {
    
    
    return new Promise((resolve, reject) => {
    
    
      //此方法是login登陆成功后执行用写死的数据代替返回值,注意框架结构!
      getInfo(state.token).then(response => {
    
    
        console.info('请求数据', response)
        const  data  = {
    
    
          roles: ['admin'],
          introduction: 'I am a super administrator',
          avatar: response.data.avatar,
          name: response.data.name,
          isSuperAdmin: response.data.isSuperAdmin, // 后端返回的 当前用户是否为超级管理员
          menu: response.data.menu // 后端返回的 当前用户 菜单JSON数组
        }

        if (!data) {
    
    
          reject('Verification failed, please Login again.')
        }

        const {
    
     roles, name, avatar, introduction,  isSuperAdmin, menu} = data

        // roles must be a non-empty array
        if (!roles || roles.length <= 0) {
    
    
          reject('getInfo: roles must be a non-null array!')
        }

        commit('SET_ROLES', roles)
        commit('SET_NAME', name)
        commit('SET_AVATAR', avatar)
        commit('SET_INTRODUCTION', introduction)
        // 把 是否为超级管理员保存到 vuex中
        commit('SET_IS_SUPER', isSuperAdmin)
        // 把 菜单JSON数组 保存到 vuex中
        commit('SET_MENU', menu)

        resolve(data)
      }).catch(error => {
    
    
        reject(error)
      })
    })
  },
  // user logout
  logout({
     
      commit, state, dispatch }) {
    
    
    return new Promise((resolve, reject) => {
    
    
      logout(state.token).then(() => {
    
    
        commit('SET_TOKEN', '')
        commit('SET_ROLES', [])
        removeToken()
        resetRouter()

        // reset visited views and cached views
        // to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2485
        dispatch('tagsView/delAllViews', null, {
    
     root: true })

        resolve()
      }).catch(error => {
    
    
        reject(error)
      })
    })
  },

  // remove token
  resetToken({
     
      commit }) {
    
    
    return new Promise(resolve => {
    
    
      commit('SET_TOKEN', '')
      commit('SET_ROLES', [])
      removeToken()
      resolve()
    })
  },

  // dynamically modify permissions
  async changeRoles({
     
      commit, dispatch }, role) {
    
    
    const token = role + '-token'

    commit('SET_TOKEN', token)
    setToken(token)

    const {
    
     roles } = await dispatch('getInfo')

    resetRouter()

    // generate accessible routes map based on roles
    const accessRoutes = await dispatch('permission/generateRoutes', roles, {
    
     root: true })
    // dynamically add accessible routes
    router.addRoutes(accessRoutes)

    // reset visited views and cached views
    dispatch('tagsView/delAllViews', null, {
    
     root: true })
  }
}

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

2.3 修改 permission.js 权限文件

文件路径: src/store/modules/permission.js
这一步是最后一步,拿到vuex保存的 当前用户菜单JSON数组,实现 动态路由

重点 是 filterAsyncRouter 方法

permission.js 代码

import {
    
     asyncRoutes, constantRoutes } from '@/router'
import store from '@/store'
import Layout from '@/layout'
import request from '@/utils/request'

/**
 * Use meta.role to determine if the current user has permission
 * @param roles
 * @param route
 */
function hasPermission(roles, route) {
    
    
  console.info('hasPermission')
  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 = []
  console.info('filterAsyncRoutes')
  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)
      }
      let isSuper = store.state.user.isSuper
      console.info('当前登录用户是否为超级管理员', isSuper)
      let userRoutes = []
      if (isSuper === false) {
    
    
        // 不是超级管理员
        userRoutes = filterAsyncRouter(store.state.user.menu)
        commit('SET_ROUTES', userRoutes)
        resolve(userRoutes)
      }else{
    
    
        // 超级管理员,全部权限
        userRoutes = accessedRoutes
        commit('SET_ROUTES', userRoutes)
        resolve(userRoutes)
      }
      console.info('设置动态菜单权限', userRoutes)

    })
  }
}

/**
 * 获取一个字符串值在指定字符串第n次出现的位置
 * @param str 字符串
 * @param cha 查找的字符
 * @param num 第一次出现, 从0开始
 * @returns {*}
 */
function find(str, cha, num) {
    
    
  var x = str.indexOf(cha)
  for (var i = 0; i < num; i++) {
    
    
    x = str.indexOf(cha, x + 1)
  }
  return x
}

/**
 * 将 用户菜单JSON信息 转换为 router 可识别的路由json信息
 * @param t 管理员菜单JSON数组
 * @returns {*}
 */
function filterAsyncRouter(t) {
    
    
  t.filter(index => {
    
    
    if (index.component === 'Layout') {
    
    
      index.component = Layout
    } else {
    
    
      // 不是路由菜单,转换对应 vue组件
      // @/views/sn-manage/dict/index
      let component = index.component
      console.info(component)
      //
      /**
       * 截取 示例 @/views/sn-manage/dict/index 截取出 @/viesw/ 以外的字符,因为 拼接会异常
       * 新逻辑: 创建页面的时候去掉 @/viesw/
       * @type {*|string}
       * let test = component.substr(find(component, '/', 1) + 1)
       */
      index.component = require(`@/views/${
      
      component}.vue`).default

    }
    // 递归子菜单
    if (index.children && index.children.length) {
    
    
      index.children = filterAsyncRouter(index.children)
    }
    return true
  })
  return t
}

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

ok 了

猜你喜欢

转载自blog.csdn.net/qq_40739917/article/details/125943971