问题描述:
elementUI的数字输入框组件el-input-number可以通过 :precision 属性设置精度,从而控制小数位数。但是产品给出的小数位数需求如下:
- 当数字是整数或者一位小数时,小数位自动补零,保持两位小数展示;
- 当数字是三位小数时,展示为三位小数;
- 当数字为四位小数时,展示为四位小数;
- 当数字超过四位小数时,四舍五入展示为四位小数。
解决方案:
上述逻辑要求el-input-number组件不仅需要正确回写出数字的小数位,且能根据用户输入的数字动态变更el-input-number的精度,即precision 属性值。
考虑到项目中所有使用el-input-number的地方都需要改造成上述逻辑,所以决定采用自定义指令实现此动态精度的功能。这样不仅易于维护和扩展,且不容易和原先el-input-number组件上已绑定的事件及逻辑发生耦合。
代码:
1. 计算数字小数位数(精度)函数代码片
// 返回精度结果
export const calcPrecision = (inputNum, precision = 4) => {
// 当inputNum为undefined或中文等特殊字符时,直接输出空字符串
if (isNaN(Number(inputNum))) return '';
const roundTo = (num, precision = 4) => {
const factor = Math.pow(10, precision);
return Math.round(num * factor) / factor;
} // 计算出保留4位小数且四舍五入后的结果
let num = roundTo(inputNum, precision);
let pricisionNum;
const numString = num.toString();
const decimalIndex = numString.indexOf('.');
let decimalPlaces = 0;
if (decimalIndex !== -1) { // 当数字是小数时
decimalPlaces = numString.length - decimalIndex - 1; // 返回小数位数
}
if (decimalPlaces < 2) {
pricisionNum = 2; // 整数或不足两位小数,小数位数补为2位
} else {
pricisionNum = decimalPlaces;
}
return pricisionNum;
};
2. 自定义指令代码片
import Vue from 'vue';
import { calcPrecision } from ./utils'
// 注册一个数字输入框精度自定义指令 `v-precision`
Vue.directive('precision', {
componentUpdated: function (el) {
Vue.nextTick(()=>{
/* 项目中部分el-input-number组件的value会有一些逻辑联动变化,
因此获取value的逻辑写在nextTick里面,保证获取的value是最新的*/
const inputEl = el.getElementsByClassName('el-input__inner')[0]
const value = inputEl.value
let num = calcPrecision(value) // 计算出当前el-input-number组件小数精度
el.__vue__.precision = num // 赋值最新精度给el-input-number组件
})
},
bind: function (el) {
el.addEventListener('input', function () {
const inputEl = el.getElementsByClassName('el-input__inner')[0]
const value = inputEl.value
let num = calcPrecision(value)
el.__vue__.precision = num
})
},
unbind: function (el) {
el.removeEventListener('input', ()=>{});
}
})
注意点及总结:
在上面自定义指令中,bind钩子是修改el-input-number组件数值时触发的,但是需要注意el-input-number组件初始化回写数据的时候,也需要根据数值计算出相应的精度,因此还需要componentUpdated钩子,保证首次渲染el-input-number组件时精度展示正确。
写在最后: 使用 el._vue_.precision = num 赋值在本地环境控制台中会触发强制赋值修改父组件的报错问题,因为线上环境不报错就暂时没有处理这个问题。如果读者知道解决办法,或者有更好的实现el-input-number动态精度的方案欢迎分享。