vue如何根据权限生成动态路由、导航栏

 更新时间:2022年03月30日 11:31:47   作者:秋刀鱼笛滋味  
这篇文章主要介绍了vue如何根据权限生成动态路由、导航栏,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

基本思路

1、创建vueRouter,用公共路由实例化

2、创建需要根据权限筛选的路由对象(在路由对象,添加必要的权限判断字段)

3、登录完成,由后端配合返回当前用户的权限集合

4、筛选出有权限的路由对象,利用vueRouter的addRoutes方法,生成完整路由

5、处理刷新页面导致vueRouter重新实例化导致路由对象不完善 (利用router.beforeEach导航守卫,,利用addRoutes()完善 路由对象 )

6、侧边导航栏相关代码

相关代码

根据上面的顺序

1、如下

Vue.use(Router)
// 公共路由
export const publicRoutes = [
  {
    path: '/',
    name: 'login',
    component: login,
    meta: {
      title: '登录'
    }
  }
]
// 需要根据权限筛选的路由
export const asyncRoutes = [
  ...Home,
  ...Seting,
  ...CRM,
  {
    path: '*',
    component: login,
    meta: {
      hidden: true
    },
    redirect: '/'
  }
]
const vr = new Router({
  mode: 'history',
  routes: publicRoutes
})

2、以seting模块为例,role为判断是的权限字段,角色有这个字段对应的值就有当前页面的权限

import Layout from '@/views/layout/layout.vue'
const account = r => require.ensure([], () => r(require('@/views/seting/account/account.vue')), 'seting');
const logs = r => require.ensure([], () => r(require('@/views/seting/logs/logs.vue')), 'seting');
const role = r => require.ensure([], () => r(require('@/views/seting/role/role.vue')), 'seting');
const Seting = [
  {
    path: '/seting',
    component: Layout,
    redirect: '/seting/account',
    meta: {
      title: '系统设置',
      role: '123c6c6514d416472e640bc3f49297c550',
      icon: 'icon-xitong'
    },
    children: [
      {
        path: 'account',
        name: 'account',
        component: account,
        meta: {
          title: '账号管理',
          role: '1325cdeb897cc7f8e951d647de9b1d8e11',
        }
      },
      {
        path: 'logs',
        name: 'logs',
        component: logs,
        meta: {
          title: '日志管理',
          role: '14bfbb0337ad3e7e2c9fc101294c3fe645',
        }
      },
      {
        path: 'role',
        name: 'role',
        component: role,
        meta: {
          title: '角色管理',
          role: '1559d1c05d15a0dce5549b8bf5a58c0cf9',
        }
      }
    ]
  }
]
export default Seting

如果有一些详情页不需要在导航列表展示还可以添加字段,在生成导航栏时去掉

eg:

 {
            path: 'addJoiner',
            name: 'addJoiner',
            component: addJoiner,
            meta: {
              hidden: true,  // 隐藏字段
              title: '***详情页',
              role: '14bfbb0337ad3e7e2c9fc101294c3fe645',
            }
          },

3、登录获取权限集合和基本信息

 // 登录
    login () {
      this.rememberPassword()
      this.$refs.form.validate((valid) => {
        if (valid) {
          this.loading = true
          let params = {
            login_name: this.form.user,
            password: this.form.password,
            code: this.form.yanzhenma,
          }
          this.$api.post('api/user/login', params).then(res => {
            if (res.err_code === 1) {
              // 把用户的基本信息储存在vuex,中
              this.$store.dispatch('setBaseInfo', res.data).then(() => {
                // 获取有权限的路由表,添加到路由
                router.addRoutes(this.$store.getters.addRouters)
                this.$router.push({ name: 'home' })
              })
            }
            this.loading = false
          })
        }
      })

部分vuex代码

如果不太理解,点击下面链接:

vuex刷新就没了解决方案

秒懂vuex

actions.js

// import api from '@/api'
const actions = {
  setBaseInfo ({
    commit
  }, data) {
    return new Promise(resolve => {
      commit('set_userInfo', data.userInfo)
      commit('set_token', data.token)
      commit('set_roles', data.menus)
      // 把基本信息保存在本地防止刷新之后丢失
      sessionStorage.setItem('baseInfo', JSON.stringify(data))
      resolve()
    })
  }
}
export default actions

mutations.js

const setStorage = (key, value) => {
  if (typeof (value) === 'object') {
    value = JSON.stringify(value)
  }
  sessionStorage.setItem(key, value)
}
/*
* 避免刷新之后vuex被重置,在sessionStorage做一个备份
 */
const mutations = {
  set_userInfo (state, payload) {
    state.userInfo = payload
    setStorage('userInfo', payload)
  },
  set_token (state, payload) {
    state.token = payload
    setStorage('token', payload)
  },
  set_roles (state, payload) {
    state.roles = payload
    setStorage('roles', payload)
  },
  set_breadcrumb (state, payload) {
    state.breadcrumb = payload
    setStorage('breadcrumb', payload)/*  */
  },
  changeCollapsed (state, payload) {
    state.isCollapsed = payload
  }
}
export default mutations

getters.js

import createdRoutes from '@/utils/createdRoutes.js'
import { asyncRoutes } from '@/router/index.js'
let getStoryage = (item) => {
  let str = sessionStorage.getItem(item)
  return JSON.parse(str)
}
const getters = {
  get_userInfo: (state) => {
    return state.userInfo ? state.userInfo : getStoryage('userInfo')
  },
  get_token: (state) => {
    return state.token ? state.token : sessionStorage.getItem('token')
  },
  get_roles: (state) => {
    return state.roles.length ? state.roles : getStoryage('roles')
  },
  addRouters: (state, getters) => {
    let routes = createdRoutes(asyncRoutes, getters.get_roles)
    return routes
  },
  get_breadcrumb: (state, getters) => {
    return state.breadcrumb.length ? state.breadcrumb : getStoryage('getStoryage')
  }
}
export default getters;

4、核心的筛选需要权限的路由方法:createdRoutes()

也就是3的getters,用到的方法,毫无保留奉上

/**
 * 判单当前的路由对象是否在登录人的权限之内
 * @param {Array} roles 权限
 * @param {Object} route 路由
 */
function hasPermission (roles, route) {
  if (route.meta && route.meta.role) { // 路由需要权限就要在权限数组里面判断
    return roles.includes(route.meta.role)
  } else { // 不需要权限就直接通过
    return true
  }
}
/**
 * 根据接口获取的权限列表动态生成当前用户的侧边导航栏,返回通过权限验证的路由数组
 * @param {Array} asyncRoutes 需要过滤的路由
 * @param {Array} roles 权限
 */
function createdRoutes (asyncRoutes, roles) {
  const accessedRouters = asyncRoutes.filter(route => {
    if (hasPermission(roles, route)) { // 当前路由通过权限验证直接通过
      if (route.children && route.children.length) { // 当前路由有子路由,就递归验证
        route.children = createdRoutes(route.children, roles)
      }
      return true
    }
    return false
  })
  return accessedRouters
}
export default createdRoutes

5、处理刷新带来的问题

其实这里的代码是连接1的,注意注释

// 全局的导航守卫
vr.beforeEach((to, from, next) => {
  // 刷新页面之后导致vue-router和vuex重置,路由丢失,利用的就是刷新后vuex的state被重置判断
  if (to.name !== 'login' && !store.state.token) {
    // 避免直接不登陆进页面
    if (!sessionStorage.getItem('token')) {
      location.href = '/'
      return
    }
    let data = JSON.parse(sessionStorage.getItem('baseInfo'))
    store.dispatch('setBaseInfo', data).then(() => {
      vr.addRoutes(store.getters.addRouters)
    })
  }
  // 设置面包屑导航
  let breadcrumb = to.matched.filter(item => item.meta.title)
  if (breadcrumb.length) {
    breadcrumb = breadcrumb.map(item => item.meta.title)
    store.commit('set_breadcrumb', breadcrumb)
  }
  // 设置title
  document.title = to.meta.title
  next()
})

6、侧边导航栏的完整代码

还是遍历的根据权限生成的路由表,干掉一些需要隐藏的详情页之类, 

这里的代码和过滤l路由的核心函数,要根据自己的业务做相应的处理

element-ui的menu

<template>
  <el-menu class="el-menu-vertical-demo" :collapse="isCollapsed" background-color="#545c64" :default-active='activeIndex' text-color="#fff" active-text-color="#7EA8F5">
    <section v-for="(item,index) in addRouters" :key="item.name" :class="isCollapsed ? 'collapsed':''">
      <!-- 有子菜单 -->
      <el-submenu :index=" `${index+1}`" v-if="!item.meta.hidden && item.children && item.children.length">
        <template slot="title">
          <i :class="`icon iconfont ${item.meta.icon}`"></i>
          <span slot="title">{{item.meta.title}}</span>
        </template>
        <section v-for="(item2,index2) in item.children" :key="item2.name">
          <!-- 二级菜单有子菜单 -->
          <el-submenu :index="`${index+1}-${index2+1}`" v-if="item2.children && item2.children.length" class="sub2">
            <template slot="title">
              <span slot="title">{{item2.meta.title}}</span>
            </template>
            <!-- 三级菜单 -->
            <el-menu-item v-for="(item3,index3) in item2.children" v-if="!item3.meta.hidden" :index="item3.name" :key="index3" @click.native="$router.push({name:item3.name})">
              <span slot="title">{{item3.meta.title}}</span>
            </el-menu-item>
          </el-submenu>
          <!-- 二级菜单无子菜单 -->
          <!-- 不是隐藏的,详情页隐藏 -->
          <el-menu-item :index="item2.name" v-else-if="!item2.meta.hidden" @click.native="$router.push({name:item2.name})">
            <span slot="title">{{item2.meta.title}}</span>
          </el-menu-item>
        </section>
      </el-submenu>
      <!-- 无子菜单 -->
      <el-menu-item v-else-if="item.meta.hidden && item.children && item.children.length" :index="item.children[0].name" @click.native="$router.push({name:item.children[0].name})" class="item">
        <i :class="`iconfont ${item.children[0].meta.icon}`"></i>
        <span slot="title">{{item.children[0].meta.title}}</span>
      </el-menu-item>
    </section>
  </el-menu>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
  props: {
    isCollapsed: {
      type: Boolean,
      default: false
    }
  },
  computed: {
    ...mapGetters(['addRouters']),
    activeIndex () { //集火的菜单
      return this.$route.name
    }
  }
}
</script>
<style lang="scss" scoped>
section {
  /deep/ .el-submenu__title {
    .icon {
      margin-right: 10px;
    }
    i {
      color: white;
      font-size: 14px;
    }
  }
  /deep/ .el-menu-item {
    padding-left: 50px !important;
  }
  /deep/ .el-menu-item.item {
    padding-left: 19px !important;
    i {
      color: white;
      font-size: 14px;
      margin-right: 12px;
    }
  }
  /deep/ .el-submenu .el-menu-item {
    min-width: 0;
  }
  /deep/ .el-submenu.sub2 .el-submenu__title {
    padding-left: 50px !important;
    i {
      margin-right: 0px;
    }
  }
  /*   /deep/ .el-submenu.sub2 .el-menu-item {
    text-indent: 12px;
  } */
}
.collapsed {
  width: 50px;
  /deep/ .el-submenu__title {
    .el-icon-arrow-right {
      display: none;
    }
    span[slot="title"] {
      display: none;
    }
  }
}
</style>

路由图

在这里插入图片描述

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。 

相关文章

  • Vue3 (五)集成HTTP库axios详情

    Vue3 (五)集成HTTP库axios详情

    这篇文章主要讲解Vue3 集成HTTP库axios的相关内容,本文讲围绕如何利用Vue3 集成HTTP库axios的相关资料展开文章,需要的朋友可以参考一下,希望对你有所帮助
    2021-10-10
  • springboot vue接口测试前端模块树和接口列表

    springboot vue接口测试前端模块树和接口列表

    这篇文章主要为大家介绍了springboot vue接口测试前端模块树和接口列表,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-05-05
  • Vue vm.$attrs使用场景详解

    Vue vm.$attrs使用场景详解

    这篇文章主要介绍了vm.$attrs使用场景详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-03-03
  • 基于vue封装一个带眼睛的密码子组件

    基于vue封装一个带眼睛的密码子组件

    这篇文章给大家介绍了基于vue封装一个带眼睛的密码子组件的方法,文章中有详细的代码讲解,对大家的学习或工作有一定的帮助,需要的朋友可以参考下
    2023-09-09
  • Vue中的作用域CSS和CSS模块的区别

    Vue中的作用域CSS和CSS模块的区别

    这篇文章主要介绍了Vue中的作用域CSS和CSS模块的区别,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
    2018-10-10
  • vue实现锚点定位功能

    vue实现锚点定位功能

    这篇文章主要为大家详细介绍了vue实现锚点定位功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-06-06
  • 用v-html解决Vue.js渲染中html标签不被解析的问题

    用v-html解决Vue.js渲染中html标签不被解析的问题

    这篇文章主要给大家介绍了如何利用v-html解决Vue.js渲染过程中html标签不能被解析,html标签显示为字符串的问题,文中通过图文介绍的很详细,有需要的朋友们可以参考借鉴,下面来一起看看吧。
    2016-12-12
  • ElementUI中el-dropdown-item点击事件无效问题

    ElementUI中el-dropdown-item点击事件无效问题

    这篇文章主要介绍了ElementUI中el-dropdown-item点击事件无效问题及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-04-04
  • elementui的默认样式修改方法

    elementui的默认样式修改方法

    下面小编就为大家分享一篇elementui的默认样式修改方法,具有很好的参考价值,希望对大家有所帮助,一起跟随小编过来看看吧
    2018-02-02
  • vue使用Highcharts实现3D饼图

    vue使用Highcharts实现3D饼图

    这篇文章主要为大家详细介绍了vue使用Highcharts实现3D饼图,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-03-03

最新评论