前端使用扫码枪

扫码枪

扫码枪是与键盘功能类似的输入设备,通过扫描条形码,解析条形码编码,随后像键盘一样一个字符一个字符的输入焦点所在位置。


获取扫码枪输入

扫码枪输入效果类似键盘,连续扫码不会清空上一个条形码的编码。

一般会在扫码后默认触发一个Enter键,扫码设备一般也可以设置取消Enter键,具体见设备的说明书。

扫码枪输入速度很快。

1.焦点元素获取扫码结果

焦点元素是指 input,textarea, <el editable />。此类元素在获取扫码枪输入时,有两个关键点:

  1. 保证扫码中途聚焦(且焦点始终在内容结尾)
  2. 每扫一次码,都需要清空上一个条形码的内容。

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();
   		}}
   	/>
几个问题:
  1. 监听输入的事件有 onChange, onKeyDown, onKeyUp,经尝试发现 onKeyUp是最合适的。
    原因在于按下回车的时候,也会触发onChange事件。因此,无法排除这次onChange的调用,会造成获取到的编码拼接混乱。

    onKeyDown则是因为:作为输入设备,监听onKeyDown 在输入shift切换大小写(或者中文输入法的情况下),会出现keyCode是229的情况,
    根据229获取内容(String.fromCharCode(229)), 得到的是 å这样的符号,因此会乱码。

  2. 编码兼容的问题,条形码分为几种,常见的需要支持数字,英文大小写,$ + -符号, 以上这些能满足大部分条形码需求。

  3. 字母上方的数字键,需要按住shift + 按键,又由于采用onKeyUp, 所以这时候 shift的keyCode是出现在按键的keyCode之后的, 因此需要借助另一个属性
    shiftKey。此外类似的还有ctrlKey, altKey, 这些属性表明了按键的时候,是否同时按下了 shift, alt,ctrl键(组合键)。

  4. 纯数字条形码,是可以用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事件,在父级元素或根元素上,区别在于不用聚焦。

posted @ 2022-05-20 11:20  橙云生  阅读(2784)  评论(0编辑  收藏  举报