Vue Formulate数字输入限制小数位数异常问题求助
问题描述
需求:限制输入框仅允许小数位数不超过2位的数字(如10.12、10.1、10.00、10均合法,10.123非法)。基于vue-formulate实现时采用截取小数点后位数的方式处理,出现异常:
- 输入
10.11后,第一次输入2会被正常拦截; - 再次输入
2时,输入框会显示10.112,超出限制; - 输入其他数字(如
3)则不会出现该问题。
当前代码:
<FormulateInput name="height" type="number" step=".01" placeholder="0.0" v-model="height" :value="getHeightValue" :disabled="disableHeight" @input="updateField('height', $event)" min="0" />
updateField(field, value) { const numberOfDecimal = (field === 'height' || field === 'depth') ? 3 : 2 this[field] = (value.indexOf(".") >= 0) ? (value.substr(0, value.indexOf(".")) + value.substr(value.indexOf("."), numberOfDecimal)) : value; },
原因分析
你的推测完全正确:第二次输入相同数字时updateField未触发,本质是vue-formulate的v-model同步逻辑问题:
- 第一次输入
2后,updateField将值修正为10.11,输入框DOM值同步为该值; - 再次输入
2时,输入框DOM值短暂变为10.112,但由于修正后的模型值还是10.11,vue-formulate判断「模型值未变化」,因此不触发@input事件,修正逻辑不执行,非法值留在输入框; - 输入不同数字时,模型值会产生变化,触发
@input执行修正,因此无异常。
另外注意:代码中numberOfDecimal给height/depth设为3,和需求的「最多2位小数」逻辑冲突,是潜在问题。
解决方案
方案1:修正截取逻辑+强制同步输入框值
直接在修正模型值后,强制更新输入框DOM值,避免重复输入时的同步遗漏:
updateField(field, value) { // 按需求统一设置最多2位小数 const maxDecimal = 2; let correctedValue = value; if (value?.indexOf(".") >= 0) { // substr第二个参数是长度,需+1(包含小数点本身) correctedValue = value.substr(0, value.indexOf(".")) + value.substr(value.indexOf("."), maxDecimal + 1); } // 强制赋值更新模型 this[field] = correctedValue; // 强制同步输入框DOM值 this.$nextTick(() => { const inputEl = document.querySelector(`[name="${field}"]`); if (inputEl) inputEl.value = correctedValue; }); }
方案2:改用vue-formulate自定义验证规则
利用组件原生验证能力,比手动截取更符合设计逻辑,也避免DOM操作:
- 全局/局部注册验证规则:
Vue.use(VueFormulate, { rules: { maxDecimal2: ({ value }) => { if (!value) return true; // 匹配最多2位小数的数字 return /^\d+(\.\d{1,2})?$/.test(value.toString()); } } });
- 修改组件配置:
<FormulateInput name="height" type="text" // 改用text类型,避免number类型自动格式化干扰 placeholder="0.0" v-model="height" :disabled="disableHeight" min="0" validation="maxDecimal2" validation-message="请输入最多2位小数的数字" />
方案3:双事件监听+优化截取逻辑
同时监听@input和@change事件,确保所有输入场景都触发修正:
<FormulateInput name="height" type="number" step=".01" placeholder="0.0" v-model="height" :value="getHeightValue" :disabled="disableHeight" @input="updateField('height', $event)" @change="updateField('height', $event)" min="0" />
优化截取逻辑:
updateField(field, value) { const maxDecimal = 2; if (value?.indexOf(".") >= 0) { const [integerPart, decimalPart] = value.split("."); const trimmedDecimal = decimalPart.slice(0, maxDecimal); this[field] = `${integerPart}.${trimmedDecimal}`; } else { this[field] = value; } }
关键注意点
- 原代码中
numberOfDecimal的取值与需求冲突,必须修正; number类型输入框存在浏览器兼容性和自动格式化问题,追求精准控制时推荐用text类型配合正则验证;- 重复输入相同数字时,vue响应式系统可能认为值未变化,需通过强制同步DOM或多事件监听解决。
内容的提问来源于stack exchange,提问作者Serenity
相关产品推荐
相关产品推荐

