浏览代码

feat: 添加长按指令及使用示例,该长按指令支持自定义时长的持续回调 (#620)

xiaoming 1 年之前
父节点
当前提交
b8200125dc

+ 1 - 1
locales/en.yaml

@@ -68,7 +68,7 @@ menus:
   hsguide: Guide
   hsAble: Able
   hsMenuTree: Menu Tree
-  hsOptimize: Debounce、Throttle、Copy Directives
+  hsOptimize: Debounce、Throttle、Copy、Longpress Directives
   hsWatermark: Water Mark
   hsPrint: Print
   hsDownload: Download

+ 1 - 1
locales/zh-CN.yaml

@@ -68,7 +68,7 @@ menus:
   hsguide: 引导页
   hsAble: 功能
   hsMenuTree: 菜单树结构
-  hsOptimize: 防抖、截流、复制指令
+  hsOptimize: 防抖、截流、复制、长按指令
   hsWatermark: 水印
   hsPrint: 打印
   hsDownload: 下载

+ 1 - 1
src/directives/auth/index.ts

@@ -1,5 +1,5 @@
 import { hasAuth } from "@/router/utils";
-import { Directive, type DirectiveBinding } from "vue";
+import type { Directive, DirectiveBinding } from "vue";
 
 export const auth: Directive = {
   mounted(el: HTMLElement, binding: DirectiveBinding) {

+ 1 - 1
src/directives/copy/index.ts

@@ -1,7 +1,7 @@
 import { message } from "@/utils/message";
 import { useEventListener } from "@vueuse/core";
 import { copyTextToClipboard } from "@pureadmin/utils";
-import { Directive, type DirectiveBinding } from "vue";
+import type { Directive, DirectiveBinding } from "vue";
 
 interface CopyEl extends HTMLElement {
   copyValue: string;

+ 1 - 0
src/directives/index.ts

@@ -1,3 +1,4 @@
 export * from "./auth";
 export * from "./copy";
+export * from "./longpress";
 export * from "./optimize";

+ 63 - 0
src/directives/longpress/index.ts

@@ -0,0 +1,63 @@
+import { useEventListener } from "@vueuse/core";
+import type { Directive, DirectiveBinding } from "vue";
+import { subBefore, subAfter, isFunction } from "@pureadmin/utils";
+
+export const longpress: Directive = {
+  mounted(el: HTMLElement, binding: DirectiveBinding) {
+    const cb = binding.value;
+    if (cb && isFunction(cb)) {
+      let timer = null;
+      let interTimer = null;
+      let num = 500;
+      let interNum = null;
+      const isInter = binding?.arg?.includes(":") ?? false;
+
+      if (isInter) {
+        num = Number(subBefore(binding.arg, ":"));
+        interNum = Number(subAfter(binding.arg, ":"));
+      } else if (binding.arg) {
+        num = Number(binding.arg);
+      }
+
+      const clear = () => {
+        if (timer) {
+          clearTimeout(timer);
+          timer = null;
+        }
+        if (interTimer) {
+          clearInterval(interTimer);
+          interTimer = null;
+        }
+      };
+
+      const onDownInter = (ev: PointerEvent) => {
+        ev.preventDefault();
+        if (interTimer === null) {
+          interTimer = setInterval(() => cb(), interNum);
+        }
+      };
+
+      const onDown = (ev: PointerEvent) => {
+        clear();
+        ev.preventDefault();
+        if (timer === null) {
+          timer = isInter
+            ? setTimeout(() => {
+                cb();
+                onDownInter(ev);
+              }, num)
+            : setTimeout(() => cb(), num);
+        }
+      };
+
+      // Register using addEventListener on mounted, and removeEventListener automatically on unmounted
+      useEventListener(el, "pointerdown", onDown);
+      useEventListener(el, "pointerup", clear);
+      useEventListener(el, "pointerleave", clear);
+    } else {
+      throw new Error(
+        '[Directive: longpress]: need callback and callback must be a function! Like v-longpress="callback"'
+      );
+    }
+  }
+};

+ 1 - 1
src/directives/optimize/index.ts

@@ -6,7 +6,7 @@ import {
   throttle
 } from "@pureadmin/utils";
 import { useEventListener } from "@vueuse/core";
-import { Directive, type DirectiveBinding } from "vue";
+import type { Directive, DirectiveBinding } from "vue";
 
 /** 防抖(v-optimize或v-optimize:debounce)、节流(v-optimize:throttle)指令 */
 export const optimize: Directive = {

+ 40 - 1
src/views/able/directives.vue

@@ -13,6 +13,9 @@ const searchFour = ref("");
 const searchFive = ref("");
 const searchSix = ref("copy");
 const text = ref("可复制的文本");
+const long = ref(false);
+const cbText = ref("");
+const idx = ref(0);
 
 function onInput() {
   message(search.value);
@@ -30,13 +33,30 @@ function onInputFour() {
 function onInputFive({ name, sex }) {
   message(`${name}${sex}${searchFive.value}`);
 }
+
+function onLongpress() {
+  long.value = true;
+}
+function onCustomLongpress() {
+  long.value = true;
+}
+function onCbLongpress() {
+  idx.value += 1;
+  long.value = true;
+  cbText.value = `持续回调${idx.value}次`;
+}
+function onReset() {
+  long.value = false;
+  cbText.value = "";
+  idx.value = 0;
+}
 </script>
 
 <template>
   <el-card shadow="never">
     <template #header>
       <div class="card-header">
-        <span class="font-medium">自定义防抖、截流、文本复制指令</span>
+        <span class="font-medium">自定义防抖、截流、文本复制、长按指令</span>
       </div>
     </template>
     <div class="mb-2">
@@ -113,5 +133,24 @@ function onInputFive({ name, sex }) {
       文本复制指令(自定义触发事件,单击复制)
       <span v-copy:click="text" class="text-sky-500">{{ text }}</span>
     </div>
+
+    <el-divider />
+    <el-space wrap>
+      长按指令
+      <el-button v-longpress="onLongpress">长按(默认500ms)</el-button>
+      <el-button v-longpress:1000="onCustomLongpress">
+        自定义长按时长(1000ms)
+      </el-button>
+      <el-button v-longpress:2000:200="onCbLongpress">
+        2秒后每200ms持续回调
+      </el-button>
+      <el-button @click="onReset"> 重置状态 </el-button>
+      <el-tag :type="long ? 'success' : 'info'" class="ml-2" size="large">
+        {{ long ? "当前为长按状态" : "当前非长按状态" }}
+      </el-tag>
+      <el-tag v-if="cbText" type="danger" class="ml-2" size="large">
+        {{ cbText }}
+      </el-tag>
+    </el-space>
   </el-card>
 </template>