当前案例全部是vue3+TS,vue2的可以根据自己情况修改
1.v-copy指令
文本内容复制指令
// copy.ts
import { App, Directive } from "vue";
const copy: Directive<HTMLElement> = {
beforeMount(el, binding){
if(binding.modifiers.ref && binding.value){// 如果是要复制指定ref元素的数据
// 交给mounted里处理
}else {// 复制当前元素下的文字
if(binding.modifiers.dblclick){ //如果是双击复制
el.addEventListener('dblclick', () => handlerClick(el.innerText));
}else{//单击复制
el.addEventListener('click', () => handlerClick(el.innerText));
el.style.cursor = 'copy';
}
}
},
mounted(el, binding){
const refElem = binding.instance?.$refs[binding.value] as HTMLElement | undefined;
if(!refElem){// 如果不存在这个元素
return;
}
if(binding.modifiers.dblclick){// 双击复制,注意这里的双击复制跟上面的不是同一个元素
el.addEventListener('dblclick', () => handlerClick(refElem.innerText));
}else{//单击复制
el.addEventListener('click', () => handlerClick(refElem.innerText));
}
}
}
const handlerClick = (txt: string) => {
//创建元素
if(!document.getElementById('copyTarget')){// 如果不存在该元素
const copyTarget = document.createElement('input');
copyTarget.setAttribute('style', 'position:fixed;top:0;left:0;opacity:0;z-index:-1000;');
copyTarget.setAttribute('id', 'copyTarget');
document.body.appendChild(copyTarget);
}
//复制内容
const input = document.getElementById('copyTarget') as HTMLInputElement;
input.value = txt;
input.select();
document.execCommand('copy');
input.remove();
//execCommand已被弃用,可考虑navigator.clipboard.writeText(txt).then(() => {console.log('复制成功')})
}
export default {
install(app: App<Element>){
app.directive('copy', copy);
}
}
/*
main.ts使用
app.use(copy);
*/
指令 | 说明 | 实例 |
v-copy | 单击复制 | <button v-copy>单击我,文本会被复制</button> |
v-copy.dblclick | 双击复制当前节点下的文本数据 | <button v-copy>双击我,文本会被复制</button> |
v-copy.ref="'ref'" | 单击之后复制节点名为ref的文本数据 | <p ref="ref">我将会被复制</p> <button v-copy.ref="'ref'">单击我,复制ref节点文本</button> |
v-.copy.ref.dblclick="'ref'" | 双击之后复制节点名为ref的文本数据 | <p ref="ref">我将会被复制</p> <button v-copy.ref="'ref'">双击我,复制ref节点文本</button> |
2.v-screenfull指令
全屏指令,需提前引用screenfull包(screenfull - npm)
import { App, Directive } from "vue";
import screenfull, { Screenfull } from "screenfull";
const screen: Directive<HTMLElement> = {
beforeMount(el, binding){
let iconName = binding.value || 'el-icon-full-screen'; // 默认使用element-plus的图标
if(binding.modifiers.icon) { // 添加icon
const i = document.createElement('i');
i.setAttribute('style', 'margin-left: 5px;');
i.setAttribute('class', iconName);
el.appendChild(i);
}
el.style.cursor = el.style.cursor || 'pointer';
el.addEventListener('click', handleClick);
}
}
const handleClick = () => {
if(!screenfull.isEnabled) {
alert('您的浏览器不支持全屏');
return;
}
(screenfull as Screenfull).toggle();
}
export default {
install(app: App<Element>){
app.directive('screenfull', screen);
}
}
指令 | 说明 | 实例 |
v-screenfull | 不使用图标 | <button v-screenfull>切换全屏</button> |
v-screenfull.icon | 使用默认图标 | <div v-screenfull.icon>切换全屏</div> |
v-screenfull.icon="'xxx'" | 使用xxx的图标 | <div v-screenfull.icon="'xxx'">切换全屏</div> |
3.v-ellipsis指令
文字超出显示省略号
import { App, Directive } from "vue";
const ellipsis: Directive<HTMLElement> = {
mounted(el, binding){
el.style.whiteSpace = 'nowrap'
el.style.overflow = 'hidden';
el.style.textOverflow = 'ellipsis';
let width = '0';
if(binding.arg){ //如果存在自定义的宽度
width = binding.arg;
}else{// 否则拿取父级的宽度
const parent = el.parentElement;
if(parent){// 存在父元素
if(binding.modifiers.flex){// 如果是flex布局
parent.style.overflow = 'hidden';
}
width = parent.offsetWidth.toString();
}else{//不存在父元素,默认100,一般不会进入这里
width = '100';
}
}
el.style.width = width + 'px';
}
}
export default {
install(app: App<Element>){
app.directive('ellipsis', ellipsis);
}
}
指令 | 说明 | 实例 |
v-ellipsis | 以父元素为宽度 | <p v-ellipsis>超出文本隐藏</p> |
v-ellipsis:200 | 超出200px隐藏 | <div v-ellipsis:200>超出文本隐藏</div> |
v-ellipsis.flex | 如果父级使用了flex用这个 | <div v-ellipsis.flex>超出文本隐藏</div> |
4.v-drag指令
使用后可拖拽当前元素,注意父元素必须使用了绝对定位,当前元素使用了相对定位
import { App, Directive } from "vue";
const drag: Directive<HTMLElement> = {
mounted(el) {
const parent = el.parentElement; // 获取父级元素
if(!parent){
return;
}
const minX = 0;// 最小x方向距离
const minY = 0;// 最小y方向距离
const maxX = parent.offsetWidth;// 最大x方向距离
const maxY = parent.offsetHeight;// 最大y方向距离
document.onselectstart = () => {
return false;
}
el.style.cursor = 'move';
el.onmousedown = (e) => {
const left = e.clientX - el.offsetLeft;
const top = e.clientY - el.offsetTop;
document.onmousemove = (de) => {
let l = de.clientX - left;
let t = de.clientY - top;
// 限制不能超出父级区域
if(l <= minX) {
l = minX;
}
if(t <= minY) {
t = minY;
}
if(l >= maxX - el.offsetWidth) {
l = maxX - el.offsetWidth;
}
if(t >= maxY - el.offsetHeight) {
t = maxY - el.offsetHeight;
}
el.style.left = l + 'px';
el.style.top = t + 'px';
}
document.onmouseup = () => {
document.onmousemove = document.onmouseup = null;
}
return false;
}
}
}
export default {
install(app: App<Element>){
app.directive('drag', drag);
}
}
指令 | 说明 | 实例 |
v-drag | 无 | <div v-drag>拖拽我</div> |
5.v-format指令
格式化数字,可保留指定小数位,转成千分制
import { App, Directive, DirectiveBinding, VNode } from "vue";
const format: Directive<HTMLElement> = {
beforeMount(el, binding, vnode) {
formatCallBack(el, binding, vnode);
},
updated(el, binding, vnode) {
formatCallBack(el, binding, vnode);
}
}
const formatCallBack = (el: HTMLElement, binding: DirectiveBinding<any>, vnode: VNode<any, HTMLElement>) => {
let oldVal = vnode.children;
let val = Number(oldVal);
if(isNaN(val)) {
throw '值必须为数字类型';
}
const value = Number(binding.value);
const fixedNum = isNaN(value) ? 2 : value;
el.innerText = val.toFixed(fixedNum);
if(binding.modifiers.price) {
el.innerText = formatNumber(el.innerText);
}
}
const formatNumber = (num: string) => {
let str = num.split('.');
let x1 = str[0];
let x2 = str.length > 1 ? '.' + str[1] : '';
var rgx = /(\d+)(\d{3})/;
while (rgx.test(x1)) {
x1 = x1.replace(rgx, '$1' + ',' + '$2');
}
return x1 + x2;
}
export default {
install(app: App<Element>) {
app.directive('format', format);
}
}
指令 | 说明 | 实例 |
v-format | 默认保留两位小数 | <div v-format>25.123456</div> |
v-format="4" | 保留指定小数 | <div v-format="4">25.123456</div> |
v-format.price | 保留两位小数,并格式化为千分制 | <div v-format.price>2525525.123456</div> |
v-format.price="1" | 保留一位小数,并格式化为千分制 | <div v-format.price="1">225255.123456</div> |
6.v-debounce指令
防抖指令,避免重复点击
import { App, Directive } from "vue";
const debounce: Directive<HTMLElement> = {
mounted(el, binding) {
if(typeof binding.value !== 'function') {
throw '回调必须是函数';
}
const arg = Number(binding.arg);
let timer: NodeJS.Timeout;
el.addEventListener('click', () => {
if(timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
binding.value()
}, isNaN(arg) ? 500 : arg);
})
}
}
export default {
install(app: App<Element>) {
app.directive('debounce', debounce);
}
}
指令 | 说明 | 实例 |
v-debounce="callBack" | 默认延迟500ms | <button v-debounce="callBack">防抖</button> |
v-debounce:200="xxx" | 设置延迟为200ms | <button v-debounce:200="xxx">防抖</button> |