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對象中,所以我們可以很方便的在每個頁面回去到當前用戶在當前頁面所擁有的權限
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章