一、解码页的能力面比文案窄得多
拿一张自家打印的 EAN-13 位图去条形码解码页,版面上出现的不是码文,而是「当前浏览器不支持条形码识别」那一行小字。
模板一共 195 行,外部脚本 0 个,这条链上没有第二套引擎兜底。第 101 行的 'BarcodeDetector' in window 是唯一判定,本机 headless Chrome 实测 false,第 102–122 行整段解码因此不可达,只剩第 172–175 行那块不支持的面板。
为排除图像本身的问题,我用站内条形码生成器现画一张合法的 EAN-13(5901234123457,实测图 95×40 像素)喂进同一个入口,输出 0 个字符。这张图校验位合法、扫码枪认,浏览器一侧却一个字都给不出。
「扫描」也不存在:模板里 getUserMedia、video、capture 都是 0 次,第 15 行只有一个 type="file" 的静态上传框。
二、校验位是从 12 位算出来的
EAN-13 的第 13 位是模 10 加权:生成器第 188 行按索引偶数位 ×1、奇数位 ×3 求和,第 190 行取 (10 - sum % 10) % 10。实测 '590123412345' sum=83 → 7、'999999999999' sum=216 → 4。
图 1:校验位如何从 12 位算出,以及一个被放行的错码
三、给满 13 位,一位都不重算
「明明算了校验位却对不上」的困惑,根子多半在生成端。生成器第 184–194 行只在 length === 12 的分支里算校验位,给满 13 位直接落进空分支:实测 6/6 组错校验位原样成图,'5901234123456' 应为 7、'4006381333930' 应为 1、'9999999999999' 应为 4,一个错都不报。超过 13 位时生成器第 192–193 行直接截断,而截断在补位之后,超长输入同样不会触发重算。图能出来只证明数字被画了出来,不证明它自己前后一致。
四、UPC-A 要补一个 0 才等价
两种码制的权重方向差一位,换算动作只有一个:11 位 UPC-A 数据前面补一个 0,再走 encodeEAN13。实测 10/10 组与 UPC-A 自身口径同校验位,'04210000526'→4、'01234567890'→5 都落在这一条上。
坑是已经带末位的 12 位 UPC-A:原样当作 EAN-13 的 12 位数据喂进去,末位被重算成另一位,'012345678905' 变成 '0123456789050'。同一串数字在两种口径下合法与非法互串,前面那种「对不上」多半就是这个。
五、CODE128 是模 103
换成模 103 就对不上前面那套:第 116–117 行取的起始码 104 同时是校验和初值,第 123 行按位置权重 (i+1) 累加,第 126 行做 checksum % 103。实测 'HELLO123' sum=1141 → 8、'abc' sum=502 → 90。
这张模式表本身还是坏的:patterns 数组实测长度 184,而 CODE128 只需要 107 项;0..102 里只有 79 种不同图形,24 组被两个码值共用;终止码 106 落在与码值 7 完全相同的 11 模块图形上,标准终止符应是 13 模块。后果是同图不同文——'CS' 与 'DS' 的 bit 串逐位相同、都是 55 模块,穷举 1296 个两字符输入只得到 1292 种图形,这一族上生成到解码的闭环并不成立。
六、CODE39 没有校验位,还吞字符
生成器第 229–257 行没有一次求和、也没有一次取模,只在首尾各加一个 '*' 定界符。生成器第 230 行先 toUpperCase,'abc' 与 'ABC' 的 bit 串完全相同;表外字符更安静,第 249 行直接丢弃,encodeCode39('A_B') 与 encodeCode39('AB') 同为 51 模块,'UU工具箱' 只剩 51 模块的 UU。
图 2:三种码制的校验口径对照——模 10、模 103、无校验
七、卡片全量渲染,复制只取第一条
第 133 行的 forEach 全量渲染,实测传 4 个码得到 4 张卡片、margin-top:10px 出现 3 次;但第 159 行只把 codes[0].rawValue 写进 dataset.result,第 2、3、4 个码看得见、复制不到。第 165 行「未识别」与第 174 行「不支持」又都不清 dataset.result,面板换成「不支持」后旧值仍在,第 177–182 行 copyResult 照旧把它送进剪贴板。
八、三种码制的口径并排
| 码制 | 校验口径 | 实测与后果 |
|---|---|---|
| EAN-13 | 模 10 加权 | sum=83 → 7;给满 13 位不重算 |
| UPC-A | 补一个前导 0 后同 EAN-13 | '01234567890' → 5;12 位原样喂会重算末位 |
| CODE128 | 模 103,权重 (i+1) | 'HELLO123' sum=1141 → 8;模式表只有 79 种图形 |
| CODE39 | 没有校验位 | 'A_B' 与 'AB' 同为 51 模块 |
边界很清楚:条码能自证的只有「这串数字自己前后一致」。模板里 checksum、verify、校验、商品、真伪五个词都是 0 次,第 134 行只把 rawValue 原样取值,没重算校验位,也就无从判定背后那件商品是不是真的。
先把码制、位数和该不该带校验位对上——条形码选型那篇管的是这一层;口径定了再落一张图,交给条形码生成器;回到解码页要核对的,是屏幕上那串与剪贴板里那串是不是同一个东西。
九、常被问反的三件事
Q:生成 13 位没报错,校验位就是对的?
不是。生成器第 184–194 行只在 length === 12 的分支里算,13 位直接落空。要自证,把末位去掉、按 12 位重跑一遍再比。
Q:UPC-A 前面加个 0 就行了?
加 0 只在 11 位数据上成立;已经带末位的 12 位不能再当作 EAN-13 的 12 位数据用,末位会被重算。
Q:面板写着不支持,复制却吐出一串码?
那是上一次的旧值,两个失败出口都不清 dataset.result。看到失败面板时,卡片内容不可信,剪贴板里那串更不可信。