当前位置:   article > 正文

vue-element-admin动态路由权限、菜单_vue动态路由导航菜单权限管理

vue动态路由导航菜单权限管理

前言

首先我对于vue和语法糖不熟练,使用的应该是vue3,但具体并不明白vue2和vue3的区别;在此分享我的package.json

  1. {
  2. "name": "vue3_cli_default",
  3. "version": "0.0.0",
  4. "scripts": {
  5. "dev": "vite",
  6. "build": "vite build",
  7. "serve": "vite preview"
  8. },
  9. "dependencies": {
  10. "2": "^3.0.0",
  11. "axios": "^1.3.2",
  12. "element-plus": "^2.2.29",
  13. "fast-glob": "^3.2.12",
  14. "qs": "^6.11.0",
  15. "vue": "^3.2.8",
  16. "vue-router": "^4.1.6",
  17. "vue-wechat-title": "^2.0.7",
  18. "vuex": "^4.0.2"
  19. },
  20. "devDependencies": {
  21. "@vitejs/plugin-vue": "^1.6.0",
  22. "@vue/compiler-sfc": "^3.2.6",
  23. "vite": "^2.5.2",
  24. "vite-plugin-svg-icons": "^2.0.1"
  25. }
  26. }

1、router/index.js

在此配置不需要验证的路由:如login、404;将不需要验证的路由放在constRouter中

  1. import {
  2. createRouter,
  3. createWebHashHistory
  4. } from 'vue-router' //引入vue-router
  5. import store from '../store/index.js'
  6. // 通用页面,这里的配置不需要权限
  7. export const constRouter = [{
  8. path: '/login',
  9. name: 'login',
  10. component: () =>
  11. import ('@/views/login/index.vue'),
  12. meta: {
  13. title: '登录'
  14. }
  15. }, {
  16. path: '/404',
  17. name: '404',
  18. component: () =>
  19. import ('../views/error/404.vue'),
  20. meta: { title: "404" }
  21. }, {
  22. path: '/*',
  23. redirect: '/404'
  24. }, {
  25. path: '/',
  26. redirect: '/home/index', //首页
  27. }]
  28. const router = createRouter({
  29. history: createWebHashHistory(),
  30. routes: constRouter
  31. })
  32. export default router

2、router/permission.js

在permission.js这个文件中处理前置路由守卫的功能。

(如果文字逻辑不清晰请忽略,仅为初步的个人理解)

此时的主要逻辑为:缓存中有token等数据时-->判断store中是否有从后端调起的路由信息数据(addRoutes)-->store中addRoutes没有数据;调用store中的函数去处理从后端调起的路由信息数据

未登录时的逻辑:进入login页面;进行登录操作

输入密码后的逻辑:通过store的user.js处理从后端获取的token、动态路由数据;并进行处理操作(后续有详细代码

  1. // 路由的全局首位
  2. // 权限控制逻辑
  3. import router from './index'
  4. import store from '../store/index'
  5. import { ElMessage } from 'element-plus' //导入消息提示
  6. import { getUser, getRoutes } from '@/utils/auth' // 从cookie获取令牌
  7. const whiteList = ['/login'] //排除的路径
  8. router.beforeEach(async(to, from, next) => {
  9. if (to.path === '/login' && !getRoutes()) {
  10. // 没有菜单权限数据&去登录页-->路由放行
  11. // 清空store的路由数据
  12. await store.dispatch('permission/generateRoutes', [])
  13. next()
  14. } else {
  15. // 判断用户是否已登录
  16. const hasToken = getUser()
  17. if (hasToken) {
  18. try {
  19. if (!store.getters.user) {
  20. store.dispatch('user/setInfo', hasToken)
  21. }
  22. // 判断是否进行路由过滤
  23. if (store.getters.addRoutes.length <= 0) {
  24. const roles_routes_lo = getRoutes()
  25. // 获取动态路由数据
  26. const accessRoutes = await store.dispatch('permission/generateRoutes', roles_routes_lo)
  27. console.log(accessRoutes, "accessRoutes-获取动态路由数据", store.getters.addRoutes)
  28. // 添加这些路由至路由器
  29. router.addRoute(accessRoutes)
  30. next({...to, replace: true })
  31. } else {
  32. // 已经进行路由过滤-->放行
  33. next() //继续即可
  34. }
  35. } catch (error) {
  36. // 捕获异常-->主要是获取用户信息的异常,此处是必要的,返回数据异常极容易造成路由过滤问题,
  37. console.log('add permission error', error)
  38. // // 出错需要重置令牌并重新登陆(令牌过期,网络错误等原因)
  39. await store.dispatch('user/resetToken')
  40. ElMessage({
  41. showClose: true,
  42. message: error || "网络错误-beforeEach",
  43. type: 'error',
  44. })
  45. next(`/login?redirect=${to.path}`)
  46. }
  47. } else {
  48. // 用户无令牌
  49. if (whiteList.indexOf(to.path) !== -1) {
  50. //白名单路由放过
  51. next()
  52. } else {
  53. // 重定向至登录页
  54. next(`/login?redirect=${to.path}`)
  55. }
  56. }
  57. }
  58. })

3、main.js

在此处增加路由前置守卫文件,重点是:import './router/permission'

  1. import { createApp } from 'vue'
  2. import App from './App.vue'
  3. const app = createApp(App)
  4. // router路由
  5. import router from './router/index.js'
  6. app.use(router)
  7. // 路由标题
  8. import VueWebchatTitle from 'vue-wechat-title'
  9. app.use(VueWebchatTitle)
  10. // elementUi
  11. import ElementPlus from 'element-plus'
  12. import 'element-plus/dist/index.css'
  13. app.use(ElementPlus)
  14. // vux
  15. import Vuex from 'vuex'
  16. app.use(Vuex)
  17. // // axios
  18. // import axios from 'axios'
  19. // app.use(axios)
  20. // store
  21. import store from './store/index.js'
  22. app.use(store)
  23. // 全局路由守卫
  24. import './router/permission'
  25. // 导入svg图片插件,可以在页面上显示svg图片
  26. import 'virtual:svg-icons-register'
  27. import SvgIcon from './components/SvgIcon.vue'
  28. app.component('svg-icon', SvgIcon)
  29. app.mount('#app')

4、utils/auth.js

这里处理的是获取缓存中的user、routes等信息

  1. const UserKey = 'user';
  2. const RoutesKey = 'anxinRoutes'
  3. const BtnKey = 'anxinBtn'
  4. // 获取用户信息
  5. export function getUser() {
  6. return JSON.parse(window.localStorage.getItem(UserKey))
  7. }
  8. // 添加用户信息
  9. export function setUser(userInfo) {
  10. return window.localStorage.setItem(UserKey, JSON.stringify(userInfo))
  11. }
  12. export function removeStorage(key) {
  13. // 清除特定键值对
  14. return window.localStorage.removeItem(key);
  15. }
  16. // 获取菜单权限
  17. export function getRoutes() {
  18. return JSON.parse(window.localStorage.getItem(RoutesKey))
  19. }
  20. // 添加菜单权限
  21. export function setRoutes(routes) {
  22. return window.localStorage.setItem(RoutesKey, JSON.stringify(routes))
  23. }

5、store/index.js

  1. import { createStore } from 'vuex'
  2. import permission from './modules/permission'
  3. import user from './modules/user'
  4. export default createStore({
  5. state: {},
  6. mutations: {},
  7. actions: {},
  8. modules: {
  9. permission,
  10. user
  11. },
  12. // 定义全局getters 方便访问user 模块的数据
  13. getters: {
  14. user: state => state.user.user,
  15. addRoutes: state => state.permission.addRoutes,//仅需要加权限设置的路由数据
  16. roles_routes: state => state.permission.roles_routes,//所有路由数据
  17. menuRoutes: state => state.permission.menuRoutes,//在页面中渲染显示的菜单数据
  18. }
  19. })

6、store/modules/permission.js

在此处把路由数据处理为自己需要的数据格式,

从后端获取的路由数据:

 

我需要的数据格式为:

 store/modules/permission.js处理数据格式

  1. // 权限管理模块
  2. import { constRouter } from '@/router'
  3. import { getBtn } from '@/utils/auth'
  4. const state = {
  5. roles_routes: [], //完整路由表
  6. addRoutes: [], //用户可访问路由表
  7. menuRoutes: [], //菜单数据
  8. }
  9. const mutations = {
  10. SET_ROUTES: (state, routes) => {
  11. // routes 用户可以访问的权限
  12. state.addRoutes = routes
  13. // 完整的路由表
  14. state.roles_routes = constRouter.concat(routes)
  15. },
  16. SET_MENU_ROUTES: (state, routes) => {
  17. // 菜单数据
  18. state.menuRoutes = routes
  19. },
  20. }
  21. const actions = {
  22. generateRoutes({ commit }, asyncRoutes) {
  23. return new Promise(resolve => {
  24. let accessedRoutes;
  25. accessedRoutes = asyncRoutes || []
  26. // 需要处理路由数据,在此加函数处理
  27. var last_routes = []
  28. if (accessedRoutes.length != 0) {
  29. last_routes = arrToMenu(accessedRoutes)
  30. }
  31. commit('SET_ROUTES', last_routes) //添加用户可访问的路由表
  32. commit('SET_MENU_ROUTES', accessedRoutes) //添加菜单数据
  33. resolve(last_routes)
  34. })
  35. }
  36. }
  37. // 处理菜单数据
  38. export function arrToMenu(routes) {
  39. var nodes = {
  40. path: '/',
  41. component: eval(`() => import('../../views/layout/index.vue')`),
  42. children: [],
  43. }
  44. var res = []
  45. for (var i = 0; i < routes.length; i++) {
  46. const row = routes[i]
  47. var child = {}
  48. if (!row.children) {
  49. child = {
  50. path: row.path,
  51. name: row.name,
  52. component: eval(`() => import('../../views${row.path}.vue')`),
  53. meta: {
  54. title: row.name,
  55. icon: row.source
  56. },
  57. }
  58. res.push(child)
  59. } else {
  60. for (var j = 0; j < row.children.length; j++) {
  61. var row_child = row.children[j]
  62. child = {
  63. path: row_child.path,
  64. name: row_child.name,
  65. component: eval(`() => import('../../views${row.path}${row_child.path}.vue')`),
  66. meta: {
  67. title: row_child.name,
  68. icon: row_child.source
  69. },
  70. }
  71. res.push(child)
  72. }
  73. }
  74. }
  75. nodes.children = res
  76. // console.log(nodes, 'nodes')
  77. return nodes;
  78. }
  79. export default {
  80. namespaced: true,
  81. state,
  82. mutations,
  83. actions
  84. }

7、login.vue

  1. <template>
  2. <div class="login-main">
  3. <div class="login-box">
  4. <div class="login-header">
  5. <img src="../../assets/logo.png" class="login-logo">
  6. </div>
  7. <el-form
  8. ref="ruleFormRef"
  9. :rules="formRule"
  10. :model="formVal"
  11. >
  12. <el-form-item label="账号" prop="account">
  13. <el-input v-model="formVal.account" placeholder="请输入账号" />
  14. </el-form-item>
  15. <el-form-item label="密码" prop="password">
  16. <el-input v-model="formVal.password" placeholder="请输入密码" />
  17. </el-form-item>
  18. <el-form-item>
  19. <el-button type="primary" @click="onSumbit" :loading="loading">登录</el-button>
  20. </el-form-item>
  21. </el-form>
  22. </div>
  23. </div>
  24. </template>
  25. <script>
  26. import { login,menutreeList } from '@/utils/api/user.js'
  27. export default{
  28. data(){
  29. return {
  30. formVal:{account:'',password:'',},
  31. formRule:{
  32. account: [
  33. { required: true, message: '请输入账号', trigger: 'blur' },
  34. ],
  35. password: [
  36. { required: true, message: '请输入密码', trigger: 'blur' },
  37. ],
  38. },
  39. loading: false, //登陆状态
  40. }
  41. },
  42. methods: {
  43. // 登录功能
  44. async onSumbit () {
  45. var _this = this
  46. try {
  47. // 1、表单验证
  48. await this.$refs.ruleFormRef.validate()
  49. this.loading = true
  50. // 2、请求
  51. const { data } = await login(_this.formVal)
  52. // 处理菜单栏数据
  53. _this.login(data)
  54. _this.$message.success(data.msg)
  55. } catch (err) {
  56. this.loading = false
  57. console.log('验证失败', err)
  58. }
  59. },
  60. login(data){
  61. // 在此处理数据
  62. this.$store
  63. .dispatch('user/login',data)
  64. .then(()=>{
  65. this.loading = true
  66. // 登陆成功后重定向
  67. this.$router.push({
  68. path: this.$route.query.redirect || '/'
  69. })
  70. })
  71. .catch(err=>{
  72. this.loading = true
  73. console.log(err)
  74. })
  75. }
  76. }
  77. }
  78. </script>
  79. <style scoped>
  80. .login-main{
  81. width: 100vw;
  82. height: 100vh;
  83. display: flex;
  84. align-items: center;
  85. justify-content: center;
  86. background-image: url('../../assets/bg1.jpg');
  87. background-position: right bottom;
  88. background-repeat: no-repeat;
  89. background-size: cover;
  90. }
  91. .login-box{
  92. width: 300px;
  93. height: 200px;
  94. border-radius: 5px;
  95. box-shadow: 10px 10px 5px rgba(0, 0, 0, 0.3);
  96. background-color: #fff;
  97. display: flex;
  98. flex-direction: column;
  99. padding: 2rem;
  100. align-items: center;
  101. justify-content: center;
  102. }
  103. .login-header{
  104. padding-bottom: 20px;
  105. }
  106. .login-logo{
  107. width: 70px;
  108. height: 70px;
  109. border-radius: 50%;
  110. box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.3);
  111. }
  112. .el-button{
  113. width: 100%;
  114. }
  115. </style>

8、store/modules/user.js

在此处理登陆后获取的数据,将其存入缓存中;然后跳转页面会自动触发路由守卫

  1. import { setUser, removeStorage, setRoutes, setBtn } from '@/utils/auth'
  2. // 村赤用户令牌和角色信息
  3. const state = {
  4. user: {}, //用户名、token等
  5. }
  6. const mutations = {
  7. SET_USERINFO: (state, userInfo) => {
  8. state.user = userInfo;
  9. }
  10. };
  11. const actions = {
  12. // 用户登录
  13. login({ commit }, userInfo) {
  14. const { data } = userInfo;
  15. return new Promise((resolve, reject) => {
  16. var userInfo = {
  17. accessToken: data.accessToken,
  18. refreshToken: data.refreshToken,
  19. userId: data.userId
  20. }
  21. // 保存状态
  22. commit('SET_USERINFO', userInfo)
  23. // 写入cookie(token等、user,anxinRoles)
  24. setUser(userInfo)
  25. setRoutes(data.menuVoList)
  26. resolve()
  27. })
  28. },
  29. // 获取用户角色信息
  30. setInfo({ commit }, user) {
  31. return new Promise((resolve) => {
  32. commit('SET_USERINFO', user)
  33. resolve(user)
  34. })
  35. },
  36. // 重置令牌
  37. resetToken({ commit }) {
  38. return new Promise(resolve => {
  39. commit('SET_USERINFO', {})
  40. removeStorage('user')
  41. removeStorage('anxinRoutes')
  42. resolve()
  43. })
  44. }
  45. }
  46. export default {
  47. namespaced: true,
  48. state,
  49. mutations,
  50. actions
  51. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/article/detail/58964
推荐阅读
相关标签
  

闽ICP备14008679号