全局注册指令
编写指令
/src/directives/tableLoadMore.ts
import type { Directive, DirectiveBinding } from "vue";
interface ElType extends HTMLElement {
copyData: string | number;
__handleClick__: any;
}
// 表格滚动加载指令
export const tableLoadMore: Directive = {
mounted(el: ElType, binding: DirectiveBinding) {
if (typeof binding.value !== "function") {
throw "callback must be a function";
}
if (!binding.arg) {
throw "需要传递给指令table-class参数";
}
console.log(binding,'binding');
const SELECTDROPDOWN_DOM:ElType|null = document.querySelector(`.${binding.arg} .el-scrollbar__wrap`);
SELECTDROPDOWN_DOM?.addEventListener("scroll", function () {
// scrollTop 这里可能因为浏览器缩放存在小数点的情况,导致了滚动到底部时
// scrollHeight 减去滚动到底部时的scrollTop ,依然大于clientHeight 导致无法请求更多数据
// 这里将scrollTop向上取整 保证滚到底部时,触发调用
const CONDITION = this.scrollHeight - Math.ceil(this.scrollTop) <= this.clientHeight;
// el.scrollTop !== 0 当输入时,如果搜索结果很少,以至于没看到滚动条,那么此时的CONDITION计算结果是true,会执行bind.value(),此时不应该执行,否则搜索结果不匹配
if (CONDITION && this.scrollTop !== 0) {
binding.value();
}
});
},
beforeUnmount(el: ElType) {
el.removeEventListener("scroll", el.__handleClick__);
}
};
权限按钮指令
/src/directives/permissionDirective.ts
import store from '@/store'
import type { Directive, DirectiveBinding } from 'vue';
const checkPermission = (el: HTMLElement, binding: DirectiveBinding) => {
const { value } = binding
// const roles= JSON.parse(sessionStorage.getItem('roles'))
const roles = store.getters?.roles
if ( value instanceof Array && value.length) {
const elRoles = value
const hasPermission = roles.some(role =>elRoles.includes(role))
if (!hasPermission) {
// el.parentNode && el.parentNode.removeChild(el))
el.style.display = 'none'
}
} else {
throw new Error(`Directive value should be a non-empty array containing roles`)
}
}
export const permissionDirective: Directive {
mounted: checkPermission,
updated: checkPermission
}
用法1: v-permission:DELETE, 指令里用binding.arg接收
用法2: v-permission="['add']", 指令里用binding.value接收
图片预览自定义指令
/src/directives/previewImageDirective.ts
import type { Directive, DirectiveBinding } from 'vue';
import { h, render } from 'vue';
// import { ElImageViewer } from 'element-plus';
import { Image } from 'ant-design-vue';
export const previewImageDirective: Directive = {
mounted(el: HTMLElement, binding: DirectiveBinding) {
el.style.cursor = 'pointer'; // 设置鼠标样式为指针
const previewBox = document.createElement('div');
previewBox.classList.add('preview-box');
// const vnode = h(ElImageViewer, {
// urlList: [binding.value], // 图片地址
// hideOnClickModal: true, // 允许点击遮罩层关闭
// onClose: () => {
// el.style.cursor = ''; // 恢复鼠标样式
// document.body.removeChild(previewBox);
// },
// });
const vnode = h(Image.Preview, {
visible: false, // 控制预览组件是否可见
onVisibleChange: (visible: boolean) => {
if (!visible) {
el.style.cursor = ''; // 恢复鼠标样式
document.body.removeChild(previewBox);
}
},
src: binding.value, // 图片地址
});
render(vnode, previewBox); // 将 vnode 渲染成 html
document.body.appendChild(previewBox); // 将 html 插入到 body 标签里面
},
}
统一导出
在directives文件夹下建index.ts文件
export * from "./tableLoadMore"; // 表格滚动加载
export * from "./permissionDirective"; // 按钮权限指令
export * from "./previewImageDirective"; // 图片预览指令
全局注册
在main.ts中引入指令的文件, 在app上注册指令
// 自定义指令
import { createApp, Directive } from "vue";
import * as directives from "@/directives";
const app = createApp(App);
Object.keys(directives).forEach(key => {
app.directive(key, (directives as { [key: string]: Directive })[key]);
});
组件内注册指令
使用了script setup
<script setup>
// 在模板中启用 v-focus
const vFocus = { mounted: (el) => el.focus() }
</script>
<template>
<input v-focus />
</template>
未使用script setup
<script>
export default {
setup() {
/*...*/
},
directives: {
// 在模板中启用 v-focus
focus: {
/* ... */
}
}
}
</script>
binding 参数
指令钩子函数
const myDirective = {
// 在绑定元素的 attribute 前
// 或事件监听器应用前调用
created(el, binding, vnode, prevVnode) {
// 下面会介绍各个参数的细节
},
// 在元素被插入到 DOM 前调用
beforeMount(el, binding, vnode, prevVnode) {},
// 在绑定元素的父组件
// 及他自己的所有子节点都挂载完成后调用
mounted(el, binding, vnode, prevVnode) {},
// 绑定元素的父组件更新前调用
beforeUpdate(el, binding, vnode, prevVnode) {},
// 在绑定元素的父组件
// 及他自己的所有子节点都更新后调用
updated(el, binding, vnode, prevVnode) {},
// 绑定元素的父组件卸载前调用
beforeUnmount(el, binding, vnode, prevVnode) {},
// 绑定元素的父组件卸载后调用
unmounted(el, binding, vnode, prevVnode) {}
}