• 技术文章 >web前端 >js教程

    vue addRoutes实现动态权限路由菜单步骤详解

    php中世界最好的语言php中世界最好的语言2018-05-21 13:52:48原创12165
    这次给大家带来vue addRoutes实现动态权限路由菜单步骤详解,vue addRoutes实现动态权限路由菜单的注意事项有哪些,下面就是实战案例,一起来看一下。

    需求

    最近接手一个后台管理系统,需要实现导航菜单从后台拉取的效果;根据登录用户的权限不同分别拉出来的导航菜单也不一样,另外可操作的界面也存在区别。

    问题

    因为后台管理系统是准备使用vue+vue-router+element-ui+vuex的搭配来做的,可是单页应用在进入页面之前就已经将vue-router实例化并且注入vue实例中了,所以在进入登录页面的时候旧没办法在重新定制路由了。接下来各种百之谷之,发现vue-router在2.0版本中提供了addRoutes方法添加路由,希望的曙光出现。

    经过一番折腾终于实现了功能,记录下来便于回顾,也希望能帮助到同样有需求的同志。

    思路

    1、首先在本地配置好固定不变的路由地址,例如登录,404这些页面,如下:

    import Vue from 'vue'
    import Router from 'vue-router'
    import store from '@/vuex/store'
    Vue.use(Router)
    let router = new Router({
     routes: [
      {
       path: '/login',
       name: 'login',
       meta: {requireAuth: false},
       // 模块使用异步加载
       component: (resolve) => require(['../components/login/login.vue'], resolve)
      }]
    })
    // 拦截登录,token验证
    router.beforeEach((to, from, next) => {
     if (to.meta.requireAuth === undefined) {
      if (store.state.token) {
       next()
      } else {
       next({
        path: '/login'
       })
      }
     } else {
      next()
     }
    })
    export default router

    配置好这些固定的路由后我们才能够到登录页面,不然是无法继续下去的。

    2、然后重要的一步,我们需要跟后端老铁约定好需要返回的权限菜单列表信息;首先这里我们先分析一下自己需要的路由结构,这里以我自己的路由作为例子。如果是我自己直接定义路由的话,会是以下结构:

    let router = new Router({
     routes: [
      {
       path: '/login',
       name: 'login',
       meta: {requireAuth: false},
       component: (resolve) => require(['../components/login/login.vue'], resolve)
      },
      {
        path: '/',
        redirect: '/layout'
      },
      {
        path: '/layout',
        component: (resolve) => require(['../layout.vue'], resolve),
        children: [
          {
            path: 'index', 
            meta: {
              type: '1',    //控制是否显示隐藏 1显示,2隐藏
              code: 00010001, // 后面需要控制路由高亮
              title: '首页',  // 菜单名称
              permissonList: [] // 权限列表
            }
            component: (resolve) => require(['@/components/index/index.vue'], resolve)
          },
          {
          ...
          }   
        ]
      }]
    })

    根据以上结构分析,其实真正需要动态配置的路由其实是/layout下面的children部分,所以需要后端返回给我们包含所有路由的一个数组就可以了

    返回的数据中rootList中是一级导航的列表,一级导航实际是没有路由功能,只是作为切换二级菜单的触发器,subList才是我们真正需要的路由信息。

    3、拿到权限路由信息后,需要我们在本地对数据进行处理组装成我们需要的数据:

    // 登录
       login () {
        let params = {
         account: this.loginForm.username,
         password: encrypt(this.loginForm.password)
        }
        this.loading = true
        this.$http.post(this.$bumng + '/login', this.$HP(params))
         .then((res) => {
          this.loging = false
          console.info('菜单列表:', res)
          if (res.resultCode === this.$state_ok) {
           // 合并一级菜单和二级菜单,便于显示
           let menus = handleMenu.mergeSubInRoot(res.rootList, res.subList)
           // 本地化处理好的菜单列表
           this.saveRes({label: 'menuList', value: menus})
           // 根据subList处理路由
           let routes = handleMenu.mergeRoutes(res.subList)
           // 本地化subList,便于在刷新页面的时候重新配置路由
           this.saveRes({label: 'subList', value: res.subList})
           // 防止重复配置相同路由
           if (this.$router.options.routes.length <= 1) {
            this.$router.addRoutes(routes)
            // this.$router不是响应式的,所以手动将路由元注入路由对象
            this.$router.options.routes.push(routes)
           }
           this.$router.replace('/layout/index')
          }
         })
         .catch((err) => {
          this.loging = false
          console.error('错误:', err)
         })
       },

    处理菜单列表和subList的方法:mergeSubInRoot 和 mergeRoutes

    const routes = [
     {
      path: '/',
      redirect: '/layout'
     },
     {
      path: '/layout',
      component: (resolve) => require(['../layout.vue'], resolve),
      children: []
     }
    ]
    export default {
     /**
      * 合并主菜单和子菜单
      * @param: rootList [Array] 主菜单列表
      * @param: subList [Array] 子菜单
      * */
     mergeSubInRoot (roots, subs) {
      if (roots && subs) {
       for (let i = 0; i < roots.length; i++) {
        let rootCode = roots[i].code
        roots[i].children = []
        for (let j = 0; j < subs.length; j++) {
         if (rootCode === subs[j].code.substring(0, 4)) {
          roots[i].children.push(subs[j])
         }
        }
       }
      }
      return roots
     },
     /**
      * 合并远程路由到本地路由
      * @param: subList [Array] 远程路由列表
      * @param: routes [Array] 本地路由列表
      * */
     mergeRoutes (subs) {
      if (subs) {
       for (let i = 0; i < subs.length; i++) {
        let temp = {
         path: subs[i].actUrl,
         name: subs[i].actUrl,
         component: (resolve) => require([`@/components/${subs[i].component}.vue`], resolve),
         meta: {
          type: subs[i].type,
          code: subs[i].code,
          title: subs[i].name,
          permissionList: subs[i].permissionList
         }
        }
        routes[1].children.push(temp)
       }
      }
      return routes
     }
    }

    至此我们已经将权限路由成功配置进本地路由了,我的系统登录进入如下

    后续优化

    1、菜单列表的显示以及二级导航切换:

    <template>
      <p class="mainMenu">
       <el-menu
        class="menubar"
        mode="horizontal"
        :default-active="activeCode"
        background-color="#545c64"
        text-color="#fff"
        active-text-color="#ffd04b">
        <el-menu-item :index="item.code | splitCode" v-for="item in menuList" :key="item.code" @click="switchSubMenu(item)" v-if="item.code !== '0008'">
         <i :class="`iconfont icon-${item.imgUrl}`"></i>
         <span slot="title">{{item.name}}</span>
        </el-menu-item>
       </el-menu>
      </p>
    </template>
    <script type="text/ecmascript-6">
     import {mapState, mapMutations} from 'vuex'
     export default {
      name: 'menu',
      data () {
       return {
        msg: 'Welcome to Your Vue.js App'
       }
      },
      computed: {
       ...mapState(['menuList']),
       activeCode () {
         // 通过code保证在切换字路由的情况下一级路由也是高亮显示
        return this.$route.meta.code.substring(0, 4)
       }
      },
      methods: {
       ...mapMutations(['saveRes']),
       // 切换二级路由
       switchSubMenu (route) {
        console.info('路由:', route)
        if (route.actUrl !== 'index') {
         // 用currentSubMenu控制二级路由数据 
         this.saveRes({label: 'currentSubMenu', value: route.children})
         this.$router.push(`/layout/${route.children[0].actUrl}`)
        } else {
         // 不存在二级路由隐藏二级 
         this.saveRes({label: 'currentSubMenu', value: ''})
         this.$router.push(`/layout/${route.actUrl}`)
        }
       }
      },
      filters: {
       splitCode (code) {
        return code.substring(0, 4)
       }
      }
     }
    </script>

    2、防止刷新路由丢失;由于在刷新的时候单页应用会重新初始化,这时候所有配置的路由都会丢失,一朝回到解放前,只有本地配置的路由能够跳转。这时候我们可以在app.vue(ps:不论在哪里进行刷新,app.vue都会执行)中执行如下代码:

    <script>
     import {decrypt} from '@/libs/AES'
     import handleMenu from '@/router/handleMenu'
     export default {
      name: 'app',
      created () {
       // 当this.$router.options.routes的长度为1,且本地缓存存在菜单列表的时候才重新配置路由
       if (this.$router.options.routes.length <= 1 && sessionStorage.getItem('subList')) {
        let subList = JSON.parse(decrypt(sessionStorage.getItem('subList')))
        let routes = handleMenu.mergeRoutes(subList)
        this.$router.addRoutes(routes)
        // this.$router不是响应式的,所以手动将路由元注入路由对象
        this.$router.options.routes.push(routes)
       }
      }
     }
    </script>

    这样即使刷新,也会重新配置路由了。

    3、关于页面按钮级别控制,可以自定义一个指令,去做这件事情。因为我们已经权限列表放入了相应路由的meta对象中,所以我们可以很方便的在每个页面回去到当前用户在当前页面所拥有的权限

    参考官方文档自定义指令

    结语

    打完收工,得亏vue-router2中添加了addRoutes的方法,不然

    相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!

    推荐阅读:

    php生成自定义长度随机字符串步骤详解

    php图片裁剪与缩略图使用实例讲解

    以上就是vue addRoutes实现动态权限路由菜单步骤详解的详细内容,更多请关注php中文网其它相关文章!

    声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。
    专题推荐:addRoutes 路由 权限
    上一篇:实现JavaScript的组成(BOM和DOM详细解读) 下一篇:AngularJS中$http服务使用方法详解
    Web大前端开发直播班

    相关文章推荐

    • 你能搞懂JS的this指向问题吗?看看这篇文章• JavaScript经典基础详解之函数• 值得了解的几个实用JavaScript优化小技巧• JavaScript学习理解之JSON(总结分享)• es6新增数组方法是什么

    全部评论我要评论

  • kaiking

    您好,这是我的写法,不知道为何,就是没法动态引进,我觉得是应该是下面这行代码的问题: oMenu.component = (resolve) => require([`@/views/${componentPath}`], resolve)

    2020-07-02

  • 取消发布评论发送
  • 1/1

    PHP中文网