1. 程式人生 > 前端設計 >從零開始配置vue許可權控制

從零開始配置vue許可權控制

許可權控制方式

路由完全由後端返回

確認控制邏輯

約定後端返回資料格式

children: [ // 子路由放入children屬性中
    {
        children: []
        component: "views/index/content/index"
        id: 5
        meta: {isLogin: 1,icon: "el-icon-postcard",title: "專案概覽",isShow: 1}
        name: "/home/index/content"
        path: "/home/index/content"
    }
]
component: "views/index"
// 對應頁面的檔案地址 id: 3 // 唯一屬性 // isLogin判斷是否需要登入訪問,isShow判斷是否顯示在導航欄上,title展示導航欄名字 meta: {isLogin: 1,icon: "el-icon-date",title: "總體概況",isShow: 1} name: "index" // 元件名 path: "index" // 路由地址 複製程式碼

vue頁面配置

1. APP.vue 配置

<template>
  <div id="app">
    <router-view />
  </div>
</template>

複製程式碼

2.首頁設定

根據父子路由的配置,子路由必須要在有rout-view情況下才能切換,所以需要在首頁設定導航欄,還有導航欄路由對應的頁面

類似這種佈局

<template>
    <div class="main">
        <div class="main-left">
            <!-- 放置導航欄選單元件 -->
        </div>
        <div class="main-right">
            
            <!-- 右邊內容 -->
            <div class="main-right-container"
> <router-view></router-view> </div> </div> </div> </template> 複製程式碼

登入頁設定

引入vuex資料

computed: {
    ...mapGetters([
      'menuList'
    ])
},methods: {
    ...mapActions([
      'getMenuList',]),}
複製程式碼
 // 登入後觸發的函式
 login() {
     let url='/你的api地址';
     let reqData={};
    
     let res = await Http.axiosPost(url,reqData); // 這裡使用axios,晚點出一篇axios配置到原型鏈的文章
     if (res.code === 200) {
        // 存登入狀態
        localStorage.setItem('Flag','isLogin'); 
        // 存登入token
        localStorage.setItem('TOKEN',res.obj);
        
        // 呼叫mapGetters,檢視vuex中是否有路由資料
         let code = await this.getMenuList();
        if(code === 200){ // 存在路由
          // 跳轉導航欄首頁
          let Path= this.menuList[0].children[0].children[0].path
          
          this.$router.push({path: Path})
        }else{ // 不存在路由,說明配置問題了
          console.log('路由獲取失敗',code)
          MessageBox({
            title: '警告',message: '導航配置問題,請聯絡管理員',callback: action => { // 回撥函式
              localStorage.clear(); // 清除快取資料
              next({path: '/login',replace: true }) // 跳轉登入
            }
          })
        }
     } 
 }

複製程式碼

配置vuex

import Vue from 'vue'
import Vuex from 'vuex'
import { normalRouter } from '../router'
import createLogger from 'vuex/dist/logger'

Vue.use(Vuex)

// 設定只有在開發環境的時候才打印日誌
const debug = process.env.NODE_ENV !== 'production'

const state = {
    menuList: '1'
}
const getters = {
    menuList: state => state.menuList
}
const mutations = {
   setMenuList (state,value) {
        // 存入資料
        state.menuList = menus;
        
        // 加入登入頁面等一級路由
        menus = menus.concat(normalRouter)
    
        // matcher是用來解決 Duplicate named routes definition 報錯解決路由重複
        VueRouter.matcher = new Router().matcher
        // 錯誤頁面要最後新增,不然訪問動態的路由會先出現404
        menus.push({
           path: '*',// 頁面不存在的情況下會跳到404頁面
           redirect: '/error',})
        VueRouter.addRoutes(menus)
   }
}
const actions = {
  // 獲取動態路由
  async getMenuList({commit}) {
    // 通過介面獲取路由資料
    let url='/你的api地址';
    let reqData={};
    
    let res = await Http.axiosPost(url,reqData); // 這裡使用axios,晚點出一篇axios配置到原型鏈的文章
    if(res.code === 200){ // 成功
      // res.obj[0].component = "views/homepage"
      let menus = needRoutes(res.obj) // 生成路由資訊
      
      // 確認路由層級,一級路由只有一個,往下的頁面都是子路由
      commit('setMenuList',[{
        path: "/home",component:(resolve) =>{
          require(['@/views/homepage'],resolve)
        },meta: { isLogin: true // 新增該欄位,表示進入這個路由是需要登入的
        },children:menus
      }])
      return res.code
    } else { // 失敗
      return res.code
    }
  }
}

// 生成路由資料
let needRoutes = (data) => {
  // 判斷是否是陣列
  if (!Array.isArray(data)) {
    return new TypeError('arr must be an array.');
  }
  let arr = formatComponent(data)
  
  return arr;
}

// 遞迴函式,用來對元件進行非同步渲染
let formatComponent = (data) => {
  for (let obj of data) {
    const component = obj.component
    // 把後臺返回的路由引數,拼接路徑
    obj.component = resolve => { require(['@/' + component],resolve) }
    if (obj.children.length > 0) { // children有長度,說明有子路由
      formatComponent(obj.children)
    }
  }
  return data
}

const store = new Vuex.Store({
    state,getters,mutations,actions,strict: debug,plugins: debug ? [createLogger()] : []
})
export default store
複製程式碼

在router頁面構建導航守衛

1. 確認預設路由

// 配置一級路由配置和重定向
// login頁面和error頁面是不需要登入判斷的,可以放在預設路由內
export const normalRouter = [
  {
    path: "/",redirect: '/login',// meta: {
    //   isLogin: false,// 新增該欄位,表示進入這個路由是需要登入的
    //   title:'登入'
    // }
  },{
    path: "/login",name: "Login",// components:require('./views/login'),component: resolve => require (['@/views/login'],resolve),meta: {
      isLogin: false,// 新增該欄位,表示進入這個路由是需要登入的
      title:'登入'
    }
  },{
    path: "/home",redirect: '/home/index',},{
    path: "/error",name: "error",component: error,// 新增該欄位,表示進入這個路由是需要登入的
      title:'錯誤'
    }
  },]
複製程式碼

2. 匯入路由中

let router = new Router({
  mode: "history",base: process.env.BASE_URL,routes:normalRouter
});
複製程式碼

3. 配置全域性路由導航守衛

// 因為用到了element ui,所以要引入一下
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI)

router.beforeEach( async (to,from,next) => {
  console.log('before路由',to);
  // 獲取快取的登入狀態
  let hasLogin = localStorage.getItem("Flag")
  
  // 有登入狀態
  if (hasLogin) {
    console.log('state',store.state.menuList.length,to,from)
    // 判斷vuex中是否有存入路由資料
    if (store.state.menuList.length === 0) {
      
      // 進入到這一步使用者已經登入過,但是又重新整理了瀏覽器,導致路由清空了,所以要在vuex中重新請求路由
      let res = await store.dispatch('getMenuList')
      // code 不為200 時候,說明路由介面報錯,需要重新登入
      if (res !== 200) {
        // element 提示框
        MessageBox({
          title: '警告',callback: action => { // 回撥函式
            // 清除登入快取資料 
            localStorage.clear();
            // 跳轉登入頁
            next({path: '/login',replace: true })
          }
        })
      } else { // 有路由資料
        // router.addRoutes是非同步的,所以把全域性的跳轉 *也動態添加了,同時使用 next({ ...to,replace: true })重新載入
        next({...to,replace: true })
      }
    }
    
    if (to.name === 'Login') { // 已經登入過,但是訪問的是login,強制跳轉至首頁
      // 這裡設定的是vuex中首個導航欄的path地址
      next({
        path: store.state.menuList[0].children[0].children[0].path,})
    } else { // 正常跳轉
      next()
    }
  } else { // 沒有登入,則強制跳轉登入頁
    console.log('to',to)
    // 沒有登入想訪問其他頁面,跳轉至
    if (to.name !== 'Login') {
      next({
        path: '/login',})
    } else {
      next()
    }
  }
})
複製程式碼

參考資料

vue許可權路由實現方式總結

關於addRoutes、axios動態新增路由