mirror of
				https://github.com/pure-admin/vue-pure-admin.git
				synced 2025-11-03 13:44:47 +08:00 
			
		
		
		
	feat: 添加长按指令及使用示例,该长按指令支持自定义时长的持续回调 (#620)
This commit is contained in:
		
							parent
							
								
									5f71e0aad7
								
							
						
					
					
						commit
						b8200125dc
					
				@ -68,7 +68,7 @@ menus:
 | 
				
			|||||||
  hsguide: Guide
 | 
					  hsguide: Guide
 | 
				
			||||||
  hsAble: Able
 | 
					  hsAble: Able
 | 
				
			||||||
  hsMenuTree: Menu Tree
 | 
					  hsMenuTree: Menu Tree
 | 
				
			||||||
  hsOptimize: Debounce、Throttle、Copy Directives
 | 
					  hsOptimize: Debounce、Throttle、Copy、Longpress Directives
 | 
				
			||||||
  hsWatermark: Water Mark
 | 
					  hsWatermark: Water Mark
 | 
				
			||||||
  hsPrint: Print
 | 
					  hsPrint: Print
 | 
				
			||||||
  hsDownload: Download
 | 
					  hsDownload: Download
 | 
				
			||||||
 | 
				
			|||||||
@ -68,7 +68,7 @@ menus:
 | 
				
			|||||||
  hsguide: 引导页
 | 
					  hsguide: 引导页
 | 
				
			||||||
  hsAble: 功能
 | 
					  hsAble: 功能
 | 
				
			||||||
  hsMenuTree: 菜单树结构
 | 
					  hsMenuTree: 菜单树结构
 | 
				
			||||||
  hsOptimize: 防抖、截流、复制指令
 | 
					  hsOptimize: 防抖、截流、复制、长按指令
 | 
				
			||||||
  hsWatermark: 水印
 | 
					  hsWatermark: 水印
 | 
				
			||||||
  hsPrint: 打印
 | 
					  hsPrint: 打印
 | 
				
			||||||
  hsDownload: 下载
 | 
					  hsDownload: 下载
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
import { hasAuth } from "@/router/utils";
 | 
					import { hasAuth } from "@/router/utils";
 | 
				
			||||||
import { Directive, type DirectiveBinding } from "vue";
 | 
					import type { Directive, DirectiveBinding } from "vue";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const auth: Directive = {
 | 
					export const auth: Directive = {
 | 
				
			||||||
  mounted(el: HTMLElement, binding: DirectiveBinding) {
 | 
					  mounted(el: HTMLElement, binding: DirectiveBinding) {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
import { message } from "@/utils/message";
 | 
					import { message } from "@/utils/message";
 | 
				
			||||||
import { useEventListener } from "@vueuse/core";
 | 
					import { useEventListener } from "@vueuse/core";
 | 
				
			||||||
import { copyTextToClipboard } from "@pureadmin/utils";
 | 
					import { copyTextToClipboard } from "@pureadmin/utils";
 | 
				
			||||||
import { Directive, type DirectiveBinding } from "vue";
 | 
					import type { Directive, DirectiveBinding } from "vue";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface CopyEl extends HTMLElement {
 | 
					interface CopyEl extends HTMLElement {
 | 
				
			||||||
  copyValue: string;
 | 
					  copyValue: string;
 | 
				
			||||||
 | 
				
			|||||||
@ -1,3 +1,4 @@
 | 
				
			|||||||
export * from "./auth";
 | 
					export * from "./auth";
 | 
				
			||||||
export * from "./copy";
 | 
					export * from "./copy";
 | 
				
			||||||
 | 
					export * from "./longpress";
 | 
				
			||||||
export * from "./optimize";
 | 
					export * from "./optimize";
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										63
									
								
								src/directives/longpress/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								src/directives/longpress/index.ts
									
									
									
									
									
										Normal file
									
								
							@ -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"'
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@ -6,7 +6,7 @@ import {
 | 
				
			|||||||
  throttle
 | 
					  throttle
 | 
				
			||||||
} from "@pureadmin/utils";
 | 
					} from "@pureadmin/utils";
 | 
				
			||||||
import { useEventListener } from "@vueuse/core";
 | 
					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)指令 */
 | 
					/** 防抖(v-optimize或v-optimize:debounce)、节流(v-optimize:throttle)指令 */
 | 
				
			||||||
export const optimize: Directive = {
 | 
					export const optimize: Directive = {
 | 
				
			||||||
 | 
				
			|||||||
@ -13,6 +13,9 @@ const searchFour = ref("");
 | 
				
			|||||||
const searchFive = ref("");
 | 
					const searchFive = ref("");
 | 
				
			||||||
const searchSix = ref("copy");
 | 
					const searchSix = ref("copy");
 | 
				
			||||||
const text = ref("可复制的文本");
 | 
					const text = ref("可复制的文本");
 | 
				
			||||||
 | 
					const long = ref(false);
 | 
				
			||||||
 | 
					const cbText = ref("");
 | 
				
			||||||
 | 
					const idx = ref(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function onInput() {
 | 
					function onInput() {
 | 
				
			||||||
  message(search.value);
 | 
					  message(search.value);
 | 
				
			||||||
@ -30,13 +33,30 @@ function onInputFour() {
 | 
				
			|||||||
function onInputFive({ name, sex }) {
 | 
					function onInputFive({ name, sex }) {
 | 
				
			||||||
  message(`${name}${sex}${searchFive.value}`);
 | 
					  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>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <el-card shadow="never">
 | 
					  <el-card shadow="never">
 | 
				
			||||||
    <template #header>
 | 
					    <template #header>
 | 
				
			||||||
      <div class="card-header">
 | 
					      <div class="card-header">
 | 
				
			||||||
        <span class="font-medium">自定义防抖、截流、文本复制指令</span>
 | 
					        <span class="font-medium">自定义防抖、截流、文本复制、长按指令</span>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </template>
 | 
					    </template>
 | 
				
			||||||
    <div class="mb-2">
 | 
					    <div class="mb-2">
 | 
				
			||||||
@ -113,5 +133,24 @@ function onInputFive({ name, sex }) {
 | 
				
			|||||||
      文本复制指令(自定义触发事件,单击复制)
 | 
					      文本复制指令(自定义触发事件,单击复制)
 | 
				
			||||||
      <span v-copy:click="text" class="text-sky-500">{{ text }}</span>
 | 
					      <span v-copy:click="text" class="text-sky-500">{{ text }}</span>
 | 
				
			||||||
    </div>
 | 
					    </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>
 | 
					  </el-card>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user