内射老阿姨1区2区3区4区_久久精品人人做人人爽电影蜜月_久久国产精品亚洲77777_99精品又大又爽又粗少妇毛片

如何使用Vue實現(xiàn)按鈕級權(quán)限方案

這篇文章將為大家詳細(xì)講解有關(guān)如何使用Vue實現(xiàn)按鈕級權(quán)限方案,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。

創(chuàng)新互聯(lián)專注于晉源企業(yè)網(wǎng)站建設(shè),成都響應(yīng)式網(wǎng)站建設(shè)公司,商城網(wǎng)站制作。晉源網(wǎng)站建設(shè)公司,為晉源等地區(qū)提供建站服務(wù)。全流程按需求定制開發(fā),專業(yè)設(shè)計,全程項目跟蹤,創(chuàng)新互聯(lián)專業(yè)和態(tài)度為您提供的服務(wù)

演示

在年初開發(fā)一個中后臺管理系統(tǒng),功能涉及到了各個部門(產(chǎn)品、客服、市場等等),在開始的版本中,我和后端配合使用了花褲衩手摸手系列的權(quán)限方案,前期非常nice,但是慢慢的隨著功能增多、業(yè)務(wù)越來越復(fù)雜,就變得有些吃力了,因為我們的權(quán)限動態(tài)性太大了

  • 手摸手系列權(quán)限方案是有比較清晰的權(quán)限劃分的,而我們公司部門的崗位職責(zé)有時比較模糊。

  • 后端采用RBAC權(quán)限方案,為了達(dá)到第1點要求,將角色劃分的很細(xì),并且角色有時頻繁變動,導(dǎo)致每一次前端都需要手動維護(hù)

  • 為了解決上面2個痛點,我將原方案進(jìn)行了一丟丟改造。

  • 前端不再以角色來控制權(quán)限,而是以更小粒度的操作(接口)來控制,也就是前端不關(guān)心角色

  • 路由還是由前端維護(hù)(我們的后端很排斥維護(hù)和他們不相干的東西:joy:),但改為通過操作列表對權(quán)限路由進(jìn)行過濾

  • 使用單一的方式(方便維護(hù))控制頁面的局部權(quán)限,不再使用自定義指令方式,而是通過函數(shù)式組件,原因是使用自定義指令有多余的開銷(插入再移除)

后端的配合:

routerName

有一些注意點:

  • 比如一個有權(quán)限的列表頁面A,同時這個列表接口被權(quán)限頁面B使用,現(xiàn)在你配置權(quán)限讓某一個用戶沒有A頁面權(quán)限,但可以使用B頁面,如果你的本意是可以使用B頁面的所有功能,這時就會有問題,所以盡量不要將權(quán)限接口跨頁面使用,需要分清哪些數(shù)據(jù)需要通過字典接口獲取還是通過權(quán)限接口獲取

  • 有些人可能會糾結(jié),前端維護(hù)權(quán)限安全嗎?肯定是不安全的,安全性主要還在后端這邊把控,后端做好數(shù)據(jù)和接口方面的權(quán)限控制,前端做權(quán)限控制我認(rèn)為主要還是為了交互體驗等。沒有權(quán)限你為什么要讓我看到那一坨?

  • 在使用這種方式之前,要明確當(dāng)前場景是否確實需要這么做,畢竟在項目比較大且接口很多的情況下,你跟操作碼之間有一場持久戰(zhàn)

實現(xiàn)

操作列表示例

以Restful風(fēng)格接口為例

const operations = [
 {
 url: '/xxx',
 type: 'get',
 name: '查詢xxx',
 routeName: 'route1', // 接口對應(yīng)的路由
 opcode: 'XXX_GET' // 操作碼,不變的
 },
 {
 url: '/xxx',
 type: 'post',
 name: '新增xxx',
 routeName: 'route1',
 opcode: 'XXX_POST'
 },
 // ......
]

路由的變化

在路由的 meta 中增加一個配置字段如 requireOps ,值可能為 String 或者 Array ,這表示當(dāng)前路由頁面要顯示的必要的操作碼, Array 類型是為了處理一個路由頁面需要滿足同時存在多個操作權(quán)限時才顯示的情況。若值不為這2種則視為無權(quán)限控制,任何用戶都能訪問

由于最終需要根據(jù)過濾后的權(quán)限路由動態(tài)生成菜單,所以還需要在路由選項中增加幾個字段處理顯示問題,其中 hidden 優(yōu)先級大于 visible

hidden
visible
const permissionRoutes = [
 {
 // visible: false,
 // hidden: true,
 path: '/xxx',
 name: 'route1',
 meta: {
  title: '路由1',
  requireOps: 'XXX_GET'
 },
 // ...
 }
]

由于路由在前端維護(hù),所以以上配置只能寫死,如果后端能同意維護(hù)這一份路由表,那就可以有很多的發(fā)揮空間了,體驗也能做的更好。

權(quán)限路由過濾

先將權(quán)限路由規(guī)范一下,同時保留一個副本,可能在可視化時需要用到

const routeMap = (routes, cb) => routes.map(route => {
 if (route.children && route.children.length > 0) {
 route.children = routeMap(route.children, cb)
 }
 return cb(route)
})
const hasRequireOps = ops => Array.isArray(ops) || typeof ops === 'string'
const normalizeRequireOps = ops => hasRequireOps(ops)
 ? [].concat(...[ops])
 : null
const normalizeRouteMeta = route => {
 const meta = route.meta = {
 ...(route.meta || {})
 }
 meta.requireOps = normalizeRequireOps(meta.requireOps)
 return route
}

permissionRoutes = routeMap(permissionRoutes, normalizeRouteMeta)
const permissionRoutesCopy = JSON.parse(JSON.stringify(permissionRoutes))

獲取到操作列表后,只需要遍歷權(quán)限路由,然后查詢 requireOps 代表的操作有沒有在操作列表中。這里需要處理一下 requireOps 未設(shè)置的情況,如果子路由中都是權(quán)限路由,需要為父級路由自動加上 requireOps 值,不然當(dāng)所有子路由都沒有權(quán)限時,父級路由就被認(rèn)為是無權(quán)限控制且可訪問的;而如果子路由中只要有一個路由無權(quán)限控制,那就不需要處理父路由。所以這里可以用遞歸來解決,先處理子路由再處理父路由

const filterPermissionRoutes = (routes, cb) => {
 // 可能父路由沒有設(shè)置requireOps 需要根據(jù)子路由確定父路由的requireOps
 routes.forEach(route => {
 if (route.children) {
  route.children = filterPermissionRoutes(route.children, cb)
  
  if (!route.meta.requireOps) {
  const hasNoPermission = route.children.some(child => child.meta.requireOps === null)
  // 如果子路由中存在不需要權(quán)限控制的路由,則跳過
  if (!hasNoPermission) {
   route.meta.requireOps = [].concat(...route.children.map(child => child.meta.requireOps))
  }
  }
 }
 })

 return cb(routes)
}

然后根據(jù)操作列表對權(quán)限路由進(jìn)行過濾

let operations = null // 從后端獲取后更新它
const hasOp = opcode => operations
 ? operations.some(op => op.opcode === opcode)
 : false

const proutes = filterPermissionRoutes(permissionRoutes, routes => routes.filter(route => {
 const requireOps = route.meta.requireOps

 if (requireOps) {
 return requireOps.some(hasOp)
 }

 return true
}))

// 動態(tài)添加路由
router.addRoutes(proutes)

函數(shù)式組件控制局部權(quán)限

這個組件實現(xiàn)很簡單,根據(jù)傳入的操作碼進(jìn)行權(quán)限判斷,若通過則返回插槽內(nèi)容,否則返回null。另外,為了統(tǒng)一風(fēng)格,支持一下 root 屬性,表示組件的根節(jié)點

const AccessControl = {
 functional: true,
 render (h, { data, children }) {
 const attrs = data.attrs || {}

 // 如果是root,直接透傳
 if (attrs.root !== undefined) {
  return h(attrs.root || 'div', data, children)
 }

 if (!attrs.opcode) {
  return h('span', {
  style: {
   color: 'red',
   fontSize: '30px'
  }
  }, '請配置操作碼')
 }

 const opcodes = attrs.opcode.split(',')

 if (opcodes.some(hasOp)) {
  return children
 }

 return null
 }
}

動態(tài)生成權(quán)限菜單

以ElementUI為例,由于動態(tài)渲染需要進(jìn)行遞歸,如果以文件組件的形式會多一層根組件,所以這里直接用render function簡單寫一個示例,可以根據(jù)自己的需求改造

// 權(quán)限菜單組件
export const PermissionMenuTree = {
 name: 'MenuTree',
 props: {
 routes: {
  type: Array,
  required: true
 },
 collapse: Boolean
 },
 render (h) {
 const createMenuTree = (routes, parentPath = '') => routes.map(route => {
  // hidden: 為true時當(dāng)前菜單和子菜單都不顯示
  if (route.hidden === true) {
  return null
  }

  // 子路徑處理
  const fullPath = route.path.charAt(0) === '/' ? route.path : `${parentPath}/${route.path}`

  // visible: 為false時不顯示當(dāng)前菜單,但顯示子菜單
  if (route.visible === false) {
  return createMenuTree(route.children, fullPath)
  }

  const title = route.meta.title
  const props = {
  index: fullPath,
  key: route.path
  }

  if (!route.children || route.children.length === 0) {
  return h(
   'el-menu-item',
   { props },
   [h('span', title)]
  )
  }

  return h(
  'el-submenu',
  { props },
  [
   h('span', { slot: 'title' }, title),
   ...createMenuTree(route.children, fullPath)
  ]
  )
 })

 return h(
  'el-menu',
  {
  props: {
   collapse: this.collapse,
   router: true,
   defaultActive: this.$route.path
  }
  },
  createMenuTree(this.routes)
 )
 }
}

接口的權(quán)限控制

我們一般用axios,這里只需要在axios封裝的基礎(chǔ)上加幾行代碼就可以了,axios封裝花樣多多,這里簡單示例

const ajax = axios.create(/* config */)

export default {
 post (url, data, opcode, config = {}) {
 if (opcode && !hasOp(opcode)) {
  return Promise.reject(new Error('沒有操作權(quán)限'))
 }
 return ajax.post(url, data, { /* config */ ...config }).then(({ data }) => data)
 },
 // ...
}

關(guān)于“如何使用Vue實現(xiàn)按鈕級權(quán)限方案”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,使各位可以學(xué)到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。

文章名稱:如何使用Vue實現(xiàn)按鈕級權(quán)限方案
文章網(wǎng)址:http://www.rwnh.cn/article44/gjhiee.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供App開發(fā)、手機網(wǎng)站建設(shè)、云服務(wù)器、虛擬主機、全網(wǎng)營銷推廣、域名注冊

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

h5響應(yīng)式網(wǎng)站建設(shè)
安泽县| 隆安县| 屯昌县| 偏关县| 安丘市| 岚皋县| 沾益县| 浮梁县| 普定县| 沅陵县| 吴忠市| 肥西县| 马尔康县| 离岛区| 乌审旗| 恭城| 剑川县| 阿尔山市| 绍兴市| 遂宁市| 大埔县| 九龙坡区| 长治县| 泸溪县| 仙游县| 湘西| 琼结县| 嵊州市| 滦南县| 中方县| 河间市| 昌乐县| 柳江县| 哈密市| 彭水| 习水县| 青海省| 房产| 平湖市| 新乡县| 昭通市|