最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

如何優(yōu)雅地在vue中添加權(quán)限控制示例詳解

 更新時(shí)間:2019年03月07日 11:49:11   作者:邪瓶張起靈  
這篇文章主要給大家介紹了關(guān)于如何優(yōu)雅地在vue中添加權(quán)限控制的相關(guān)資料,文中通過示例代碼以及圖文介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧

前言

在一個(gè)項(xiàng)目中,一些功能會(huì)涉及到重要的數(shù)據(jù)管理,為了確保數(shù)據(jù)的安全,我們會(huì)在項(xiàng)目中加入權(quán)限來限制每個(gè)用戶的操作。作為前端,我們要做的是配合后端給到的權(quán)限數(shù)據(jù),做頁面上的各種各樣的限制。

需求

因?yàn)檫@是一個(gè)工作上的業(yè)務(wù)需求,所以對(duì)于我來說主要有兩個(gè)地方需要進(jìn)行權(quán)限控制。

第一個(gè)是側(cè)邊菜單欄,需要控制顯示與隱藏。

第二個(gè)就是頁面內(nèi)的各個(gè)按鈕,彈窗等。

流程

1、如何獲取用戶權(quán)限?

后端(當(dāng)前用戶擁有的權(quán)限列表)-> 前端(通過后端的接口獲取到,下文中我們把當(dāng)前用戶的權(quán)限列表叫做 permissionList)

2、前端如何做限制?

通過產(chǎn)品的需求,在項(xiàng)目中進(jìn)行權(quán)限點(diǎn)的配置,然后通過 permissionList 尋找是否有配置的權(quán)限點(diǎn),有就顯示,沒有就不顯示。

3、然后呢?

沒了。

當(dāng)我剛開始接到這個(gè)需求的時(shí)候就是這么想的,這有什么難的,不就獲取 permissionList 然后判斷就可以了嘛。后來我才發(fā)現(xiàn)真正的需求遠(yuǎn)比我想象的復(fù)雜。

真正的問題

上面的需求有提到我們主要解決兩個(gè)問題,側(cè)邊菜單欄的顯示 & 頁面內(nèi)操作。

假設(shè)我們有這樣一個(gè)路由的設(shè)置(以下只是一個(gè)例子):

import VueRouter from 'vue-router'
/* 注意:以下配置僅為部分配置,并且省去了 component 的配置 */
export const routes = [
 {
 path: '/',
 name: 'Admin',
 label: '首頁'
 },
 {
 path: '/user',
 name: 'User',
 label: '用戶',
 redirect: { name: 'UserList' },
 children: [
 {
 path: 'list',
 name: 'UserList',
 label: '用戶列表'
 },
 {
 path: 'group',
 name: 'UserGroup',
 label: '用戶組',
 redirect: { name: 'UserGroupList' },
 children: [
 {
 path: 'list',
 name: 'UserGroupList',
 label: '用戶組列表'
 },
 {
 path: 'config',
 name: 'UserGroupConfig',
 label: '用戶組設(shè)置'
 }
 ]
 }
 ]
 },
 {
 path: '/setting',
 name: 'Setting',
 label: '系統(tǒng)設(shè)置'
 },
 {
 path: '/login',
 name: 'Login',
 label: '登錄'
 }
]

const router = new VueRouter({
 routes
})

export default router

其中前兩級(jí)路由會(huì)顯示在側(cè)邊欄中,第三級(jí)就不會(huì)顯示在側(cè)邊欄中了。

頁面內(nèi)操作的權(quán)限設(shè)置不需要考慮很多其他東西,我們主要針對(duì)側(cè)邊欄以及路由進(jìn)行問題的分析,通過分析,主要有以下幾個(gè)問題:

  1. 什么時(shí)候獲取 permissionList,如何存儲(chǔ) permissionList
  2. 子路由全都沒權(quán)限時(shí)不應(yīng)該顯示本身(例:當(dāng)用戶列表和用戶組都沒有權(quán)限時(shí),用戶也不應(yīng)該顯示在側(cè)邊欄)
  3. 默認(rèn)重定向的路由沒有權(quán)限時(shí),應(yīng)尋找 children 中有權(quán)限的一項(xiàng)重定向(例:用戶路由重定向到用戶列表路由,若用戶列表沒有權(quán)限,則應(yīng)該重定向到用戶組路由)
  4. 當(dāng)用戶直接輸入沒有權(quán)限的 url 時(shí)需要跳轉(zhuǎn)到?jīng)]有權(quán)限的頁面或其他操作。(路由限制)

下面我們針對(duì)以上問題一個(gè)一個(gè)解決。

什么時(shí)候獲取權(quán)限,存儲(chǔ)在哪 & 路由限制

我這里是在 router 的 beforeEach 中獲取的,獲取的 permissionList 是存放在 vuex 中。

原因是考慮到要做路由的限制,以及方便后面項(xiàng)目中對(duì)權(quán)限列表的使用,以下是實(shí)現(xiàn)的示例:

首先我們加入權(quán)限配置到 router 上:

// 以下只展示部分配置
{
 path: '/user',
 name: 'User',
 label: '用戶',
 meta: {
 permissions: ['U_1']
 },
 redirect: { name: 'UserList' },
 children: [
 {
 path: 'list',
 name: 'UserList',
 label: '用戶列表',
 meta: {
 permissions: ['U_1_1']
 }
 },
 {
 path: 'group',
 name: 'UserGroup',
 label: '用戶組',
 meta: {
 permissions: ['U_1_2']
 },
 redirect: { name: 'UserGroupList' },
 children: [
 {
 path: 'list',
 name: 'UserGroupList',
 label: '用戶組列表',
 meta: {
 permissions: ['U_1_2_1']
 }
 },
 {
 path: 'config',
 name: 'UserGroupConfig',
 label: '用戶組設(shè)置',
 meta: {
 permissions: ['U_1_2_2']
 }
 }
 ]
 }
 ]
}

可以看到我們把權(quán)限加在了 meta 上,是為了更簡單的從 router.beforeEch 中進(jìn)行權(quán)限判斷,權(quán)限設(shè)置為一個(gè)數(shù)組,是因?yàn)橐粋€(gè)頁面可能涉及多個(gè)權(quán)限。

接下來我們?cè)O(shè)置 router.beforeEach :

// 引入項(xiàng)目的 vuex
import store from '@/store'
// 引入判斷是否擁有權(quán)限的函數(shù)
import { includePermission } from '@/utils/permission'

router.beforeEach(async (to, from, next) => {
 // 先判斷是否為登錄,登錄了才能獲取到權(quán)限,怎么判斷登錄就不寫了
 if (!isLogin) {
 try {
 // 這里獲取 permissionList
 await store.dispatch('getPermissionList')
 // 這里判斷當(dāng)前頁面是否有權(quán)限
 const { permissions } = to.meta
 if (permissions) {
 const hasPermission = includePermission(permissions)
 if (!hasPermission) next({ name: 'NoPermission' })
 }
 next()
 }
 } else {
 next({ name: 'Login' })
 }
})

我們可以看到我們需要一個(gè)判斷權(quán)限的方法 & vuex 中的 getPermissionList 如下:

// @/store
export default {
 state: {
 permissionList: []
 },
 mutations: {
 updatePermissionList: (state, payload) => {
 state.permissionList = payload
 }
 },
 actions: {
 getPermissionList: async ({ state, commit }) => {
 // 這里是為了防止重復(fù)獲取
 if (state.permissionList.length) return
 // 發(fā)送請(qǐng)求方法省略
 const list = await api.getPermissionList()
 commit('updatePermissionList', list)
 }
 }
}
// @/utils/permission
import store from '@/store'

/**
 * 判斷是否擁有權(quán)限
 * @param {Array<string>} permissions - 要判斷的權(quán)限列表
 */
function includePermission (permissions = []) {
 // 這里要判斷的權(quán)限沒有設(shè)置的話,就等于不需要權(quán)限,直接返回 true
 if (!permissions.length) return true
 const permissionList = store.state.permissionList
 return !!permissions.find(permission => permissionList.includes(permission))
}

重定向問題

以上我們解決了路由的基本配置與權(quán)限如何獲取,怎么限制路由跳轉(zhuǎn),接下來我們要處理的就是重定向問題了。
這一點(diǎn)可能和我們項(xiàng)目本身架構(gòu)有關(guān),我們項(xiàng)目的側(cè)邊欄下還有子級(jí),是以下圖中的 tab 切換展現(xiàn)的,正常情況當(dāng)點(diǎn)擊藥品管理后頁面會(huì)重定向到入庫管理的 tab 切換頁面,但當(dāng)入庫管理沒有權(quán)限時(shí),則應(yīng)該直接重定向到出庫管理界面。

所以想實(shí)現(xiàn)以上的效果,我需要重寫 router 的 redirect,做到可以動(dòng)態(tài)判斷(因?yàn)樵谖遗渲寐酚蓵r(shí)并不知道當(dāng)前用戶的權(quán)限列表)

然后我查看了 vue-router 的文檔,發(fā)現(xiàn)了 redirect 可以是一個(gè)方法,這樣就可以解決重定向問題了。

vue-router 中 redirect 說明 ,根據(jù)說明我們可以改寫 redirect 如下:

// 我們需要引入判斷權(quán)限方法
import { includePermission } from '@/utils/permission'

const children = [
 {
 path: 'list',
 name: 'UserList',
 label: '用戶列表',
 meta: {
 permissions: ['U_1_1']
 }
 },
 {
 path: 'group',
 name: 'UserGroup',
 label: '用戶組',
 meta: {
 permissions: ['U_1_2']
 }
 }
]

const routeDemo = {
 path: '/user',
 name: 'User',
 label: '用戶',
 redirect: (to) => {
 if (includePermission(children[0].meta.permissions)) return { name: children[0].name }
 if (includePermission(children[1].meta.permissions)) return { name: children[1].name }
 },
 children
}

雖然問題解決了,但是發(fā)現(xiàn)這樣寫下去很麻煩,還要修改 router 的配置,所以我們使用一個(gè)方法生成:

// @/utils/permission
/**
 * 創(chuàng)建重定向函數(shù)
 * @param {Object} redirect - 重定向?qū)ο?
 * @param {string} redirect.name - 重定向的組件名稱
 * @param {Array<any>} children - 子列表
 */
function createRedirectFn (redirect = {}, children = []) {
 // 避免緩存太大,只保留 children 的 name 和 permissions
 const permissionChildren = children.map(({ name = '', meta: { permissions = [] } = {} }) => ({ name, permissions }))
 return function (to) {
 // 這里一定不能在 return 的函數(shù)外面篩選,因?yàn)闄?quán)限是異步獲取的
 const hasPermissionChildren = permissionChildren.filter(item => includePermission(item.permissions))
 // 默認(rèn)填寫的重定向的 name
 const defaultName = redirect.name || ''
 // 如果默認(rèn)重定向沒有權(quán)限,則從 children 中選擇第一個(gè)有權(quán)限的路由做重定向
 const firstPermissionName = (hasPermissionChildren[0] || { name: '' }).name
 // 判斷是否需要修改默認(rèn)的重定向
 const saveDefaultName = !!hasPermissionChildren.find(item => item.name === defaultName && defaultName)
 if (saveDefaultName) return { name: defaultName }
 else return firstPermissionName ? { name: firstPermissionName } : redirect
 }
}

然后我們就可以改寫為:

// 我們需要引入判斷權(quán)限方法
import { includePermission, createRedirectFn } from '@/utils/permission'

const children = [
 {
 path: 'list',
 name: 'UserList',
 label: '用戶列表',
 meta: {
 permissions: ['U_1_1']
 }
 },
 {
 path: 'group',
 name: 'UserGroup',
 label: '用戶組',
 meta: {
 permissions: ['U_1_2']
 }
 }
]

const routeDemo = {
 path: '/user',
 name: 'User',
 label: '用戶',
 redirect: createRedirectFn({ name: 'UserList' }, children),
 children
}

這樣稍微簡潔一些,但我還是需要一個(gè)一個(gè)路由去修改,所以我又寫了一個(gè)方法來遞歸 router 配置,并重寫他們的 redirect:

// @/utils/permission
/**
 * 創(chuàng)建有權(quán)限的路由配置(多級(jí))
 * @param {Object} config - 路由配置對(duì)象
 * @param {Object} config.redirect - 必須是 children 中的一個(gè),并且使用 name
 */
function createPermissionRouter ({ redirect, children = [], ...others }) {
 const needRecursion = !!children.length
 if (needRecursion) {
 return {
 ...others,
 redirect: createRedirectFn(redirect, children),
 children: children.map(item => createPermissionRouter(item))
 }
 } else {
 return {
 ...others,
 redirect
 }
 }
}

這樣我們只需要在最外層的 router 配置加上這樣一層函數(shù)就可以了:

import { createPermissionRouter } from '@/utils/permission'

const routesConfig = [
 {
 path: '/user',
 name: 'User',
 label: '用戶',
 meta: {
 permissions: ['U_1']
 },
 redirect: { name: 'UserList' },
 children: [
 {
 path: 'list',
 name: 'UserList',
 label: '用戶列表',
 meta: {
  permissions: ['U_1_1']
 }
 },
 {
 path: 'group',
 name: 'UserGroup',
 label: '用戶組',
 meta: {
  permissions: ['U_1_2']
 },
 redirect: { name: 'UserGroupList' },
 children: [
  {
  path: 'list',
  name: 'UserGroupList',
  label: '用戶組列表',
  meta: {
  permissions: ['U_1_2_1']
  }
  },
  {
  path: 'config',
  name: 'UserGroupConfig',
  label: '用戶組設(shè)置',
  meta: {
  permissions: ['U_1_2_2']
  }
  }
 ]
 }
 ]
 }
]

export const routes = routesConfig.map(item => createPermissionRouter(item))

const router = new VueRouter({
 routes
})

export default router

當(dāng)然這樣寫還有一個(gè)好處,其實(shí)你并不需要設(shè)置 redirect,這樣會(huì)自動(dòng)重定向到 children 的第一個(gè)有權(quán)限的路由

側(cè)邊欄顯示問題

我們的項(xiàng)目使用的是根據(jù)路由的配置來生成側(cè)邊欄的,當(dāng)然會(huì)加一些其他的參數(shù)來顯示顯示層級(jí)等問題,這里就不寫具體代碼了,如何解決側(cè)邊欄 children 全都無權(quán)限不顯示的問題呢。

這里我的思路是,把路由的配置也一同更新到 vuex 中,然后側(cè)邊欄配置從 vuex 中的配置來讀取。

由于這個(gè)地方涉及修改的東西有點(diǎn)多,而且涉及業(yè)務(wù),我就不把代碼拿出來了,你可以自行實(shí)驗(yàn)。

方便團(tuán)隊(duì)部署權(quán)限點(diǎn)的方法

以上我們解決了大部分權(quán)限的問題,那么還有很多涉及到業(yè)務(wù)邏輯的權(quán)限點(diǎn)的部署,所以為了團(tuán)隊(duì)中其他人可以優(yōu)雅簡單的部署權(quán)限點(diǎn)到各個(gè)頁面中,我在項(xiàng)目中提供了以下幾種方式來部署權(quán)限:

通過指令 v-permission 來直接在 template 上設(shè)置

<div v-permission="['U_1']"></div>

通過全局方法 this.$permission 判斷,因?yàn)橛行?quán)限并非在模版中的

{
 hasPermission () {
 // 通過方法 $permission 判斷是否擁有權(quán)限
 return this.$permission(['U_1_1', 'U_1_2'])
 }
}

這里要注意,為了 $permission 方法的返回值是可被監(jiān)測的,判斷時(shí)需要從 this.$store 中來判斷,以下為實(shí)現(xiàn)代碼:

// @/utils/permission
/**
 * 判斷是否擁有權(quán)限
 * @param {Array<string|number>} permissions - 要判斷的權(quán)限列表
 * @param {Object} permissionList - 傳入 store 中的權(quán)限列表以實(shí)現(xiàn)數(shù)據(jù)可監(jiān)測
 */
function includePermissionWithStore (permissions = [], permissionList = []) {
 if (!permissions.length) return true
 return !!permissions.find(permission => permissionList.includes(permission))
}
import { includePermissionWithStore } from '@/utils/permission'
export default {
 install (Vue, options) {
 Vue.prototype.$permission = function (permissions) {
 const permissionList = this.$store.state.permissionList
 return includePermissionWithStore(permissions, permissionList)
 }
 }
}

以下為指令的實(shí)現(xiàn)代碼(為了不與 v-if 沖突,這里控制顯示隱藏通過添加/移除 className 的方式):

// @/directive/permission
import { includePermission } from '@/utils/permission'
const permissionHandle = (el, binding) => {
 const permissions = binding.value
 if (!includePermission(permissions)) {
 el.classList.add('hide')
 } else {
 el.classList.remove('hide')
 }
}
export default {
 inserted: permissionHandle,
 update: permissionHandle
}

總結(jié)

針對(duì)之前的問題,有以下的總結(jié):

1、什么時(shí)候獲取 permissionList,如何存儲(chǔ) permissionList

router.beforeEach 獲取,存儲(chǔ)在 vuex。

2、子路由全都沒權(quán)限時(shí)不應(yīng)該顯示本身(例:當(dāng)用戶列表和用戶設(shè)置都沒有權(quán)限時(shí),用戶也不應(yīng)該顯示在側(cè)邊欄)

通過存儲(chǔ)路由配置到 vuex 中,生成側(cè)邊欄設(shè)置,獲取權(quán)限后修改 vuex 中的配置控制顯示 & 隱藏。

3、默認(rèn)重定向的路由沒有權(quán)限時(shí),應(yīng)尋找 children 中有權(quán)限的一項(xiàng)重定向(例:用戶路由重定向到用戶列表路由,若用戶列表沒有權(quán)限,則應(yīng)該重定向到用戶組路由)

通過 vue-router 中 redirect 設(shè)置為 Function 來實(shí)現(xiàn)

4、當(dāng)用戶直接輸入沒有權(quán)限的 url 時(shí)需要跳轉(zhuǎn)到?jīng)]有權(quán)限的頁面或其他操作。(路由限制)

在 meta 中設(shè)置權(quán)限, router.beforeEach 中判斷權(quán)限。

以上就是我對(duì)于這次權(quán)限需求的大體解決思路與代碼實(shí)現(xiàn),可能并不是很完美,但還是希望可以幫助到你 ^_^

好了,以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對(duì)腳本之家的支持。

相關(guān)文章

  • 深入理解vue.js中的v-if和v-show

    深入理解vue.js中的v-if和v-show

    這篇文章主要給大家深入的介紹了關(guān)于vue.js中v-if和v-show的相關(guān)資料,文中詳細(xì)介紹兩者的共同點(diǎn)和區(qū)別,通過圖文介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起看看吧。
    2017-06-06
  • vue父組件傳值子組件報(bào)錯(cuò)Avoid?mutating?a?prop?directly解決

    vue父組件傳值子組件報(bào)錯(cuò)Avoid?mutating?a?prop?directly解決

    這篇文章主要為大家介紹了vue父組件傳值子組件報(bào)錯(cuò)Avoid?mutating?a?prop?directly解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-09-09
  • Vue讓router-view默認(rèn)顯示頁面操作方法

    Vue讓router-view默認(rèn)顯示頁面操作方法

    一個(gè)home頁面,點(diǎn)擊左邊的菜單欄,右邊顯示頁面,因此都知道在右邊放一個(gè)router-view然后配置路由即可,然而問題出現(xiàn)在:重新打開的時(shí)候,默認(rèn)是白色空的,遇到這樣的問題如何解決呢,下面小編給大家分享Vue讓router-view默認(rèn)顯示頁面操作方法,感興趣的朋友一起看看吧
    2024-03-03
  • vue3配置Element及element-plus/lib/theme-chalk/index.css報(bào)錯(cuò)的解決

    vue3配置Element及element-plus/lib/theme-chalk/index.css報(bào)錯(cuò)的解決

    這篇文章主要介紹了vue3配置Element及element-plus/lib/theme-chalk/index.css報(bào)錯(cuò)的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • vue如何在線預(yù)覽各類型文件

    vue如何在線預(yù)覽各類型文件

    這篇文章主要介紹了vue如何在線預(yù)覽各類型文件問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-11-11
  • vue父元素點(diǎn)擊事件與子元素點(diǎn)擊事件沖突問題

    vue父元素點(diǎn)擊事件與子元素點(diǎn)擊事件沖突問題

    這篇文章主要介紹了vue父元素點(diǎn)擊事件與子元素點(diǎn)擊事件沖突問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-01-01
  • 基于vue.js中關(guān)于下拉框的值默認(rèn)及綁定問題

    基于vue.js中關(guān)于下拉框的值默認(rèn)及綁定問題

    今天小編就為大家分享一篇基于vue.js中關(guān)于下拉框的值默認(rèn)及綁定問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2018-08-08
  • Vue后臺(tái)管理系統(tǒng)之實(shí)現(xiàn)分頁功能示例

    Vue后臺(tái)管理系統(tǒng)之實(shí)現(xiàn)分頁功能示例

    本文主要介紹了Vue后臺(tái)管理系統(tǒng)之實(shí)現(xiàn)分頁功能,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-12-12
  • vue全局指令文件?directives詳解

    vue全局指令文件?directives詳解

    全局指令是Vue.js中的一種自定義指令,可以在整個(gè)應(yīng)用中重復(fù)使用,通過Vue.directive方法定義,可以在組件內(nèi)部直接使用,指令有多個(gè)生命周期鉤子,可以接收參數(shù)和修飾符,提高代碼復(fù)用性和可維護(hù)性,本文介紹vue全局指令文件directives,感興趣的朋友一起看看吧
    2025-02-02
  • idea部署RuoYi-Vue分離版的圖文詳解

    idea部署RuoYi-Vue分離版的圖文詳解

    RuoYi-Vue是一款基于SpringBoot+Vue的前后端分離極速后臺(tái)開發(fā)框架, 本文主要介紹了idea部署RuoYi-Vue分離版的圖文詳解,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-06-06

最新評(píng)論

浙江省| 屏山县| 兴海县| 罗平县| 城市| 蒙阴县| 饶河县| 额尔古纳市| 夹江县| 横峰县| 毕节市| 碌曲县| 镶黄旗| 吉水县| 许昌县| 禄丰县| 邻水| 白山市| 肇源县| 娱乐| 稷山县| 邳州市| 铜陵市| 抚顺市| 罗江县| 辉南县| 格尔木市| 淳安县| 忻州市| 明水县| 临沭县| 元江| 泸州市| 新龙县| 怀宁县| 洪泽县| 色达县| 吕梁市| 深圳市| 朔州市| 垦利县|