/
CATEGORY
Vue
/
使用 Vue 自定義指令(custom directive)製作網站權限

使用 Vue 自定義指令(custom directive)製作網站權限

MUKI AI Summary

Vue 支援自定義指令,如 v-role,可用來設計網站權限。只需在 template 中使用 v-role 設定會員權限即可。這些指令可應用於 router,實現選單的顯示與隱藏,並可連接後端資料庫動態修改會員權限。

main.ts 中定義指令,並封裝後匯入。使用 pinia 管理 roles data,或暫存於 localStorage。設置 router meta 定義不同權限的訪問路由,使用 nav guard 確保未授權用戶被導回 /home。...

我們常用的 v-on, v-model... 等 v-* 的語法,都是屬於 Vue 的指令,但大家有想過,我們是否能自己定義這個指令呢?例如發展成 v-muki, v-mukispace... 這樣的自訂指令?

答案是可以的!

我最近也用了 v-* 自定義指令,做了一個 v-role 的權限設計,只要在 template 這樣寫:

<template>
	<div v-role="['basic']">權限為 basic 的會員才看得到</div>
	<div v-role="['free']">權限為 free 的會員才看得到</div>
	<div v-role="['basic', 'free']">權限為 basic 或 free 的會員才看得到</div>
</template>

此外也可以應用在 router,讓選單對應隱藏 / 顯示,也能跟後端資料庫串接,即時修改會員的權限。

使用 Vue-* 自定義指令

首先要在 main.ts 定義自己的指令,不過考量到未來可能不只一組自定義指令,所以我選擇的方法是封裝後再匯入,這樣未來如果要開發更多組自定義指令,會比較方便與乾淨

▼ 先在 main.ts 匯入一個管理自定義指令的檔案

import directives from '@/directives/index'

app.use(directives)

▼ 新增剛剛匯入的檔案 (directives/index.ts)

import { App } from 'vue'
import role from './modules/role' // 這邊再匯入一個新的模組檔,就是我要定義的指令內容

const directiveList: any = {
  role
}

const directives = {
  install: function (app: App<Element>) {
    Object.keys(directiveList).forEach(key => {
      app.directive(key, directiveList[key])
    })
  }
}

export default directives

▼ 再新增一個 modules/role.ts 檔案

import { useUserStore } from '@/stores'
import type { Directive, DirectiveBinding } from 'vue'

interface ElType extends HTMLElement {
  parentNode: any
}

const role: Directive = {
  mounted(el: ElType, binding: DirectiveBinding) {
    const store = useUserStore()
    const { value } = binding
    // 該會員權限從 store 取得,我用的是 pinia 套件 (store.roles type : array)
    const roles = store && store.roles
    if (value && value instanceof Array && value.length > 0) {
      const requiredRoles = value
      const hasRole = roles.some(role => {
        return requiredRoles.includes(role)
      })
      // 如果沒有這個權限,就移除整個 HTML 節點
      if (!hasRole) {
        el.parentNode && el.parentNode.removeChild(el)
      }
    } else {
      throw new Error('error')
    }
  }
}

export default role

我把 roles data 存在 pinia store,如果沒有用 pinia 或是想先測試一下看看,可以選擇先存在 localStorage

記得一定要先有 roles data 才能在 Vue component 測試效果喔,不然他會找不到你對應的權限

<template>
    <div v-role="['basic']">權限為 basic 的會員才看得到</div>
    <div v-role="['free']">權限為 free 的會員才看得到</div>
    <div v-role="['basic', 'free']">權限為 basic 或 free 的會員才看得到</div>
</template>

在 router 設定存取權限

假設我希望免費會員,不能瀏覽 /no-free 這個網頁,假設他因為某些原因輸入了這個網址,我必須導回首頁 /home

設定 router meta

首先先設定 router meta,讓我們知道哪些權限的人,可以瀏覽哪些路由位置。

{
  name: 'Home',
  meta: {
    title: 'home',
    roles: ['vip', 'basic', 'free']	// 這一頁 vip, basic, free 都可以瀏覽
  },
  path: '/home',
  component: () => import('@/views/Home.vue'),
}, {
  name: 'NoFree',
  meta: {
    title: 'No Free',
    roles: ['vip', 'basic'] // 這一頁只有 vip, basic 可以瀏覽
  },
  path: '/no-free',
  component: () => import('@/views/NoFree.vue'),
}

設定 nav guard 導航守衛

判斷權限的寫法,跟 role.ts 一樣

router.beforeEach(async (to, from, next) => {
  let hasRole = null

  if (to.meta && to.meta.roles instanceof Array && to.meta.roles.length > 0) {
    const requiredRoles = to.meta.roles
    hasRole = roles.some(role => {
      return requiredRoles.includes(role)
    })
  }
  
  if (hasRole !== null && !hasRole) {
    next('/home')
  } else {
    next()
  }
  
})

以上這個方法就是很簡單的,用來判斷有沒有這個權限,沒有的話就導回 /home 的寫法。

但我相信大家的 nav guard 應該還有其他的判斷,所以請記得要把這些判斷全部考慮進去唷

有任何問題也歡迎留言告訴我,謝謝

參考資料

歡迎給我點鼓勵,讓我知道你來過 :)

3
MUKI says:

如果文章有幫助到你,歡迎分享給更多人知道。文章內容皆為 MUKI 本人的原創文章,我會經常更新文章以及修正錯誤內容,因此轉載時建議保留原出處,避免出現版本不一致或已過時的資訊。

本文地址:https://muki.tw/vue-custom-directive-roles-permission/ 已複製

Subscribe
Notify of
guest

0 則留言
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Copyright © since 2008 MUKI space* / omegaSS theme All Rights Reserved.