前端使用扫码枪
扫码枪
扫码枪是与键盘功能类似的输入设备,通过扫描条形码,解析条形码编码,随后像键盘一样一个字符一个字符的输入焦点所在位置。
获取扫码枪输入
扫码枪输入效果类似键盘,连续扫码不会清空上一个条形码的编码。
一般会在扫码后默认触发一个Enter键
,扫码设备一般也可以设置取消Enter键
,具体见设备的说明书。
扫码枪输入速度很快。
1.焦点元素获取扫码结果
焦点元素是指 input
,textarea
, <el editable />
。此类元素在获取扫码枪输入时,有两个关键点:
- 保证扫码中途聚焦(且焦点始终在内容结尾)
- 每扫一次码,都需要清空上一个条形码的内容。
以antd
中的 Input
为例:
此外全局变量应该定义在组件外,或者用ref存起来。
let typeVal = '';
let lastTime = 0;
组件如下:
const scanRef = useRef();
const [scanVal, setScanVal] = useState('');
const handlekeyDown = (e) => {
let nextTime = 0;
let keyCode = e.keyCode || e.which || e.charCode;
let shift = e.shiftKey;
nextTime = new Date().getTime();
// console.log('keyUp',keyCode, shift)
if(keyCode == 229){
return
}
//字母上方 数字键0-9 对应键码值 48-57; 数字键盘 数字键0-9 对应键码值 96-105
if ((keyCode >= 48 && keyCode <= 57) || (keyCode >= 96 && keyCode <= 105)) {
let codes = {
'48': 48,
'49': 49,
'50': 50,
'51': 51,
'52': 52,
'53': 53,
'54': 54,
'55': 55,
'56': 56,
'57': 57,
'96': 48,
'97': 49,
'98': 50,
'99': 51,
'100': 52,
'101': 53,
'102': 54,
'103': 55,
'104': 56,
'105': 57,
};
keyCode = codes[keyCode];
nextTime = new Date().getTime();
}
lastTime = nextTime;
if (keyCode == 13) {
let code = typeVal.trim()
setScanVal(code);
typeVal = '';
handlerCode(code)
return;
}
if (keyCode !== 16) {
let val = getValFromKeyCode(keyCode, shift);
if (nextTime && lastTime && nextTime - lastTime > 300) {
typeVal = val;
} else {
typeVal += val;
}
}
};
const getValFromKeyCode = (keyCode, shift) => {
if (keyCode == 189) {
return '-';
}
if (keyCode == 187 || keyCode == 107) {
return '+';
}
if (shift && keyCode == 52) {
return '$';
}
return String.fromCharCode(keyCode);
};
const handlerCode = (code) => {
//此处为获取到的编码,可以做接口操作
}
<Input
ref={scanRef}
value={scanVal}
placeholder="在此处扫码"
onKeyUp={handlekeyUp}
//聚焦
onBlur={() => {
scanRef.current.input.focus();
}}
/>
几个问题:
-
监听输入的事件有
onChange
,onKeyDown
,onKeyUp
,经尝试发现onKeyUp
是最合适的。
原因在于按下回车的时候,也会触发onChange
事件。因此,无法排除这次onChange
的调用,会造成获取到的编码拼接混乱。onKeyDown
则是因为:作为输入设备,监听onKeyDown
在输入shift
切换大小写(或者中文输入法的情况下),会出现keyCode是229的情况,
根据229获取内容(String.fromCharCode(229)
), 得到的是å
这样的符号,因此会乱码。 -
编码兼容的问题,条形码分为几种,常见的需要支持数字,英文大小写,
$ + -
符号, 以上这些能满足大部分条形码需求。 -
字母上方的数字键,需要按住
shift + 按键
,又由于采用onKeyUp
, 所以这时候 shift的keyCode是出现在按键的keyCode之后的, 因此需要借助另一个属性
shiftKey
。此外类似的还有ctrlKey
,altKey
, 这些属性表明了按键的时候,是否同时按下了shift
,alt
,ctrl
键(组合键)。 -
纯数字条形码,是可以用
onChange
来处理的。
全局变量:
let beforeScanValue = '';
let flag = false;
组件内:
const [scanVal, setScanVal] = useState('');
const [scanValBak, setScanValBak] = useState('');
const scanValueChange = (e) => {
let { value } = e.target;
value = value.replace(/\'/g, '');
if (!value) {
return;
}
if (beforeScanValue) {
if (beforeScanValue !== value) {
value = value.substring(beforeScanValue.length);
}
}
if (flag) {
flag = false;
setScanValBak(value);
return;
}
if (value.includes(beforeScanValue)) {
value = value.replace(beforeScanValue, '');
}
setScanValBak((scanVal) => {
return scanVal + value;
});
};
const enterSearch = (e) => {
// eslint-disable-next-line prefer-const
let inputValue = scanValBak; // e.target.value;
beforeScanValue = inputValue;
flag = true;
setScanValBak('');
setScanVal(inputValue);
console.log('========');
console.log(inputValue, 'enter');
console.log('========');
if (inputValue) {
getItemByScanCode(inputValue);
}
};
<Input
ref={scanRef}
value={scanVal}
style={{ width: '280px' }}
placeholder="请在此处使用扫码枪扫描设备二维码"
onPressEnter={enterSearch}
onChange={scanValueChange}
onBlur={() => {
scanRef.current.input.focus();
}}
/>
2.非焦点元素获取扫码结果
非焦点元素只能监听onKeyUp
事件,在父级元素或根元素上,区别在于不用聚焦。