目录结构:

- import { App, Directive } from "vue";
- import auth from "./modules/auth";
- import copy from "./modules/copy";
- import waterMarker from "./modules/waterMarker";
- import draggable from "./modules/draggable";
- import debounce from "./modules/debounce";
- import throttle from "./modules/throttle";
- import longpress from "./modules/longpress";
-
- const directivesList: { [key: string]: Directive } = {
- auth,
- copy,
- waterMarker,
- draggable,
- debounce,
- throttle,
- longpress
- };
-
- const directives = {
- install: function (app: App
) { - Object.keys(directivesList).forEach(key => {
- app.directive(key, directivesList[key]);
- });
- }
- };
-
- export default directives;


- /**
- * v-debounce
- * 按钮防抖指令,可自行扩展至input
- * 接收参数:function类型
- */
- import type { Directive, DirectiveBinding } from "vue";
- interface ElType extends HTMLElement {
- __handleClick__: () => any;
- }
- const debounce: Directive = {
- mounted(el: ElType, binding: DirectiveBinding) {
- if (typeof binding.value !== "function") {
- throw "callback must be a function";
- }
- let timer: NodeJS.Timeout | null = null;
- el.__handleClick__ = function () {
- if (timer) {
- clearInterval(timer);
- }
- timer = setTimeout(() => {
- binding.value();
- }, 500);
- };
- el.addEventListener("click", el.__handleClick__);
- },
- beforeUnmount(el: ElType) {
- el.removeEventListener("click", el.__handleClick__);
- }
- };
-
- export default debounce;
- /*
- 需求:防止按钮在短时间内被多次点击,使用节流函数限制规定时间内只能点击一次。
- 思路:
- 1、第一次点击,立即调用方法并禁用按钮,等延迟结束再次激活按钮
- 2、将需要触发的方法绑定在指令上
-
- 使用:给 Dom 加上 v-throttle 及回调函数即可
-
- */
- import type { Directive, DirectiveBinding } from "vue";
- interface ElType extends HTMLElement {
- __handleClick__: () => any;
- disabled: boolean;
- }
- const throttle: Directive = {
- mounted(el: ElType, binding: DirectiveBinding) {
- if (typeof binding.value !== "function") {
- throw "callback must be a function";
- }
- let timer: NodeJS.Timeout | null = null;
- el.__handleClick__ = function () {
- if (timer) {
- clearTimeout(timer);
- }
- if (!el.disabled) {
- el.disabled = true;
- binding.value();
- timer = setTimeout(() => {
- el.disabled = false;
- }, 1000);
- }
- };
- el.addEventListener("click", el.__handleClick__);
- },
- beforeUnmount(el: ElType) {
- el.removeEventListener("click", el.__handleClick__);
- }
- };
-
- export default throttle;
- /**
- * v-copy
- * 复制某个值至剪贴板
- * 接收参数:string类型/Ref
类型/Reactive类型 - */
- import type { Directive, DirectiveBinding } from "vue";
- import { ElMessage } from "element-plus";
- interface ElType extends HTMLElement {
- copyData: string | number;
- __handleClick__: any;
- }
- const copy: Directive = {
- mounted(el: ElType, binding: DirectiveBinding) {
- el.copyData = binding.value;
- el.addEventListener("click", handleClick);
- },
- updated(el: ElType, binding: DirectiveBinding) {
- el.copyData = binding.value;
- },
- beforeUnmount(el: ElType) {
- el.removeEventListener("click", el.__handleClick__);
- }
- };
-
- function handleClick(this: any) {
- const input = document.createElement("input");
- input.value = this.copyData.toLocaleString();
- document.body.appendChild(input);
- input.select();
- document.execCommand("Copy");
- document.body.removeChild(input);
- ElMessage({
- type: "success",
- message: "复制成功"
- });
- }
-
- export default copy;
- /**
- * v-longpress
- * 长按指令,长按时触发事件
- */
- import type { Directive, DirectiveBinding } from "vue";
-
- const directive: Directive = {
- mounted(el: HTMLElement, binding: DirectiveBinding) {
- if (typeof binding.value !== "function") {
- throw "callback must be a function";
- }
- // 定义变量
- let pressTimer: any = null;
- // 创建计时器( 2秒后执行函数 )
- const start = (e: any) => {
- if (e.button) {
- if (e.type === "click" && e.button !== 0) {
- return;
- }
- }
- if (pressTimer === null) {
- pressTimer = setTimeout(() => {
- handler(e);
- }, 1000);
- }
- };
- // 取消计时器
- const cancel = () => {
- if (pressTimer !== null) {
- clearTimeout(pressTimer);
- pressTimer = null;
- }
- };
- // 运行函数
- const handler = (e: MouseEvent | TouchEvent) => {
- binding.value(e);
- };
- // 添加事件监听器
- el.addEventListener("mousedown", start);
- el.addEventListener("touchstart", start);
- // 取消计时器
- el.addEventListener("click", cancel);
- el.addEventListener("mouseout", cancel);
- el.addEventListener("touchend", cancel);
- el.addEventListener("touchcancel", cancel);
- }
- };
-
- export default directive;
- /*
- 需求:实现一个拖拽指令,可在父元素区域任意拖拽元素。
- 思路:
- 1、设置需要拖拽的元素为absolute,其父元素为relative。
- 2、鼠标按下(onmousedown)时记录目标元素当前的 left 和 top 值。
- 3、鼠标移动(onmousemove)时计算每次移动的横向距离和纵向距离的变化值,并改变元素的 left 和 top 值
- 4、鼠标松开(onmouseup)时完成一次拖拽
- 使用:在 Dom 上加上 v-draggable 即可
-
- */
- import type { Directive } from "vue";
- interface ElType extends HTMLElement {
- parentNode: any;
- }
- const draggable: Directive = {
- mounted: function (el: ElType) {
- el.style.cursor = "move";
- el.style.position = "absolute";
- el.onmousedown = function (e) {
- let disX = e.pageX - el.offsetLeft;
- let disY = e.pageY - el.offsetTop;
- document.onmousemove = function (e) {
- let x = e.pageX - disX;
- let y = e.pageY - disY;
- let maxX = el.parentNode.offsetWidth - el.offsetWidth;
- let maxY = el.parentNode.offsetHeight - el.offsetHeight;
- if (x < 0) {
- x = 0;
- } else if (x > maxX) {
- x = maxX;
- }
-
- if (y < 0) {
- y = 0;
- } else if (y > maxY) {
- y = maxY;
- }
- el.style.left = x + "px";
- el.style.top = y + "px";
- };
- document.onmouseup = function () {
- document.onmousemove = document.onmouseup = null;
- };
- };
- }
- };
- export default draggable;
- /*
- 需求:给整个页面添加背景水印。
- 思路:
- 1、使用 canvas 特性生成 base64 格式的图片文件,设置其字体大小,颜色等。
- 2、将其设置为背景图片,从而实现页面或组件水印效果
-
- 使用:设置水印文案,颜色,字体大小即可
-
- */
-
- import type { Directive, DirectiveBinding } from "vue";
- const addWaterMarker: Directive = (str: string, parentNode: any, font: any, textColor: string) => {
- // 水印文字,父元素,字体,文字颜色
- let can: HTMLCanvasElement = document.createElement("canvas");
- parentNode.appendChild(can);
- can.width = 205;
- can.height = 140;
- can.style.display = "none";
- let cans = can.getContext("2d") as CanvasRenderingContext2D;
- cans.rotate((-20 * Math.PI) / 180);
- cans.font = font || "16px Microsoft JhengHei";
- cans.fillStyle = textColor || "rgba(180, 180, 180, 0.3)";
- cans.textAlign = "left";
- cans.textBaseline = "Middle" as CanvasTextBaseline;
- cans.fillText(str, can.width / 10, can.height / 2);
- parentNode.style.backgroundImage = "url(" + can.toDataURL("image/png") + ")";
- };
-
- const waterMarker = {
- mounted(el: DirectiveBinding, binding: DirectiveBinding) {
- addWaterMarker(binding.value.text, el, binding.value.font, binding.value.textColor);
- }
- };
-
- export default waterMarker;
- /**
- * v-auth
- * 按钮权限指令 (根据需求而定)
- */
- import { useAuthStore } from "@/stores/modules/auth";
- import type { Directive, DirectiveBinding } from "vue";
-
- const auth: Directive = {
- mounted(el: HTMLElement, binding: DirectiveBinding) {
- const { value } = binding;
- const authStore = useAuthStore();
- const currentPageRoles = authStore.authButtonListGet[authStore.routeName] ?? [];
- if (value instanceof Array && value.length) {
- const hasPermission = value.every(item => currentPageRoles.includes(item));
- if (!hasPermission) el.remove();
- } else {
- if (!currentPageRoles.includes(value)) el.remove();
- }
- }
- };
-
- export default auth;
- // 自定义指令,点击旋转 v-rotate
- const rotate = {
- beforeMount(el: any) {
- el.addEventListener("click", function () {
- console.log(el.style.transform);
-
- el.style.transition = "all 0.3s";
- if (el.style.transform) {
- let str = el.style.transform;
- let deg = str.substring(str.indexOf("(") + 1, str.indexOf("d"));
- el.style.transform = `rotate(${Number(deg) + 180}deg)`;
- } else {
- el.style.transform = "rotate(180deg)";
- }
- });
- }
- };
-
- export default rotate;