通用管理后台组件库-11-公共指令

0 阅读3分钟

公共指令

说明: 封装公共指令,v-hasPermission、v-copy、v-debounce、v-throttle

效果如图:

image.png

1.权限指令v-hasPermission

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

/**
 * 权限指令
 * 用于根据用户角色控制元素是否显示
 */
const hasPermission: Directive = {
  /**
   * 指令挂载时执行
   * @param el - 绑定指令的DOM元素
   * @param binding - 指令绑定对象,包含权限值
   */
  mounted: function (el: HTMLElement, binding: DirectiveBinding) {
    // 获取用户状态存储
    const store = useUserStore()
    // 获取绑定的权限值
    const values = binding.value

    // 添加指令修饰符,用于反转权限判断逻辑
    const { not } = binding.modifiers

    const checkPermission = () => {
      // 处理权限值为字符串的情况
      if (typeof values === 'string') {
        // 如果用户角色不包含该权限值,则移除元素
        const flag = not ? store.roles.includes(values) : !store.roles.includes(values)
        if (flag) {
          el.style.display = 'none'
        } else {
          el.style.display = ''
        }
      }
      // 处理权限值为数组的情况
      if (Array.isArray(values)) {
        // 如果用户角色不包含数组中的任何一个权限值,则移除元素
        const flag = not
          ? values.some((item) => store.roles.includes(item))
          : !values.some((item) => store.roles.includes(item))
        if (flag) {
          el.style.display = 'none'
        } else {
          el.style.display = ''
        }
      }
    }
    checkPermission()
    
    // 根据store中数据,动态更新权限
    store.$subscribe(
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      (mutation, _state) => {
        const { events } = mutation
        if ((events as any).key === 'roles') {
          checkPermission()
        }
      },
      { deep: true }
    )
  }
}

export default hasPermission

2.防抖指令v-debounce

/**
 * 防抖指令v-debounce
 * 用于在元素上添加防抖点击功能,避免用户快速连续点击导致的重复* 操作,最后只执行一次点击
 */
export default {
  /**
   * 指令挂载时执行的函数
   * @param {HTMLElement} el - 绑定指令的DOM元素
   * @param {Object} binding - 指令绑定的值对象
   */
  mounted: function (el, binding) {
    // 检查绑定值是否为函数,如果不是则抛出错误
    if (typeof binding.value !== 'function') {
      throw 'callback must be a function'
    }
    let timer: any | null = null
    el.__handleClick = function () {
      if (timer) {
        clearInterval(timer)
      }
      timer = setTimeout(() => {
        binding.value()
      }, 500)
    }
    el.addEventListener('click', el.__handleClick)
  },
  beforeUnmount: function (el) {
    el.removeEventListener('click', el.__handleClick)
  }
}

3.节流指令v-throttle.ts

/**
 * 节流指令v-throttle
 */
export default {
  /**
   * 指令挂载时执行的函数
   * @param {HTMLElement} el - 绑定指令的DOM元素
   * @param {Object} binding - 指令绑定的值对象
   */
  mounted: function (el, binding) {
    // 检查绑定值是否为函数,如果不是则抛出错误
    if (typeof binding.value !== 'function') {
      throw 'callback must be a function'
    }
    // 创建一个定时器变量,初始值为null
    let timer: any | null = null
    // 为元素添加一个自定义的点击处理函数
    el.__handleClick = function () {
      // 如果已存在定时器,则清除定时器
      if (timer) {
        clearInterval(timer)
      }
      // 如果元素未被禁用,则禁用它并执行回调函数
      if (!el.disabled) {
        el.disabled = true
        binding.value()
        // 设置一个定时器,1秒后重新启用元素
        timer = setTimeout(() => {
          el.disabled = false
        }, 1000)
      }
    }
    // 为元素添加点击事件监听器
    el.addEventListener('click', el.__handleClick)
  },
  // 组件卸载前执行的函数,用于清理事件监听器
  beforeUnmount: function (el) {
    // 移除之前添加的点击事件监听器,防止内存泄漏
    el.removeEventListener('click', el.__handleClick)
  }
}

4.复制指令 v-copy

/**
 * 复制指令:v-copy
 */
import { useClipboard } from '@vueuse/core'
import { ElMessage } from 'element-plus'

export default {
  mounted: function (el, binding) {
    // 将数据添加到元素标签上
    el.copyData = binding.value
    const { copy } = useClipboard({ source: binding.value })
    const handleClick = () => {
      // 复制内容
      copy(el.copyData)
      ElMessage.success('复制成功')
    }
    el.addEventListener('click', handleClick)
    el.__handleClick = handleClick
  },
  updated: function (el, binding) {
    el.copyData = binding.value
  },
  beforeUnmount: function (el) {
    // 移除监听事件
    el.removeEventListener('click', el.__handleClick)
  }
}

5.demo实现

  <div class="w-full h-full p-10">
    <div class="flex w-full justify-center items-center border py-4">
      <span>复制指令:</span>
      <span v-copy="copyData">点我进行复制</span>
    </div>
    <div class="flex w-full justify-center items-center border py-4 mt-4">
      <span>防抖指令:</span>
      <el-button v-debounce="debounceClick">防抖点击</el-button>
    </div>
    <div class="flex w-full justify-center items-center border py-4 mt-4">
      <span>节流指令:</span>
      <el-button v-debounce="throttleClick">节流点击</el-button>
    </div>
    <div class="flex w-full justify-center items-center border py-4 mt-4">
      <span>权限指令:</span>
      <el-button v-hasPermission="['user']">权限控制按钮</el-button>
    </div>
  </div>
</template>

<script setup lang="ts">
definePage({
  meta: {
    title: '基础指令',
    icon: 'material-symbols:keyboard-command-key'
  }
})
const copyData = ref<string>('点我进行复制')

const debounceClick = () => {
  console.log('防抖点击')
}
const throttleClick = () => {
  console.log('节流点击')
}
</script>

<style scoped></style>