vue 项目中遇到的常用自定义指令

1.el-input 输入框,只能输入数字,并且可限制上下限,保留小数位数

  • 1.输入的不是数字失去焦点时值会被清空【包括科学计数法 e 也不能输入】
  • 2.可配置输入值的范围,超出范围给出提示并清空
  • 3.可配置小数位数
import { Message } from 'element-ui';
// 判断值是否为空,注意:0是有值的
function isNUll(val) {
  return val === null || val === void 0 || val === '' || val.toString() === 'NaN';
}

/**
 * author: yf
 * date: 20241212
 * 对input type="number" 类型输入框的扩展,
 * 1.输入的不是数字失去焦点时值会被清空【包括科学计数法 e 也不能输入】
 * 2.可配置输入值的范围,超出范围给出提示并清空
 * 3.可配置小数位数
 *
 * 注意搭配 type="number" 使用效果最佳
 * 使用方式:
 *
 * 方式一:使用默认配置,只是限制了输入的只能是数字【包括e也不能输入】,不然会被清空
 * v-number-input-extend
 *
 * 方式二:带参数,注意: 所有参数都是可选
 * v-number-input-extend = "{
      label: 'xxx输入框', // 这个输入框的label值,用于提示
      min: 0, // 输入值的最小值 - 不传则不会处理
      max: 100, // 输入值的最大值 - 不传则不会处理
      toFixed: 2, // 值的小数位数 - 不传则不会处理
      isPrompt: true, // 不满足验证条件时是否提示,比如最大最小值的验证
      isClearZero: false // 值为0时,是否置为空 - 不传则不会处理
 * }"
 */
Vue.directive('number-input-extend', {
  inserted(el, binding, vnode) {
    // 配置
    let {
      label = '', // 这个输入框的label值,用于提示
      min, // 输入值的最小值
      max, // 输入值的最大值
      toFixed, // 值的小数位数
      isPrompt = true, // 不满足验证条件时是否提示,比如最大最小值的验证
      isClearZero = false // 值为0时,是否置为空 - 某些场景会用到
    } = binding.value || {};

    let $input = vnode.componentInstance;
    // 设置输入框的值,触发input事件,改变v-model绑定的值
    const setVal = val => {
      if ($input) {
        // 如果是自定义组件就触发自定义组件的input事件
        $input.$emit('input', val);
      } else {
        // 如果是原生组件就触发原生组件的input事件
        el.value = val;
        el.dispatchEvent(new Event('input'));
      }
    };

    el._customData = {
      blur: event => {
        const e = event || window.event;
        // 去掉非数字字符
        let strVal = String(e.target.value || '').replace(/[^\-\d.]/g, '')
        let val = parseFloat(strVal);
        if (isNUll(val)) {
          val = '';
        }
        if (val !== '' && !isNUll(toFixed)) {
          val = Number(val.toFixed(toFixed));
        }

        // 值为0时,置为空
        if (isClearZero && val === 0) {
          val = '';
        }

        // #region 值范围判断
        if (val !== '') {
          if (!isNUll(min) && !isNUll(max)) {
            if (val < min || val > max) {
              val = '';
              isPrompt &&
                Message({
                  message: `${label}值的范围应该是${min}-${max}`,
                  type: 'warning'
                });
            }
          } else if (!isNUll(min)) {
            if (val < min) {
              val = '';
              isPrompt &&
                Message({
                  message: `${label}值的最小值为${min}`,
                  type: 'warning'
                });
            }
          } else if (!isNUll(max)) {
            if (val > max) {
              val = '';
              isPrompt &&
                Message({
                  message: `${label}值的最大值为${max}`,
                  type: 'warning'
                });
            }
          }
        }
        // #endregion 值范围判断

        e.target.value = val;
        setVal(val);
      }
    };
    el.addEventListener('focusout', el._customData.blur);
  },
  /* eslint-disable no-unused-vars */
  unbind(el) {
    // 解绑
    if (el._customData && el._customData.blur) return;
    el.removeEventListener('focusout', el._customData.blur);
  }
});

2.div 盒子宽高变化触发resize回调

/**
 * div resize
 * author: yf
 */
Vue.directive('resize', {
  bind: function(el, binding) {
    let callbackFn = binding.value; // div resize后的回调函数
    if (!callbackFn) return;

    let t = binding.arg; // arg:传给指令的参数,可选。例如 v-resize:60 中,参数为 "60"
    t = t === void 0 ? 100 : Number(t); // 防抖时间

    // console.log(callbackFn, t)

    let resizeBox = el;
    let domInfo = {
      width: 0,
      height: 0
    };
    let resizeObserver = new ResizeObserver(
      debounce(() => {
        let oldDomInfo = domInfo;
        let newDomInfo = (domInfo = getDomWH(el));
        try {
          // console.log('div resize ....');
          callbackFn &&
            callbackFn({
              resizeBox: el, // 当前 resize 的dom信息
              oldDomInfo, // resize 前的宽高信息
              newDomInfo // resize 后的宽高信息
            });
        } catch (e) {
          // console.error('div resize', e);
        }
      }, t)
    ); // 会在绘制前和布局后调用 resize 事件,因此不用提前调用 event_resize 方法

    el._vResize = resizeObserver;
    resizeObserver.observe(resizeBox);
  },
  unbind: function(el, binding) {
    let callbackFn = binding.value; // 回调函数
    if (!callbackFn) return;

    let resizeBox = el;
    let resizeObserver = el._vResize;
    if (!resizeObserver) return;
    resizeObserver.unobserve(resizeBox);
    delete el._vResize;
  }
});

3 input focus

/**
 * author: yf
 * date: 20250109
 * input focus
 * 注意:在 el-table 行编辑时使用此指令可能不会生效,因为 el-table 存在fixed列时,focus的是fixed table 中的 input
 * */
Vue.directive('input-focus', {
  inserted: function (el) {
    if(el.nodeName.toLocaleLowerCase() === 'input'){
      el.focus()
    }else{
      let input = el.querySelector('input')
      input && input.focus()
    }
  }
});

4 el-table 内某个el-input 的 td 挂载时focus

给el-input 绑定了 blur 事件时可能用到这个指令,因为如果没触发focus是不会触发 blur 事件的

// 组件内自定义指令
  directives: {
    'tableIn-elInput-focus': {
      // 指令的定义
      inserted: function (el, binding, vnode) {
        try {
          let tableInx = vnode.context.$refs['MaterialTable'] // 这个el-table 节点是绑定了ref的, ref =“MaterialTable”
          if(!tableInx || !tableInx.$el) return
          // 因为el-table 表格在表格列 fixed 的时候会创建一模一样的节点,有以下操作为了正确定位节点:
          let parentDom = tableInx.$el.querySelector('.el-table__body-wrapper')
          if(parentDom && parentDom!== el && parentDom.contains(el)){
            el.querySelector('.el-input__inner').focus()
          }
        }catch(e){
          console.error('tableIn-focus', e)
        }
      }
    }
  },

若对你有帮助,请点个赞吧,若能打赏不胜感激,谢谢支持!
本文地址:http://08643.cn/p/6af71018f716,转载请注明出处,谢谢。

最后编辑于
?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,992评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,212评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事?!?“怎么了?”我有些...
    开封第一讲书人阅读 159,535评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,197评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,310评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,383评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,409评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,191评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,621评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,910评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,084评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,763评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,403评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,083评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,318评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,946评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,967评论 2 351