记录–解决扫码枪因输入法中文导致的问题

  • 记录–解决扫码枪因输入法中文导致的问题已关闭评论
  • 104 次浏览
  • A+
所属分类:Web前端
摘要

最近公司项目上遇到了扫码枪因搜狗/微软/百度/QQ等输入法在中文状态下,使用扫码枪扫码会丢失字符的问题


这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

记录--解决扫码枪因输入法中文导致的问题

问题

最近公司项目上遇到了扫码枪因搜狗/微软/百度/QQ等输入法在中文状态下,使用扫码枪扫码会丢失字符的问题

思考

这种情况是由于扫码枪的硬件设备,在输入的时候,是模拟用户键盘的按键来实现的字符输入的,所以会触发输入法的中文模式,并且也会触发输入法的自动联想。那我们可以针对这个来想解决方案。

方案一

首先想到的第一种方案是,监听keydown的键盘事件,创建一个字符串数组,将每一个输入的字符进行比对,然后拼接字符串,并回填到输入框中,下面是代码:

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实现了一份,贴上代码:

<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]不会再有密码补全提示,并且也不会再切换页面时,会弹出密码保存弹窗。 但是有一个缺点,就是无法完美显示光标。如果用户手动输入和删除,使用起来会有一定的影响。

本文转载于:

https://juejin.cn/post/7265666505102524475

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

 记录--解决扫码枪因输入法中文导致的问题