From e63963cf0891ac9e930e8e9585b46e7608b9b156 Mon Sep 17 00:00:00 2001 From: chen shu tao <2096679224@qq.com> Date: Wed, 23 Nov 2022 13:40:09 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E9=98=B2=E6=8A=96=E8=8A=82=E6=B5=81?= =?UTF-8?q?=E7=AD=89=E6=8C=87=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/directives/copy.ts | 34 +++++++++++++++++++++++ src/directives/debounce.ts | 31 +++++++++++++++++++++ src/directives/draggable.ts | 49 +++++++++++++++++++++++++++++++++ src/directives/longpress.ts | 49 +++++++++++++++++++++++++++++++++ src/directives/throttle.ts | 41 ++++++++++++++++++++++++++++ src/plugins/directives.ts | 12 +++++++++ src/router/modules/test.ts | 31 +++++++++++++++++++++ src/views/test/index.vue | 54 +++++++++++++++++++++++++++++++++++++ 8 files changed, 301 insertions(+) create mode 100644 src/directives/copy.ts create mode 100644 src/directives/debounce.ts create mode 100644 src/directives/draggable.ts create mode 100644 src/directives/longpress.ts create mode 100644 src/directives/throttle.ts create mode 100644 src/router/modules/test.ts create mode 100644 src/views/test/index.vue diff --git a/src/directives/copy.ts b/src/directives/copy.ts new file mode 100644 index 0000000..e888828 --- /dev/null +++ b/src/directives/copy.ts @@ -0,0 +1,34 @@ +/** + * v-copy + * 复制某个值至剪贴板 + * 接收参数:string类型/Ref类型/Reactive类型 + */ +import type { Directive, DirectiveBinding } from 'vue'; +import { useMessage } from 'naive-ui'; +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); + console.log('复制成功', this.copyData); +} + +export default copy; diff --git a/src/directives/debounce.ts b/src/directives/debounce.ts new file mode 100644 index 0000000..931092d --- /dev/null +++ b/src/directives/debounce.ts @@ -0,0 +1,31 @@ +/** + * 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; diff --git a/src/directives/draggable.ts b/src/directives/draggable.ts new file mode 100644 index 0000000..65f0cca --- /dev/null +++ b/src/directives/draggable.ts @@ -0,0 +1,49 @@ +/* + 需求:实现一个拖拽指令,可在父元素区域任意拖拽元素。 + + 思路: + 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; diff --git a/src/directives/longpress.ts b/src/directives/longpress.ts new file mode 100644 index 0000000..e12d0ed --- /dev/null +++ b/src/directives/longpress.ts @@ -0,0 +1,49 @@ +/** + * 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; diff --git a/src/directives/throttle.ts b/src/directives/throttle.ts new file mode 100644 index 0000000..3050166 --- /dev/null +++ b/src/directives/throttle.ts @@ -0,0 +1,41 @@ +/* + 需求:防止按钮在短时间内被多次点击,使用节流函数限制规定时间内只能点击一次。 + + 思路: + 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; diff --git a/src/plugins/directives.ts b/src/plugins/directives.ts index 6643915..6323b1b 100644 --- a/src/plugins/directives.ts +++ b/src/plugins/directives.ts @@ -1,6 +1,10 @@ import { App } from 'vue'; import { permission } from '@/directives/permission'; +import copy from '@/directives/copy'; +import debounce from '@/directives/debounce'; +import throttle from '@/directives/throttle'; +import draggable from '@/directives/draggable'; /** * 注册全局自定义指令 @@ -9,4 +13,12 @@ import { permission } from '@/directives/permission'; export function setupDirectives(app: App) { // 权限控制指令(演示) app.directive('permission', permission); + // 复制指令 + app.directive('copy', copy); + // 防抖指令 + app.directive('debounce', debounce); + // 节流指令 + app.directive('throttle', throttle); + // 拖拽指令 + app.directive('draggable', draggable); } diff --git a/src/router/modules/test.ts b/src/router/modules/test.ts new file mode 100644 index 0000000..c721060 --- /dev/null +++ b/src/router/modules/test.ts @@ -0,0 +1,31 @@ +import { RouteRecordRaw } from 'vue-router'; +import { Layout } from '@/router/constant'; +import { ProjectOutlined } from '@vicons/antd'; +import { renderIcon } from '@/utils/index'; + +const routes: Array = [ + { + path: '/test', + name: 'test', + component: Layout, + meta: { + sort: 12, + isRoot: true, + activeMenu: 'test_index', + icon: renderIcon(ProjectOutlined), + }, + children: [ + { + path: 'index', + name: `test_index`, + meta: { + title: '指令测试', + activeMenu: 'test_index', + }, + component: () => import('@/views/test/index.vue'), + }, + ], + }, +]; + +export default routes; diff --git a/src/views/test/index.vue b/src/views/test/index.vue new file mode 100644 index 0000000..a216b4f --- /dev/null +++ b/src/views/test/index.vue @@ -0,0 +1,54 @@ + + + + +