diff --git a/locales/en.yaml b/locales/en.yaml index 96a4ccbea..9eed96fcc 100644 --- a/locales/en.yaml +++ b/locales/en.yaml @@ -68,6 +68,7 @@ menus: hsguide: Guide hsAble: Able hsMenuTree: Menu Tree + hsOptimize: Debounce、Throttle、Copy Directives hsWatermark: Water Mark hsPrint: Print hsDownload: Download diff --git a/locales/zh-CN.yaml b/locales/zh-CN.yaml index 8cb2f1584..eb34b895e 100644 --- a/locales/zh-CN.yaml +++ b/locales/zh-CN.yaml @@ -68,6 +68,7 @@ menus: hsguide: 引导页 hsAble: 功能 hsMenuTree: 菜单树结构 + hsOptimize: 防抖、截流、复制指令 hsWatermark: 水印 hsPrint: 打印 hsDownload: 下载 diff --git a/src/directives/auth/index.ts b/src/directives/auth/index.ts index 627ea896e..62ca87c2e 100644 --- a/src/directives/auth/index.ts +++ b/src/directives/auth/index.ts @@ -7,7 +7,9 @@ export const auth: Directive = { if (value) { !hasAuth(value) && el.parentNode?.removeChild(el); } else { - throw new Error("need auths! Like v-auth=\"['btn.add','btn.edit']\""); + throw new Error( + "[Directive: auth]: need auths! Like v-auth=\"['btn.add','btn.edit']\"" + ); } } }; diff --git a/src/directives/copy/index.ts b/src/directives/copy/index.ts new file mode 100644 index 000000000..11db44618 --- /dev/null +++ b/src/directives/copy/index.ts @@ -0,0 +1,33 @@ +import { message } from "@/utils/message"; +import { useEventListener } from "@vueuse/core"; +import { copyTextToClipboard } from "@pureadmin/utils"; +import { Directive, type DirectiveBinding } from "vue"; + +interface CopyEl extends HTMLElement { + copyValue: string; +} + +/** 文本复制指令(默认双击复制) */ +export const copy: Directive = { + mounted(el: CopyEl, binding: DirectiveBinding) { + const { value } = binding; + if (value) { + el.copyValue = value; + const arg = binding.arg ?? "dblclick"; + // Register using addEventListener on mounted, and removeEventListener automatically on unmounted + useEventListener(el, arg, () => { + const success = copyTextToClipboard(el.copyValue); + success + ? message("复制成功", { type: "success" }) + : message("复制失败", { type: "error" }); + }); + } else { + throw new Error( + '[Directive: copy]: need value! Like v-copy="modelValue"' + ); + } + }, + updated(el: CopyEl, binding: DirectiveBinding) { + el.copyValue = binding.value; + } +}; diff --git a/src/directives/index.ts b/src/directives/index.ts index 97ccf7649..5e81e0657 100644 --- a/src/directives/index.ts +++ b/src/directives/index.ts @@ -1 +1,3 @@ export * from "./auth"; +export * from "./copy"; +export * from "./optimize"; diff --git a/src/directives/optimize/index.ts b/src/directives/optimize/index.ts new file mode 100644 index 000000000..43ac38ac8 --- /dev/null +++ b/src/directives/optimize/index.ts @@ -0,0 +1,55 @@ +import { + isFunction, + isObject, + isArray, + debounce, + throttle +} from "@pureadmin/utils"; +import { useEventListener } from "@vueuse/core"; +import { Directive, type DirectiveBinding } from "vue"; + +/** 防抖(v-optimize或v-optimize:debounce)、节流(v-optimize:throttle)指令 */ +export const optimize: Directive = { + mounted(el: HTMLElement, binding: DirectiveBinding) { + const { value } = binding; + const optimizeType = binding.arg ?? "debounce"; + const type = ["debounce", "throttle"].find(t => t === optimizeType); + if (type) { + if (value && value.event && isFunction(value.fn)) { + let params = value?.params; + if (params) { + if (isArray(params) || isObject(params)) { + params = isObject(params) ? Array.of(params) : params; + } else { + throw new Error( + "[Directive: optimize]: `params` must be an array or object" + ); + } + } + // Register using addEventListener on mounted, and removeEventListener automatically on unmounted + useEventListener( + el, + value.event, + type === "debounce" + ? debounce( + params ? () => value.fn(...params) : value.fn, + value?.timeout ?? 200, + value?.immediate ?? false + ) + : throttle( + params ? () => value.fn(...params) : value.fn, + value?.timeout ?? 1000 + ) + ); + } else { + throw new Error( + "[Directive: optimize]: `event` and `fn` are required, and `fn` must be a function" + ); + } + } else { + throw new Error( + "[Directive: optimize]: only `debounce` and `throttle` are supported" + ); + } + } +}; diff --git a/src/router/modules/able.ts b/src/router/modules/able.ts index aaca0bdf5..35e5bef0f 100644 --- a/src/router/modules/able.ts +++ b/src/router/modules/able.ts @@ -10,6 +10,15 @@ export default { rank: able }, children: [ + { + path: "/able/directives", + name: "Directives", + component: () => import("@/views/able/directives.vue"), + meta: { + title: $t("menus.hsOptimize"), + extraIcon: "IF-pure-iconfont-new svg" + } + }, { path: "/able/watermark", name: "WaterMark", diff --git a/src/views/able/directives.vue b/src/views/able/directives.vue new file mode 100644 index 000000000..f3e566536 --- /dev/null +++ b/src/views/able/directives.vue @@ -0,0 +1,117 @@ + + +