input输入框千分位格式化
项目地址:地址
可以直接访问链接来查看效果
效果图:
要是想要了解input组件的底层逻辑,可以跳到文章末尾
改造开始
因为我们这改造属于对element组件二次改造。
所以我们只能将eui的input组件拉出来,作为业务的自定义组件。
而将eui的组件拉出来,二次修改后作为自定义组件的方式在这篇文章
当我们按照上面的文章,将input组件的代码拉出来,作为业务代码的自定义组件之后,文件结构如下
input.vue文件就是核心。
el-input组件中的input输入框是没做任何数据绑定的。组件是通过ref去修改这个input输入框的值。然后用户输入值,会触发handleInput事件。
- 当用户在原生的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());
},
- 通过ref去更新原生input的值
setNativeInputValue() {
// 通过ref获取原生的input组件
const input = this.getInput();
if (!input) return;
if (input.value === this.nativeInputValue) return;
// 设置原生input组件的值
input.value = this.nativeInputValue;
},
目标:
- 希望原生input组件的值是带千分位的
- 但是el-input组件的value不能带千分位 实现步骤:
- 在通过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);
},
- 当用户在原生的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组件的数据流。
点击链接,当页面元素全部消失后,通过不断点击页面来出现元素。