经验分享
Vue3自定义指令封装
封装vue3自定义指令,为ant的按钮组件绑定倒计时功能;
2024-08-09 06:43:25
9

开始开发

src/directives 下新建一个 index.ts 统一注册全局自定义指令

// src/directives/index.ts

import { App } from "vue";

export const createDirective = {
  install(app: App) {
    // 指令注册
    app.directive("xxx", {
      // ...
    });
  },
};

main.ts引入,使用给vue

// main.ts

import { createDirective } from "@/directives";
app.use(createDirective);

封装指令

ant-button 绑定倒计时

为按钮绑定倒计时,倒计时结束按钮才可以被点击。

// src/directives/ctd.directive.ts

/**
 * 设置按钮状态
 * @param {BtnStatusOption} btnStatusOption - 设置按钮状态参数
 * @param {HTMLButtonElement} btnStatusOption.el - 按钮元素
 * @param {string} btnStatusOption.className - 按钮class
 * @param {string} btnStatusOption.disabled - 按钮禁用状态
 * @param {number} btnStatusOption.timeTotal - 倒计时总时间,单位s
 * @param {number} btnStatusOption.timeCur - 当前经过的时间,单位s
 * @param {string} btnStatusOption.orginText - 按钮原始文字
 * */
const setBtnStatus = (btnStatusOption: BtnStatusOption) => {
  const { el, className, disabled, timeTotal, timeCur, orginText } = btnStatusOption;
  el.className = className;
  el.disabled = disabled;
  setBtnText({ el, timeTotal, timeCur, orginText });
};

/**
 * 设置按钮文字
 * @param {BtnTextOption} btnTextOption - 设置按钮文字参数
 * @param {HTMLButtonElement} btnTextOption.el - 按钮元素
 * @param {number} btnTextOption.timeTotal - 倒计时总时间,单位s
 * @param {number} btnTextOption.timeCur - 当前经过的时间,单位s
 * @param {string} btnTextOption.orginText - 按钮原始文字
 * */
const setBtnText = (btnTextOption: BtnTextOption) => {
  const { el, timeTotal, timeCur, orginText } = btnTextOption;
  if (el.disabled) {
    el.innerText = `(${timeTotal - timeCur})${orginText}`;
  } else {
    el.innerText = `${orginText}`;
  }
};

/**
 * 自定义指令使用方法
 * @example <a-button v-ctd>确认</a-button> 默认5秒
 * @example <a-button v-ctd="10">确认</a-button> 可设置10秒倒计时
 * */
export const createCtdDirective = {
  beforeMount(el: HTMLButtonElement, binding: any, vnode) {
    const timeTotal = binding.value || 5; // 倒计时,单位s
    let timeCur = 0; // 当前经过的时间,单位s
    const orginClassName = el.className; // 原始class
    const orginText = el.innerText; // 原始文字

    // 设置按钮禁用
    setBtnStatus({
      el,
      timeTotal,
      timeCur,
      orginText,
      className: "ant-btn",
      disabled: true,
    });

    const timer = setInterval(() => {
      timeCur += 1;
      // 动态修改按钮文字
      setBtnText({ el, timeTotal, timeCur, orginText });
      if (timeCur >= timeTotal) {
        // 设置按钮启用
        setBtnStatus({
          el,
          timeTotal,
          timeCur,
          orginText,
          className: orginClassName,
          disabled: false,
        });
        clearInterval(timer);
      }
    }, 1000);
  },
};

// 设置按钮状态参数
declare type BtnStatusOption = BtnTextOption & {
  className: string; // 原始class
  disabled: boolean; // 按钮禁用状态
};

// 设置按钮文字参数
declare type BtnTextOption = {
  el: HTMLButtonElement; // 按钮元素
  timeTotal: number; // 倒计时,单位s
  timeCur: number; // 当前经过的时间,单位s
  orginText: string; // 原始文字
};

最后将指令在 src/directives/index.ts 注册

import { createCtdDirective } from "./ctd.directive"; // 新增这一行
import { App } from "vue";

export const createDirective = {
  install(app: App) {
    // 按钮倒计时指令
    app.directive("ctd", createCtdDirective); // 新增这一行
  },
};

快速使用

<!-- 默认5秒倒计时 -->
<a-button type="primary" v-ctd>确认</a-button>
<!-- 自定义60秒倒计时 -->
<a-button type="primary" v-ctd="60">确认</a-button>

效果如图