扫码枪实际是模拟了键盘的输入过程,首先排查输入时扫码枪具体输入了什么内容
编码验证脚本
// 在浏览器控制台运行此代码后扫描测试,或者在页面中执行下方代码后扫描测试
document.addEventListener('keydown', e => {
console.log('Raw input:',
`Char: ${e.key}, Code: ${e.code}, Hex: ${e.key.charCodeAt(0).toString(16)}`
);
});
根据键盘事件日志分析,扫码枪正在使用 Alt+Numpad组合键方式 模拟输入,这是典型的ASCII码输入方式。
index.tsx:13 Raw input: Char: Alt, Code: AltLeft, Hex: 41
31index.tsx:13 Raw input: Char: 3, Code: Numpad3, Hex: 33
31index.tsx:13 Raw input: Char: 1, Code: Numpad1, Hex: 31
31index.tsx:13 Raw input: Char: 5, Code: Numpad5, Hex: 35
31index.tsx:13 Raw input: Char: 0, Code: Numpad0, Hex: 30
31index.tsx:13 Raw input: Char: 8, Code: Numpad8, Hex: 38
32index.tsx:13 Raw input: Char: Alt, Code: AltLeft, Hex: 41
32index.tsx:13 Raw input: Char: 3, Code: Numpad3, Hex: 33
32index.tsx:13 Raw input: Char: 5, Code: Numpad5, Hex: 35
32index.tsx:13 Raw input: Char: 7, Code: Numpad7, Hex: 37
32index.tsx:13 Raw input: Char: 6, Code: Numpad6, Hex: 36
32index.tsx:13 Raw input: Char: 0, Code: Numpad0, Hex: 30
33index.tsx:13 Raw input: Char: Alt, Code: AltLeft, Hex: 41
33index.tsx:13 Raw input: Char: 2, Code: Numpad2, Hex: 32
33index.tsx:13 Raw input: Char: 6, Code: Numpad6, Hex: 36
33index.tsx:13 Raw input: Char: 4, Code: Numpad4, Hex: 34
33index.tsx:13 Raw input: Char: 1, Code: Numpad1, Hex: 31
33index.tsx:13 Raw input: Char: 2, Code: Numpad2, Hex: 32
34index.tsx:13 Raw input: Char: -, Code: Minus, Hex: 2d
71index.tsx:13 Raw input: Char: 0, Code: Digit0, Hex: 30
37index.tsx:13 Raw input: Char: 1, Code: Digit1, Hex: 31
尝试使用Alt+Numpad组合键方式 模拟输入,发现:
在微信中输入ASCII 编码 alt+35760显示中文 记
在网页中输入ASCII 编码 alt+35760显示乱码
出现不同环境显示差异的原因在于 Alt+Numpad输入模式的实现机制:
- 微信:直接使用Windows系统的字符映射表(CP936/GBK)
- 浏览器:遵循Unicode标准处理键盘事件
- 35760的特殊性:实际对应十六进制 0x8BB0(即 "记" 的Unicode码点)
前端解码实现(React示例)
import { useEffect, useRef } from 'react';
/**
* 特殊输入组件 - 处理Alt+Numpad方式的Unicode字符输入
* 主要用于解决扫码枪等设备通过键盘模拟输入Unicode字符的场景
*/
const UnicodeInput = () => {
// 获取输入框的DOM引用
const inputRef = useRef<HTMLInputElement>(null);
// 标记Alt键是否处于按下状态
const altActive = useRef(false);
// 存储通过小键盘输入的数字序列(用于组合Unicode码点)
const codeBuffer = useRef<string[]>([]);
useEffect(() => {
/**
* 处理键盘按下事件
* @param e 键盘事件对象
*/
const handleKeyDown = (e: KeyboardEvent) => {
// 检测左Alt键按下(扫码枪输入通常以Alt键开始)
if (e.key === 'Alt' && e.code === 'AltLeft') {
altActive.current = true; // 标记Alt键已按下
codeBuffer.current = []; // 清空之前的数字缓冲区
e.preventDefault(); // 阻止默认的Alt键行为(如菜单激活)
return;
}
// 当Alt键处于按下状态时,捕获小键盘的数字输入
// 注意:必须使用Numpad数字键(右侧小键盘),主键盘数字键无效
if (altActive.current && e.code.startsWith('Numpad')) {
codeBuffer.current.push(e.key); // 存储按下的数字键
e.preventDefault(); // 阻止默认的数字输入行为
}
};
/**
* 处理键盘释放事件
* @param e 键盘事件对象
*/
const handleKeyUp = (e: KeyboardEvent) => {
// 当Alt键释放时,处理之前记录的数字序列
if (e.key === 'Alt' && altActive.current) {
// 如果缓冲区有数字输入
if (codeBuffer.current.length > 0) {
// 将数字序列组合成十进制Unicode码点
// 例如:输入3,5,7,6,0 → 35760 → "记"的Unicode码点
const charCode = parseInt(codeBuffer.current.join(''));
// 将码点转换为实际字符
// 使用fromCodePoint而不是fromCharCode,支持更大的Unicode范围
const char = String.fromCodePoint(charCode);
// 将字符添加到输入框
if (inputRef.current) {
inputRef.current.value += char;
}
}
// 重置状态
altActive.current = false;
codeBuffer.current = [];
e.preventDefault();
}
};
// 注册全局键盘事件监听
window.addEventListener('keydown', handleKeyDown);
window.addEventListener('keyup', handleKeyUp);
// 组件卸载时清理事件监听
return () => {
window.removeEventListener('keydown', handleKeyDown);
window.removeEventListener('keyup', handleKeyUp);
};
}, []); // 空依赖数组表示只在组件挂载/卸载时执行
// 渲染一个普通的文本输入框
return <input ref={inputRef} type="text" />;
};