Copyright © 2008 ~ 2024 MUKI space* / omegaBook theme All Rights Reserved.

我們常用的 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 應該還有其他的判斷,所以請記得要把這些判斷全部考慮進去唷

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

參考資料

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

2
Subscribe
Notify of
guest

0 則留言
Inline Feedbacks
View all comments