Element-input-输入框千分位格式化

4,601 阅读3分钟

input输入框千分位格式化

项目地址:地址

可以直接访问链接来查看效果

效果图:

录制_2021_12_16_17_38_06_124.gif

要是想要了解input组件的底层逻辑,可以跳到文章末尾

改造开始

因为我们这改造属于对element组件二次改造。

所以我们只能将eui的input组件拉出来,作为业务的自定义组件。

而将eui的组件拉出来,二次修改后作为自定义组件的方式在这篇文章

当我们按照上面的文章,将input组件的代码拉出来,作为业务代码的自定义组件之后,文件结构如下

image.png

input.vue文件就是核心。

el-input组件中的input输入框是没做任何数据绑定的。组件是通过ref去修改这个input输入框的值。然后用户输入值,会触发handleInput事件。

  1. 当用户在原生的input组件中输入值时,会触发hanleInput事件,更新el-input绑定的value
handleInput(event) {
    if (this.isComposing) return;

    if (event.target.value === this.nativeInputValue) return;

    const value = event.target.value;

    // 更新el-input组件绑定的value
    this.$emit('input', value);

    this.$nextTick(() => this.setNativeInputValue());
},
  1. 通过ref去更新原生input的值
setNativeInputValue() {
    // 通过ref获取原生的input组件
    const input = this.getInput();
    if (!input) return;
    if (input.value === this.nativeInputValue) return;
    // 设置原生input组件的值
    input.value = this.nativeInputValue;
},

目标:

  1. 希望原生input组件的值是带千分位的
  2. 但是el-input组件的value不能带千分位 实现步骤:
  3. 在通过ref设置原生input组件的值时,对值进行千分位格式化,比如1000->1,000。
setNativeInputValue() {
    // 通过ref获取原生的input组件
    const input = this.getInput();
    if (!input) return;
    if (input.value === this.isThousandFormat(this.nativeInputValue)) return;
    // 对值进行千分位格式化
    // 将千分位后的值设置给原生input组件
    input.value = this.isThousandFormat(this.nativeInputValue);
},
  1. 当用户在原生的input组件中输入值时,我们将值去掉千分位(即逗号),比如1,000->1000,然后将1000更新给value
handleInput(event) {
        if (this.isComposing) return;

        if (event.target.value === this.nativeInputValue) return;

        // 去除千分位
        const value = this.thousandFormatter ? event.target.value.replace(/,/g, '') : event.target.value;

        // 将值更新给el-input绑定的value
        this.$emit('input', value);

        this.$nextTick(() => this.setNativeInputValue());
  },

异常问题: 因为我们对原生input组件的值进行了强制更改,所以其光标的位置会一直在末尾,我们需要手动设置input组件的光标位置。

手动更新光标位置

我们选择用户在输入框输入值后,也就是handleInput事件里更新光标的位置。

handleInput(event) {
        if (this.isComposing) return;

        if (event.target.value === this.nativeInputValue) return;

        // 去除千分位
        const value = this.thousandFormatter ? event.target.value.replace(/,/g, '') : event.target.value;

        // 将值更新给el-input绑定的value
        this.$emit('input', value);

        // 手动更新光标位置
        this.setCursorPos();
        
        this.$nextTick(() => this.setNativeInputValue());
  },

这个setCursorPos()方法很复杂,不建议各位去理解

// 设置光标所在位置
setCursorPos() {
        const input = this.getInput();
        if (this.thousandFormatter && input) {
          let newPos = 0;
          if (input.selectionStart === 0 && input.selectionEnd === 0) {
          } else {
            // input组件输入完,光标所在的位置
            let cursorPos = input.selectionStart === input.selectionEnd ? input.selectionEnd : 0;
            // 如果存在负号,则光标--
            /^-/.test(input.value) && cursorPos--;
            // 数值的绝对值字符串
            let absolutePart = input.value.replace(/-/g, '');
            // 数值的整数部分(去除,)
            let intergerPart = absolutePart.replace(/,/g, '').split('.')[0];
            // 光标左侧部分(去除,)
            let leftPart = absolutePart.slice(0, cursorPos).replace(/,/g, '');
            // 整数部分长度
            let intergerPartLen = intergerPart.length;
            // 光标左侧部分长度
            let leftPartLen = leftPart.length;
            // 光标如果是在小数点后的话,什么都不干
            if (leftPartLen > intergerPartLen + 1) {
              newPos = cursorPos;
            } else {
              // 标记newPos:光标左侧部分 在 整数部分的 位置
              newPos = intergerPart.indexOf(leftPart) + leftPartLen;
              // 计算整数部分理论上应该有多少个逗号
              let allComasNum = intergerPartLen === 0 ? 0 : intergerPartLen % 3 === 0 ? Math.floor(intergerPartLen / 3) - 1 : Math.floor(intergerPartLen / 3);
              // 光标在整数部分的右侧 的长度
              let rightPartLen = intergerPartLen - leftPartLen;
              // 光标在整数部分的右侧 理论上有多少个逗号
              let rightComasNum = rightPartLen === 0 ? 0 : Math.floor(rightPartLen / 3);
              // 根据整数部分的逗号数,以及右侧部分的逗号数,算出光标应该要移动多少位
              let addComasLen = allComasNum - rightComasNum;
              newPos = newPos + addComasLen;
            }
            // 如果有负号,则newPos++
            /^-/.test(input.value) && newPos++;
          }
          this.$nextTick(() => {
            input.selectionStart = newPos;
            input.selectionEnd = newPos;
          });
        }
  },

然后我们在使用el-input组件时传入thousand-formatter即可

<el-input 
    :thousand-formatter="true" 
    v-model="numberValidateForm.age" 
    autocomplete="off"
>
</el-input>

input组件底层数据流

我做了一个ppt式网页,可以很直观的展示el-input组件的数据流。

点击链接,当页面元素全部消失后,通过不断点击页面来出现元素。

看到最后的看官,希望点个小赞赞。