vue addRoutes实现动态权限路由菜单的示例

重点:addRoutes方法添加路由

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

import Vue from 'vue'
import Router from 'vue-router'
// import HelloWorld from '@/components/HelloWorld'
import store from '@/vuex/store'

Vue.use(Router)

// export default new Router({
let router =  new Router({
    routes: [
        {
            path: '/',
            name: 'HelloWorld',
            component: HelloWorld
        },
        {
            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) {//  如果存在token
            next()
        } else {
            next({
                path: '/login'
            })
        }
    } else { 
        next()
    }
})

    export default router

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

然后重要的一步,我们需要跟后端约定好需要返回的权限菜单列表信息;首先这里我们先分析一下自己需要的路由结构

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

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

在这里插入图片描述

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

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

在这里插入代码片

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

import Vue from 'vue'
import Router from 'vue-router'
import store from '@/vuex/store'

Vue.use(Router)

// export default new Router({
let router =  new Router({
    routes: [
        {
            path: '/login',
            name: 'login',
            meta: { requireAuth: false },
            //   模块使用异步加载
            component: (resolve) => require(['@/components/login/login.vue'], resolve)
        },
        {
            path: '/layout',
            component: (resolve) => require('@/components/login/layout.vue', resolve),
            children: [],
        }
  ]
})

// 拦截登录,token验证
router.beforeEach((to, from, next) => {
    if (to.meta.requireAuth === undefined) {//  如果跳转页面需要权限 
        if (store.state.token) {//  如果存在token
            next()
        } else {
            next({
                path: '/login'
            })
        }
    } else { 
        next()
    }
})

export default {
    /* 合并主菜单和子菜单 */
    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
    },
    /* 合并远程路由到本地路由 */
    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}`], resolve),
                    meta: {
                        type: subs[i].vue,
                        code: subs[i].code,
                        title: subs[i].name,
                        permissionList : subs.permissionList
                    }
                }
                routers[1].children.push(temp)
            }
        }
        return routers
    }, 
}

至此我们已经将权限路由成功配置进本地路由

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

<template>
  <div class="mainMenu">
      <el-menu class="menubar"
        mode = "horizontal"
        :default-active="activeCode"
        background-color="#ffd04b"
        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)">
            <template v-if="item.code !== '0008'">
                <i :class="`iconfont icon-${item.imgUrl}`">
                    <span slot="title">{{item.name}}</span>
                </i>
            </template>
        </el-menu-item>
      </el-menu>
  </div>
</template>

<script>
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>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1, h2 {
  font-weight: normal;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
</style>

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对象中,所以我们可以很方便的在每个页面回去到当前用户在当前页面所拥有的权限
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值