前言
el-input-number
饿了么的数字输入框组件,在项目中听常用的。而这个组件比较常用的属性就是精度设置,给组件添加属性precision
。
其实吧,之前一直没怎么研究,保留几位小数就直接填几就好了,比如保留两位小数,那就填2。但是最近遇到了个需求,需要根据设置好的舍入类型进行舍入,还是以保留两位小数为例:
- 舍位,不论第三位小数是几都舍弃
- 进位,不论第三位小数是几都往前进一位
- 四舍五入,第三位小数,按照四舍五入来决定是进位还是舍位
问题
先看下面的图片:
从上图可以看到,当第三位数字小于5时无论是组件本身的chang
事件还是watch
监听事件都不会被触发。当值大于5时才会被触发。另外chang
事件是早于watch
监听的。
解决
解决思路无非就是在这个数值被组件改变之前,按照设置的舍入类型来处理这个数值。
尝试blur事件
无效,拿到的值已经是按照四舍五入之后的值了。并且blur
的事件优先级最低
尝试原生事件
<el-input-number v-model="inputNum" :precision="2" :step="0.1" :max="10"
@change="numberChange" @blur="numberBlue" @input="numInput" v-input />
const vInput = {
mounted(el, binding) {
const inputEl = el.getElementsByClassName('el-input__inner')[0];
// 绑定input事件
console.log(inputEl);
inputEl.addEventListener('input', (event) => {
console.log(event.target.value);
});
}
};
注意:
@input
这个有问题,1是el-input-number
不存在input
事件;2是这个input
事件不知道绑到哪里去了,无法拿到事件对象,拿到的是已经发生变化的值。所以只能通过指令找到input
元素,来添加事件
const numInput = ($event:any) => {
console.log('原生事件_当前值是:', $event);
};
- 点击加减图标,不会触发
input
输入框上的input
事件,这个可以通过提供的属性将两侧的按钮隐藏掉 - 关于语法糖模式下的自定义指令,之前一直没用过。定义指令对象时,对象名必须以
v
开头。否则这个指令无效
舍入类型的处理
const vInput = {
mounted(el, binding) {
const inputEl = el.getElementsByClassName('el-input__inner')[0]
/**
* precision:当前的精度
* type,舍入类型,'0'(四舍五入),'1'(舍位),'2'(进位)
*/
const { precision, type } = binding.value
let newValue = 0
// 输入框默认四舍五入,因此不处理
if (type != 0) {
// 绑定input事件
inputEl.addEventListener('input', event => {
// 将当前数值转成字符串,后面添0防止精度不够
const numStr = (event?.target?.value || 0).toString() + new Array(precision).fill(0).join('')
// 判断是整数还是实数,整数不处理
const pointIndex = numStr.indexOf('.')
if (pointIndex !== -1) {
// 修改这一位数字,舍位时将这位数字改成0,进位时改成9,四舍五入时不处理
const newNum = {
1: '0',
2: '9',
}
newValue = parseFloat(numStr.slice(0, pointIndex + precision + 1) + newNum[type])
// 这里要等输入框失去焦点后再进行赋值
inputEl.addEventListener('blur', event => {
console.log('值:', newValue, precision, newValue.toFixed(precision))
event.target.value = parseFloat(newValue.toFixed(precision))
})
}
})
}
}
};
效果
下面以保留两位小数,舍入类型为进位为例
完整代码
<template>
<div>
<el-input-number v-model="inputNum" :precision="2" :step="0.1" :max="10"
:controls="false"
@change="numberChange" @blur="numberBlue" @input="numInput" v-input="{
precision:2,
type:1
}" />
</div>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue';
const inputNum = ref(0);
watch(
() => inputNum.value,
(val) => {
// console.log('watch监听_值改变了,当前值是:', val);
}
);
const numberChange = (val:number) => {
// console.log('change事件_值改变了,当前值是:', val);
};
const numberBlue = () => {
// console.log('blur事件_当前值:', inputNum.value);
};
const numInput = ($event:any) => {
console.log('原生事件_当前值是:', $event);
};
const vInput = {
mounted(el, binding) {
const inputEl = el.getElementsByClassName('el-input__inner')[0]
/**
* precision:当前的精度
* type,舍入类型,'0'(四舍五入),'1'(舍位),'2'(进位)
*/
const { precision, type } = binding.value
let newValue = 0
// 输入框默认四舍五入,因此不处理
if (type != 0) {
// 绑定input事件
inputEl.addEventListener('input', event => {
// 将当前数值转成字符串,后面添0防止精度不够
const numStr = (event?.target?.value || 0).toString() + new Array(precision).fill(0).join('')
// 判断是整数还是实数,整数不处理
const pointIndex = numStr.indexOf('.')
if (pointIndex !== -1) {
// 修改这一位数字,舍位时将这位数字改成0,进位时改成9,四舍五入时不处理
const newNum = {
1: '0',
2: '9',
}
newValue = parseFloat(numStr.slice(0, pointIndex + precision + 1) + newNum[type])
// 这里要等输入框失去焦点后再进行赋值
inputEl.addEventListener('blur', event => {
console.log('值:', newValue, precision, newValue.toFixed(precision))
event.target.value = parseFloat(newValue.toFixed(precision))
})
}
})
}
}
};
</script>
<style scoped>
</style>