VUE中權(quán)限+動(dòng)態(tài)路由加載+配置,理論與實(shí)踐!

哈嘍大家好,今天我們來探討vue開發(fā)中不可避免的動(dòng)態(tài)路由權(quán)限。尤其是開發(fā)后臺(tái)管理系統(tǒng),其中有很多的角色需要用來做判斷。不同的角色有著不同的權(quán)限。作為管理員還必須擁有修改這些權(quán)限的能力。

這樣的操作無疑會(huì)使得前端做路由的加載時(shí)顯得更為復(fù)雜。我司的角色有包括管理員在內(nèi)總共6種。所以我將自己在開發(fā)過程中遇到的問題整理起來。僅做參考,希望對(duì)大家有幫助!

思路

1.先從最高權(quán)限下手,把項(xiàng)目所有需要做動(dòng)態(tài)分配的路由都保存到后臺(tái)服務(wù)器。(可能會(huì)有人說放在后端就遏制住前端的處理了,但是根據(jù)我們目前的情況,放在后臺(tái)更方便處理)。
2.利用后臺(tái)存放的數(shù)據(jù),做成菜單樹。然后給每一個(gè)想要獲取權(quán)限的角色分配權(quán)限,達(dá)到每一個(gè)角色都擁有自定義權(quán)限。
3.給創(chuàng)建成的不同用戶分配角色,達(dá)到權(quán)限的動(dòng)態(tài)分配。
我司的處理情況特殊,也就是給不同的角色來劃分權(quán)限
4.當(dāng)用戶登錄時(shí),獲取當(dāng)前用戶所在角色組,利用角色返回配置的路由信息,然后將信息加載到vue-router中,實(shí)現(xiàn)不同用戶||角色登錄進(jìn)系統(tǒng)后,顯示不同的路由菜單。

業(yè)務(wù)代碼

1.用戶登錄以后,在cookie存放后臺(tái)返回的Token||sessionID。
login(userInfo).then(res => {
        if (res.code == 600) {
          resolve(res)
          return
        }
        commit('SET_TOKEN', res.res);
        setToken(res.res);
        resolve(res)
      })
2.在路由守衛(wèi)中,我們做判斷。如果有我們要的cookie,那么就讓繼續(xù)加載路由。處理相關(guān)事宜,這塊如有不懂。后期我會(huì)單獨(dú)抽出來做一次vue路由守衛(wèi)的理解。
 // 確定用戶是否已登錄
  const hasToken = store.getters.token;
  if (hasToken) {
    if (to.path === '/login') {
      // 如果已登錄,則重定向到主頁
      next({
        path: '/'
      })
      NProgress.done()
    } else {
      // 確定用戶是否通過getInfo獲得了他的權(quán)限角色
      const hasRoles = store.getters.roles && store.getters.roles.length > 0;
      if (hasRoles) {
        next()
      } else {
        try {
         /*
              主要的操作在第三步
         */
        } catch (error) {
          // 刪除token,進(jìn)入登錄頁面重新登錄
          await store.dispatch('user/resetToken')
          Message.error(error || 'Has Error')
          next(`/login?redirect=${to.path}`)
          NProgress.done()
        }
      }
    }
  } else {
    /* 沒有token */
    if (whiteList.indexOf(to.path) !== -1) {
      // 在免費(fèi)登錄白名單,直接去查看
      next()
    } else {
      // 沒有訪問權(quán)限的其他頁面被重定向到登錄頁面。
      next(`/login?redirect=${to.path}`)
      NProgress.done()
    }
  }
3.當(dāng)路由守衛(wèi)判斷這是第一次進(jìn)入系統(tǒng),沒有角色。所以就要開始獲取角色。利用在vuex中寫的異步方法請(qǐng)求到這一角色信息,以及對(duì)應(yīng)的路由信息。
  getInfo({
    commit,
    state
  }) {
    return new Promise((resolve, reject) => {
      getInfo().then(e => {
        // 因?yàn)檫@里測(cè)試時(shí)只返回了路由信息,我在這里自定義了roles和name.
        var data = {
          roles: ['admin'],
          name: '長得丑活得久,長得帥老的快',
          list: e.res
        }
        const {
          roles,
          name,
        } = data;
        commit('SET_ROLES', roles)
        commit('SET_NAME', name)
        resolve(data)
      })
    })
  }

獲取當(dāng)前角色的信息,并拋出

↓↓↓↓↓ 需要放入第二步空出來的區(qū)域

          // 獲取路由列表
          const {
            list
          } = await store.dispatch('user/getInfo');
          //如果返回的數(shù)據(jù)列表為空,讓重新登錄
          if (list == undefined) {
            await store.dispatch('user/resetToken')
            next(`/${to.path}`)
            NProgress.done()
          } else {
            const accessRoutes = await store.dispatch('permission/generateRoutes', list);
            // 根據(jù)角色生成可訪問路由映射
            // 動(dòng)態(tài)添加可訪問路由
            router.addRoutes(accessRoutes)
            // 設(shè)置replace: true,這樣導(dǎo)航就不會(huì)留下歷史記錄
            next({
              ...to,
              replace: true
            })
          }

此時(shí)拋出的data被獲取到,我們拿到想要的list。通過已經(jīng)定義好的遞歸方法,把我們傳進(jìn)去的list生成vue的路由,并將生成的數(shù)據(jù)返回。我們使用router.addRoutes()方法將路由加載。

4.接第三步路由信息的處理方法。
import {
  constantRoutes
} from '@/router'

import Layout from '@/layout'

/**
 * 加載其他路由
 * @param {Object} route 
 */
function makeOtherRouter(route) {
  var res = [];
  route.children.forEach(i => {
    const item = {
      ...i
    }
    var Unified = item.router.split('/').pop();
    var next = {
      path: Unified,
      component: () => import(`@/views${item.router}`),
      name: Unified,
      meta: {
        title: Unified
      }
    };
    // 是否可見
    if (item.visible == '0') {
      next.hidden = true;
    }
    // 子路由
    if (item.children) {
      next = {};
      next = {
        path: Unified,
        component: () => import(`@/views${item.router}/index`),
        name: Unified,
        meta: {
          title: Unified
        }
      };
      if (item.children.length == 1) {
        next.alwaysShow = true
      }
      next.children = [];
      next.children = makeRouter(item)
    }

    res.push(next)
  })
  return res;
}

//加載第一層路由
export function makeFirstRouter(routes) {
  const res = [];
  routes.forEach(route => {
    const tmp = {
      ...route
    }
    var Unified = tmp.router.split('/').pop();
    if (tmp.icon) {
      var first = {
        path: tmp.router,
        component: Layout,
        name: Unified,
        meta: {
          title: Unified,
          icon: tmp.icon
        }
      }
    }
    // 第一級(jí)路由是否可見
    if (tmp.visible == '0') {
      first.hidden = true;
    }
    if (tmp.children) {
      first.children = [];
      first.children = makeOtherRouter(tmp)
    }
    if (tmp.children.length == 1) {
      first.alwaysShow = true;
    }
    res.push(first)
  })
  return res
}

const state = {
  routes: [],
  addRoutes: []
}

const mutations = {
  SET_ROUTES: (state, routes) => {
    state.addRoutes = routes
    state.routes = constantRoutes.concat(routes)
  }
}

const actions = {
  generateRoutes({
    commit
  }, list) {
    return new Promise(resolve => {
      let accessedRoutes = makeFirstRouter(list);
      //異步路由要把404放在最后加載。
      accessedRoutes.push({
        path: '*',
        redirect: '/404',
        hidden: true
      })
      commit('SET_ROUTES', accessedRoutes)
      resolve(accessedRoutes)
    })
  }
}

export default {
  namespaced: true,
  state,
  mutations,
  actions
}

此時(shí)我們的路由就已經(jīng)處理完成。大致的流程走到這里就結(jié)束了。因?yàn)閿?shù)據(jù)為公司內(nèi)部數(shù)據(jù)在這里就沒有引用真實(shí)的數(shù)據(jù),所以只是用語言組織的。

后面如果找到合適的數(shù)據(jù)會(huì)再次提出來,希望對(duì)正在處理這個(gè)問題的朋友提供一點(diǎn)點(diǎn)思路和幫助。希望支持~

未完待續(xù)!

  • 本人博客地址:https://reinness.com 站點(diǎn)名稱:平凡的你我。 歡迎大家的到來!
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容