1. 前言
今天测试小姐姐给我提了一个bug,我打开一看一脸懵逼:‘??? 这产品也没提这个需求啊’
大致效果:
一个售价区间的input框:只能输入正整数,不能超过8位数,错误的字符要让他输入不了。
换而言之:不能输入中文,不能输入1.1,不能输入特殊字符。
我第一反应本来是想拒绝的,又想了下今天bug不多,功能也比较简单,把这个解决了也没啥~~~
于是,有了这篇文章的诞生(踩坑之旅开始)。
2. 效果展示
老规矩,先上成品展示
3. 先排除type = 'number'
<el-input
v-model="state.formData.num"
maxlength="10"
style="width: 200px"
type="number"
/>
实际使用过程中,发现不符合测试的要求,如下:
-
maxlength不生效
-
可以输入e
-
可以输入-
4. 再排除el-input-number组件
-
不想用这个组件~~~
-
组件左右自带+-护法,不想费工夫去掉这两dom
-
可以输入-和. (虽然失焦后会移除)
5. 使用自定义指令
遇到上面两个问题后,我第一想法是使用个全局的自定义指令。
这样,不仅全局可用,而且使用方法很简单,直接<inputv-model='xxx' v-inputNumber />就行了
5.1 第一版代码
<el-input
v-model="state.formData.num"
v-inputNumber="10"
type="number"
/>
export default {
mounted(el, binding, vnode) {
el.addEventListener('input', (e) => {
let { value } = e.target
let maxwordlength = bidding.value
let integer = parseInt(value)
if (!isNaN(integer)) {
e.target.value = Math.abs(integer)
if (maxwordlength) {
e.target.value = e.target.value.slice(0, maxwordlength)
}
// 触发input的change事件
e.target.dispatchEvent(new Event('input'))
} else {
e.target.value = ''
// 触发input的change事件
e.target.dispatchEvent(new Event('input'))
}
// }
})
},
};
实现思路:
-
通过addEventListener监听el的input事件实时得到用户输入的数据
-
通过parseInt自动去除用户输入的
.(比如1.1.1得到111) -
通过isNaN判断是否包含特殊字符,对数据执行处理
-
通过dispatchEvent执行input事件把处理后的数据透传给对应input的value
如上图,在多次测试后发现了一个bug
在输入中文后,虽然会被拦截成功,但是再输入正常的数字后,对应的v-model绑定的值不更新了~~~
此时,已经花了我一个小时的宝贵时间了
在那无语半天后,想了下,可能是中文+英文两种输入情况下,破坏了input事件处理逻辑里的什么东西,所以想着把中文单独来一波处理,于是有了第二版
5.2 第二版代码
export default {
mounted(el, binding, vnode) {
el.addEventListener('compositionend', (e) => {
console.log("🚀 ~ handleCompositionEnd ~ this.composing = false", e)
let { value } = e.target;
const regex = /['\u4e00-\u9fa5]/;
if (regex.test(value)) {
e.target.value = ''
// 触发input的change事件
e.target.dispatchEvent(new Event('input'));
}
});
el.addEventListener('input', (e) => {
let { value } = e.target;
let maxwordlength = bidding.value
const regex = /['\u4e00-\u9fa5]/;
if (!regex.test(value)) {
let integer = parseInt(value);
if (!isNaN(integer)) {
console.log("🚀 ~ el.addEventListener ~ integer:", integer, maxWordLength)
e.target.value = Math.abs(integer)
if (maxWordLength) {
e.target.value = e.target.value.slice(0, maxWordLength);
}
// 触发input的change事件
e.target.dispatchEvent(new Event('input'));
} else {
e.target.value = ''
// 触发input的change事件
e.target.dispatchEvent(new Event('input'));
}
}
});
},
};
实现思路:
-
在第一版的基础上,通过compositionend增加了劫持中文输入的逻辑
-
对中文场景下的输入数据单独进行dispatchEvent
嘿!你猜怎么着,不行!!!~~~ 依旧没达到我想要的效果
此时,差不多花了我快两小时了
到这里,耐心差不多花光了,毕竟这bug不在这次迭代的需求中
但是,不甘心啊... 都写到这里了
耐下心想,可能问题出在dispatchEvent上,处理后的数据没成功更新到input的value
于是,想到了常规的父子通信的传值方式,而自定义指令的第三个参数是VNode
所以第三版,也就是最终版出来了
5.3 最终版代码
export default {
mounted(el, bidding, vnode) {
el.addEventListener('input', (e) => {
let { value } = e.target
let maxWordLength = bidding.value
const regex = /['\u4e00-\u9fa5]/
if (!regex.test(value)) {
let integer = parseInt(value)
if (!isNaN(integer)) {
console.log(
'🚀 ~ el.addEventListener ~ integer:',
integer,
maxWordLength
)
e.target.value = Math.abs(integer)
if (maxWordLength) {
e.target.value = e.target.value.slice(0, maxWordLength)
}
vnode.ctx.emit('update:modelValue', e.target.value)
} else {
e.target.value = ''
vnode.ctx.emit('update:modelValue', e.target.value)
}
} else {
e.target.value = ''
vnode.ctx.emit('update:modelValue', e.target.value)
}
})
},
}
实现思路:
- 在第一版的基础上,通过vnode.ctx.emit取代了dispatchEvent更新数据方式
经过多次测试,效果如预期~~~
此时,基本花了我快一个下午的宝贵时间
6. 完结
好啦,大功告成啦~~~
这个功能属于看着有点简单,但做起来有点费事的那种,小伙伴工作如果遇到类似功能,可以直接CV大法,多出来的时间又可以摸鱼啦~~~
这篇文章我尽力把我的笔记和想法放到这了,希望对小伙伴有帮助。
欢迎转载,但请注明来源。
最后,希望小伙伴们给我个免费的点赞,祝大家心想事成,平安喜乐。