vue-element-admin實戰 | 第二篇: 最小改動接入後臺實現根據權限動態加載菜單

一. 前言

本篇基於 有來商城 youlai-mall 微服務項目,經過對vue-element-admin的權限菜單模塊理解個性定製其後臺接口,實現對vue-element-admin工程幾乎不作改動的狀況下,無縫接入後臺接口實現動態權限菜單的加載。html

在進行接下來的工做前,咱們須要對原生的vue-element-admin項目改造,移除mock連通後臺接口,具體可參考我這篇文章 vue-element-admin實戰 | 第一篇: 移除mock接入後臺,搭建有來商城youlai-mall先後端分離管理平臺,若是在過程當中有遇到問題,歡迎下方留言。前端

二. 前端調整

至於上文提到的對vue-element-admin幾乎不作改動即可實現咱們此篇文章的目的是否是我在扯,決定權給各位,我把對vue-element-admin項目改動的地方經過比對工具比對截圖放上來。vue

先聲明vue-element-admin這次改動的地方除了一個獲取權限菜單的接口以外,剩餘的改動全在 src/store/modules/permission.js 文件中。webpack

看到了嗎,能夠說僅對vue-element-admin作兩處改動,再加上對getRoutes調用後臺接口返回的菜單數據的分析,就能夠理解和實現動態權限菜單的加載了。git

改動代碼片斷 + 註釋說明github

import {list as getRoutes} from '@/api/admin/menu'
 import Layout from '@/layout'
   
 generateRoutes({commit}, roles) {
   return new Promise(resolve => {
     // 請求後臺數據替換src/router/index.js的asyncRoutes異步路由
     getRoutes({mode: 3}).then(response => {
       // filterAsyncRoutes方法做權限過濾和數據轉換,roles爲登陸用戶角色ID集合,如:[1,2]
       let accessedRoutes = filterAsyncRoutes(response.data, roles)
       commit('SET_ROUTES', accessedRoutes)
       resolve(accessedRoutes)
     })
   })
 }
 
 // 遞歸權限過濾和數據轉換
 export function filterAsyncRoutes(routes, roles) {
    const res = []
    routes.forEach(route => {
      const tmp = {...route}
      if (hasPermission(roles, tmp)) {
        const component = tmp.component
        if (route.component) {
          if (component == 'Layout') {
            tmp.component = Layout
          } else {
            // 接口組件字符串轉換成組件對象
            tmp.component = (resolve) => require([`@/views/${component}`], resolve)
          }
          if (tmp.children) {
            tmp.children = filterAsyncRoutes(tmp.children, roles)
          }
        }
        res.push(tmp)
      }
    })
    return res
 }

三. 後端接口

1. 接口數據分析

接下來經過後臺接口替換配置在src/router/index.js文件中asyncRoutes異步路由。web

首先分析下異步路由的數據結構:數據庫

經過對上圖異步路由的數據觀察和了解,得出如下幾點:json

  1. path在子路由中前面沒有反斜槓'/'
  2. 根菜單的alwaysShow爲true
  3. component組件需經過import完成編譯時導入,接口只能返回組件路徑字符串,因此這裏在接口請求完成後必須有一個組件路徑字符串到組件對象的轉換過程
  4. meta的roles屬性對應的是有該路由訪問權限角色惟一標識的集合,這裏我使用的是角色ID

2. 接口實現

完成以上的數據分析,接下來就是接口的具體實現了,有關SQL和完整代碼請點擊 有來商城 youlai-mall 拉取便可,如下僅僅貼出關鍵代碼和分析。後端

SysMenuMapper

獲取菜單列表和對應訪問權限的角色ID的集合

SysMenuServiceImpl

將菜單轉換成路由,遞歸生成父子結構樹

SysMenuController

REST對外提供接口

3. 接口測試

使用接口測試工具測試, http://localhost:9999/youlai-admin/menus?mode=3 ,這裏經過網關方式訪問,詳情請知悉有來商城 youlai-mall 項目。

完整返回數據,看看是否是很匹配asyncRoutes異步路由的數據格式了

{
    "code": "00000",
    "data": [{
        "path": "/admin",
        "component": "Layout",
        "alwaysShow": true,
        "name": "系統管理",
        "meta": {
            "title": "系統管理",
            "icon": "documentation",
            "roles": [2, 1]
        },
        "children": [{
            "path": "user",
            "component": "admin/user",
            "alwaysShow": false,
            "name": "用戶管理",
            "meta": {
                "title": "用戶管理",
                "icon": "user",
                "roles": [1]
            }
        }, {
            "path": "role",
            "component": "admin/role",
            "alwaysShow": false,
            "name": "角色管理",
            "meta": {
                "title": "角色管理",
                "icon": "peoples",
                "roles": [2, 1]
            }
        }, {
            "path": "dept",
            "component": "admin/dept",
            "alwaysShow": false,
            "name": "部門管理",
            "meta": {
                "title": "部門管理",
                "icon": "tree",
                "roles": [1, 2]
            }
        }, {
            "path": "menu",
            "component": "admin/menu",
            "alwaysShow": false,
            "name": "菜單管理",
            "meta": {
                "title": "菜單管理",
                "icon": "tree-table",
                "roles": [1, 2]
            }
        }, {
            "path": "dict",
            "component": "admin/dict",
            "alwaysShow": false,
            "name": "字典管理",
            "meta": {
                "title": "字典管理",
                "icon": "education",
                "roles": [1, 2]
            }
        }]
    }],
    "msg": "一切ok"
}

最後要作的就是將組件(component)路徑字符串轉換成組件對象便可,再次貼出在上文permission.js改造的代碼:

if (component == 'Layout') {
    tmp.component = Layout
  } else {
    // 接口組件字符串轉換成組件對象
    tmp.component = (resolve) => require([`@/views/${component}`], resolve)
  }

有一點須要注意的是上面組件動態導入不能使用import,以下:

tmp.component = () => import(`@/views/${component}`)

否則會報如下錯誤

Error: Cannot find module '@/views/***'

緣由是webpack不支持import動態導入了,可是以前確實是能夠的,因此有些讓人想不通,這裏使用require動態導入組件便可。

4. 菜單權限測試

參看當前用戶擁有的角色ID爲2

用戶管理要求只有角色ID爲1纔能有其訪問權限

測試下用戶登陸控制檯可否看到「用戶管理」菜單

怎麼樣,只擁有角色ID爲2的角色是看不到須要角色ID爲1才能訪問的「用戶管理」菜單的,也就證實了咱們成功經過接入後臺接口實現了權限菜單的動態加載的目的。

四. 結語

其實有個問題值得去思考下的。爲何咱們在從後臺獲取所有菜單列表的時候須要關聯查詢出有對應訪問權限的角色ID的集合,而不是說動態傳入當前登陸用戶的信息去查詢該用戶擁有的菜單集合呢?這實際上是兩種實現方式,區別在於第一種每一個用戶都要獲取所有菜單數據,而第二種方式針對每一個用戶加載其擁有權限的菜單數據。單看的話第二種方式確定優於第一種,但這裏咱們選用的是第一種加載所有菜單數據。若是你結合緩存去看的話天然就想的通了,全部用戶共享緩存的同一份菜單數據,仍是說緩存針對每一個用戶存放一份菜單緩存又或者用戶每次都去請求數據庫。後面計劃把菜單數據存放到緩存Redis中而後作一個分析比較。

附完整代碼:

有來商城後端:youlai-mall

有來商城管理前端:youlai-mall-admin-web

寫了這麼多其實也是想給本身的項目打個廣告,更但願對你們有所幫助,對於技術人來講少走彎路真的很重要,喜歡的朋友給個star,對我來講這份幫助更是本身繼續下去的動力,因此謝謝了。有啥問題下方留言,或直接聯繫我(微信號:haoxianrui)。

相關文章
相關標籤/搜索