记录--解决扫码枪因输入法中文导致的问题
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助
问题
最近公司项目上遇到了扫码枪因搜狗/微软/百度/QQ
等输入法在中文状态下,使用扫码枪扫码会丢失字符的问题
思考
这种情况是由于扫码枪的硬件设备,在输入的时候,是模拟用户键盘的按键
来实现的字符输入的,所以会触发输入法的中文模式,并且也会触发输入法的自动联想。那我们可以针对这个来想解决方案。
方案一
首先想到的第一种方案是,监听keydown
的键盘事件,创建一个字符串数组,将每一个输入的字符进行比对,然后拼接字符串,并回填到输入框中,下面是代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | function onKeydownEvent(e) { this .code = this .code || '' const shiftKey = e.shiftKey const keyCode = e.code const key = e.key const arr = [ 'Q' , 'W' , 'E' , 'R' , 'T' , 'Y' , 'U' , 'I' , 'O' , 'P' , 'A' , 'S' , 'D' , 'F' , 'G' , 'H' , 'J' , 'K' , 'L' , 'Z' , 'X' , 'C' , 'V' , 'B' , 'N' , 'M' , '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' , '0' , '-' ] this .nextTime = new Date().getTime() const timeSpace = this .nextTime - this .lastTime if (key === 'Process' ) { // 中文手动输入 if ( this .lastTime !== 0 && timeSpace <= 30) { for ( const a of arr) { if (keyCode === 'Key' + a) { if (shiftKey) { this .code += a } else { this .code += a.toLowerCase() } this .lastTime = this .nextTime } else if (keyCode === 'Digit' + a) { this .code += String(a) this .lastTime = this .nextTime } } if (keyCode === 'Enter' && timeSpace <= 30) { if (String( this .code)) { // TODO dosomething.... } this .code = '' this .nextTime = 0 this .lastTime = 0 } } } else { if (arr.includes(key.toUpperCase())) { if ( this .lastTime === 0 && timeSpace === this .nextTime) { this .code = key } else if ( this .lastTime !== 0 && timeSpace <= 30) { // 30ms以内来区分是扫码枪输入,正常手动输入时少于30ms的 this .code += key } this .lastTime = this .nextTime } else if (arr.includes(key)) { if ( this .lastTime === 0 && timeSpace === this .nextTime) { this .code = key } else if ( this .lastTime !== 0 && timeSpace <= 30) { this .code += String(key) } this .lastTime = this .nextTime } else if (keyCode === 'Enter' && timeSpace <= 30) { if (String( this .code)) { // TODO dosomething() } this .code = '' this .nextTime = 0 this .lastTime = 0 } else { this .lastTime = this .nextTime } } } |
这种方案能解决部分问题,但是在不同的扫码枪设备,以及不同输入法的情况下,还是会出现丢失问题
方案二
使用input[type=password]
来兼容不同输入的中文模式,让其只能输入英文,从而解决丢失问题
这种方案网上也有不少的参考
# 解决中文状态下扫描枪扫描错误
# input type=password 取消密码提示框
使用password密码框
确实能解决不同输入法的问题,并且Focus
到输入框,输入法会被强制切换为英文模式
添加autocomplete="off"
或 autocomplete="new-password"
属性
官方文档: # 如何关闭表单自动填充
但是在Chromium内核
的浏览器,不支持autocomplete="off"
,并且还是会出现这种自动补全提示:
上面的属性并没有解决浏览器会出现密码补全框,并且在输入字符后,浏览器还会在右上角弹窗提示是否保存:
先解决密码补全框,这里我想到了一个属性readonly
,input原生属性。input[type=password]
在readonly
时,是不会有密码补全的提示,并且也不会弹窗提示密码保存。
那好,我们就可以在输入前
以及输入完成
后,将input[type=password]
立即设置成readonly
但是需要考虑下面几种情况:
- 获取焦点/失去焦点时
- 当前输入框已
focus
时,再次鼠标点击输入框 - 扫码枪输出完成最后,输入
Enter
键时,如果清空输入框,这时候也会显示自动补全 - 清空输入框时
- 切换离开页面时
这几种情况都需要处理,将输入框变成readonly
我用vue
+element-ui
实现了一份,贴上代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | <template> <div class = "scanner-input" > <input class = "input-password" :name= "$attrs.name || 'one-time-code'" type= "password" autocomplete= "off" aria-autocomplete= "inline" :value= "$attrs.value" readonly @input= "onPasswordInput" > <!-- <el-input ref = "scannerInput" v-bind= "$attrs" v- on = "$listeners" @input= "onInput" > --> <el-input ref = "scannerInput" : class = "{ 'input-text': true, 'input-text-focus': isFocus }" v-bind= "$attrs" v- on = "$listeners" > <template v- for = "(_, name) in $slots" v-slot:[name]> <slot :name= "name" ></slot> </template> <!-- <slot slot= "suffix" name= "suffix" ></slot> --> </el-input> </div> </template> <script> export default { name: 'WispathScannerInput' , data() { return { isFocus: false } }, beforeDestroy() { this .$el.firstElementChild.setAttribute( 'readonly' , true ) this .$el.firstElementChild.removeEventListener( 'focus' , this .onPasswordFocus) this .$el.firstElementChild.removeEventListener( 'blur' , this .onPasswordBlur) this .$el.firstElementChild.removeEventListener( 'blur' , this .onPasswordClick) this .$el.firstElementChild.removeEventListener( 'mousedown' , this .onPasswordMouseDown) this .$el.firstElementChild.removeEventListener( 'keydown' , this .oPasswordKeyDown) }, mounted() { this .$el.firstElementChild.addEventListener( 'focus' , this .onPasswordFocus) this .$el.firstElementChild.addEventListener( 'blur' , this .onPasswordBlur) this .$el.firstElementChild.addEventListener( 'click' , this .onPasswordClick) this .$el.firstElementChild.addEventListener( 'mousedown' , this .onPasswordMouseDown) this .$el.firstElementChild.addEventListener( 'keydown' , this .oPasswordKeyDown) const entries = Object.entries( this .$refs.scannerInput) // 解决ref问题 for ( const [key, value] of entries) { if ( typeof value === 'function' ) { this [key] = value } } this [ 'focus' ] = this .$el.firstElementChild.focus.bind( this .$el.firstElementChild) }, methods: { onPasswordInput(ev) { this .$emit( 'input' , ev.target.value) if (ev.target.value === '' ) { this .$el.firstElementChild.setAttribute( 'readonly' , true ) setTimeout(() => { this .$el.firstElementChild.removeAttribute( 'readonly' ) }) } }, onPasswordFocus(ev) { this .isFocus = true setTimeout(() => { this .$el.firstElementChild.removeAttribute( 'readonly' ) }) }, onPasswordBlur() { this .isFocus = false this .$el.firstElementChild.setAttribute( 'readonly' , true ) }, // 鼠标点击输入框一瞬间,禁用输入框 onPasswordMouseDown() { this .$el.firstElementChild.setAttribute( 'readonly' , true ) }, oPasswordKeyDown(ev) { // 判断enter键 if (ev.key === 'Enter' ) { this .$el.firstElementChild.setAttribute( 'readonly' , true ) setTimeout(() => { this .$el.firstElementChild.removeAttribute( 'readonly' ) }) } }, // 点击之后,延迟200ms后放开readonly,让输入框可以输入 onPasswordClick() { if ( this .isFocus) { this .$el.firstElementChild.setAttribute( 'readonly' , true ) setTimeout(() => { this .$el.firstElementChild.removeAttribute( 'readonly' ) }, 200) } }, onInput(_value) { this .$emit( 'input' , _value) }, getList(value) { this .$emit( 'input' , value) } // onChange(_value) { // this.$emit('change', _value) // } } } </script> <style lang= "scss" scoped> .scanner-input { position: relative; height: 36px; width: 100%; display: inline-block; .input-password { width: 100%; height: 100%; border: none; outline: none; padding: 0 16px; font-size: 14px; letter-spacing: 3px; background: transparent; color: transparent; // caret-color: #484848; } .input-text { font-size: 14px; width: 100%; height: 100%; position: absolute; top: 0; left: 0; pointer-events: none; background-color: transparent; ::v-deep .el-input__inner { // background-color: transparent; padding: 0 16px; width: 100%; height: 100%; } } .input-text-focus { ::v-deep .el-input__inner { outline: none; border-color: #1c7af4; } } } </style> |
至此,可以保证input[type=password]
不会再有密码补全提示,并且也不会再切换页面时,会弹出密码保存弹窗。 但是有一个缺点,就是无法完美显示光标
。如果用户手动输入和删除,使用起来会有一定的影响。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
2020-10-08 uni-app开发经验分享六:页面跳转及提示框