nv-img-barcode-cmmn 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/TEST/tst0.js +71 -0
- package/gs1.js +454 -0
- package/index.js +2 -214
- package/package.json +1 -1
- package/ptrn.js +0 -161
package/TEST/tst0.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
const validate_and_fmt_int = (info,value)=>{
|
|
2
|
+
//整数
|
|
3
|
+
let num = Number(value);
|
|
4
|
+
if(!Object.is(num,NaN)) {
|
|
5
|
+
if(num<0) {return [false,`不可为负数`]}
|
|
6
|
+
if(num <=Number.MAX_SAFE_INTEGER){
|
|
7
|
+
if(parseInt(num) === num) {
|
|
8
|
+
value =String(num);
|
|
9
|
+
if(value.length<= info.length) {
|
|
10
|
+
value = '0'.repeat(info.length - value.length) + value;
|
|
11
|
+
return [true,value];
|
|
12
|
+
} else {
|
|
13
|
+
return [false,`${info.desc} 长度最多 ${info.length}`];
|
|
14
|
+
}
|
|
15
|
+
} else {
|
|
16
|
+
return [false,`${info.desc} 必须是整数数字`];
|
|
17
|
+
}
|
|
18
|
+
} else {
|
|
19
|
+
// num > Number.MAX_SAFE_INTEGER
|
|
20
|
+
num = BigInt(value);
|
|
21
|
+
value =String(num);
|
|
22
|
+
if(value.length<= info.length) {
|
|
23
|
+
value = '0'.repeat(info.length - value.length) + value;
|
|
24
|
+
return [true,value];
|
|
25
|
+
} else {
|
|
26
|
+
return [false,`${info.desc} 长度最多 ${info.length}`];
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
} else {
|
|
30
|
+
return [false,`${info.desc} 必须是整数数字`];
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function test_logic() {
|
|
35
|
+
const info = { length: 6, desc: "测试字段" };
|
|
36
|
+
|
|
37
|
+
console.log('MAX_SAFE_INTEGER:', Number.MAX_SAFE_INTEGER);
|
|
38
|
+
console.log('2**53:', 2**53);
|
|
39
|
+
console.log('2**53 === Number.MAX_SAFE_INTEGER?', 2**53 === Number.MAX_SAFE_INTEGER);
|
|
40
|
+
console.log('');
|
|
41
|
+
|
|
42
|
+
const testCases = [
|
|
43
|
+
// 在安全范围内的数
|
|
44
|
+
{ value: 9007199254740991, desc: "MAX_SAFE_INTEGER" }, // 应该成功
|
|
45
|
+
{ value: 9007199254740992, desc: "刚好超出安全" }, // 应该进入BigInt分支
|
|
46
|
+
{ value: 9999999999999999, desc: "9999万亿" }, // 1e16-1
|
|
47
|
+
|
|
48
|
+
// 明确超出安全范围的数
|
|
49
|
+
{ value: 10000000000000000, desc: "1e16" }, // 1e16
|
|
50
|
+
{ value: 99999999999999999, desc: "9.9e16" }, // 你提到的数
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
testCases.forEach(test => {
|
|
54
|
+
const num = Number(test.value);
|
|
55
|
+
const isSafe = num <= Number.MAX_SAFE_INTEGER;
|
|
56
|
+
|
|
57
|
+
console.log(`${test.desc.padEnd(20)}: ${test.value}`);
|
|
58
|
+
console.log(` Number() = ${num}`);
|
|
59
|
+
console.log(` <= MAX_SAFE_INTEGER? ${isSafe}`);
|
|
60
|
+
console.log(` 会进入哪个分支? ${isSafe ? '安全分支' : 'BigInt分支'}`);
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
const [success, result] = validate_and_fmt_int(info, test.value);
|
|
64
|
+
console.log(` 结果: ${success ? '成功' : '失败'} ${result || ''}`);
|
|
65
|
+
} catch (error) {
|
|
66
|
+
console.log(` 异常: ${error.message}`);
|
|
67
|
+
}
|
|
68
|
+
console.log('');
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
test_logic();
|
package/gs1.js
ADDED
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
const GS1_AI_SPECS = {
|
|
2
|
+
'00': { name: 'SSCC', length: 18, format: 'numeric', desc: '物流单元标识符' },
|
|
3
|
+
'01': { name: 'GTIN', length: 14, format: 'numeric', desc: '全球贸易项目代码' },
|
|
4
|
+
'02': { name: 'CONTENT_GTIN', length: 14, format: 'numeric', desc: '物流单元内贸易项目GTIN' },
|
|
5
|
+
'10': { name: 'BATCH', length: 20, format: 'alphanumeric', desc: '批号' },
|
|
6
|
+
'11': { name: 'PROD_DATE', length: 6, format: 'date', desc: '生产日期(YYMMDD)' },
|
|
7
|
+
'12': { name: 'DUE_DATE', length: 6, format: 'date', desc: '付款截止日期(YYMMDD)' },
|
|
8
|
+
'13': { name: 'PACK_DATE', length: 6, format: 'date', desc: '包装日期(YYMMDD)' },
|
|
9
|
+
'15': { name: 'BEST_BEFORE', length: 6, format: 'date', desc: '保质期(YYMMDD)' },
|
|
10
|
+
'16': { name: 'SELL_BY', length: 6, format: 'date', desc: '销售日期(YYMMDD)' },
|
|
11
|
+
'17': { name: 'EXPIRY', length: 6, format: 'date', desc: '有效期(YYMMDD)' },
|
|
12
|
+
'19': { name: 'EXPIRY_ALT', length: 6, format: 'date', desc: '过期日期(YYMMDD)' },
|
|
13
|
+
'20': { name: 'VARIANT', length: 2, format: 'numeric', desc: '包装内部标识' },
|
|
14
|
+
'21': { name: 'SERIAL', length: 20, format: 'alphanumeric', desc: '序列号' },
|
|
15
|
+
'22': { name: 'MEDICAL_DATA', length: 20, format: 'alphanumeric', desc: '医疗产品附加数据' },
|
|
16
|
+
'23': { name: 'LOT_GROUP', length: 20, format: 'alphanumeric', desc: '组号' },
|
|
17
|
+
'30': { name: 'VAR_COUNT', length: 8, format: 'numeric', desc: '可变数量' },
|
|
18
|
+
'3100': { name: 'NET_WEIGHT_0', length: 6, format: 'numeric', decimal: 0, unit: 'kg', desc: '净重(kg)整数' },
|
|
19
|
+
'3101': { name: 'NET_WEIGHT_1', length: 6, format: 'numeric', decimal: 1, unit: 'kg', desc: '净重(kg)1位小数' },
|
|
20
|
+
'3102': { name: 'NET_WEIGHT_2', length: 6, format: 'numeric', decimal: 2, unit: 'kg', desc: '净重(kg)2位小数' },
|
|
21
|
+
'3103': { name: 'NET_WEIGHT_3', length: 6, format: 'numeric', decimal: 3, unit: 'kg', desc: '净重(kg)3位小数' },
|
|
22
|
+
'3104': { name: 'NET_WEIGHT_4', length: 6, format: 'numeric', decimal: 4, unit: 'kg', desc: '净重(kg)4位小数' },
|
|
23
|
+
'3105': { name: 'NET_WEIGHT_5', length: 6, format: 'numeric', decimal: 5, unit: 'kg', desc: '净重(kg)5位小数' },
|
|
24
|
+
'3110': { name: 'LENGTH_0', length: 6, format: 'numeric', decimal: 0, unit: 'm', desc: '长度(m)整数' },
|
|
25
|
+
'3111': { name: 'LENGTH_1', length: 6, format: 'numeric', decimal: 1, unit: 'm', desc: '长度(m)1位小数' },
|
|
26
|
+
'3112': { name: 'LENGTH_2', length: 6, format: 'numeric', decimal: 2, unit: 'm', desc: '长度(m)2位小数' },
|
|
27
|
+
'3113': { name: 'LENGTH_3', length: 6, format: 'numeric', decimal: 3, unit: 'm', desc: '长度(m)3位小数' },
|
|
28
|
+
'3114': { name: 'LENGTH_4', length: 6, format: 'numeric', decimal: 4, unit: 'm', desc: '长度(m)4位小数' },
|
|
29
|
+
'3115': { name: 'LENGTH_5', length: 6, format: 'numeric', decimal: 5, unit: 'm', desc: '长度(m)5位小数' },
|
|
30
|
+
'3120': { name: 'WIDTH_0', length: 6, format: 'numeric', decimal: 0, unit: 'm', desc: '宽度(m)整数' },
|
|
31
|
+
'3121': { name: 'WIDTH_1', length: 6, format: 'numeric', decimal: 1, unit: 'm', desc: '宽度(m)1位小数' },
|
|
32
|
+
'3122': { name: 'WIDTH_2', length: 6, format: 'numeric', decimal: 2, unit: 'm', desc: '宽度(m)2位小数' },
|
|
33
|
+
'3123': { name: 'WIDTH_3', length: 6, format: 'numeric', decimal: 3, unit: 'm', desc: '宽度(m)3位小数' },
|
|
34
|
+
'3124': { name: 'WIDTH_4', length: 6, format: 'numeric', decimal: 4, unit: 'm', desc: '宽度(m)4位小数' },
|
|
35
|
+
'3125': { name: 'WIDTH_5', length: 6, format: 'numeric', decimal: 5, unit: 'm', desc: '宽度(m)5位小数' },
|
|
36
|
+
'3130': { name: 'HEIGHT_0', length: 6, format: 'numeric', decimal: 0, unit: 'm', desc: '高度(m)整数' },
|
|
37
|
+
'3131': { name: 'HEIGHT_1', length: 6, format: 'numeric', decimal: 1, unit: 'm', desc: '高度(m)1位小数' },
|
|
38
|
+
'3132': { name: 'HEIGHT_2', length: 6, format: 'numeric', decimal: 2, unit: 'm', desc: '高度(m)2位小数' },
|
|
39
|
+
'3133': { name: 'HEIGHT_3', length: 6, format: 'numeric', decimal: 3, unit: 'm', desc: '高度(m)3位小数' },
|
|
40
|
+
'3134': { name: 'HEIGHT_4', length: 6, format: 'numeric', decimal: 4, unit: 'm', desc: '高度(m)4位小数' },
|
|
41
|
+
'3135': { name: 'HEIGHT_5', length: 6, format: 'numeric', decimal: 5, unit: 'm', desc: '高度(m)5位小数' },
|
|
42
|
+
'3140': { name: 'AREA_0', length: 6, format: 'numeric', decimal: 0, unit: 'm²', desc: '面积(m²)整数' },
|
|
43
|
+
'3141': { name: 'AREA_1', length: 6, format: 'numeric', decimal: 1, unit: 'm²', desc: '面积(m²)1位小数' },
|
|
44
|
+
'3142': { name: 'AREA_2', length: 6, format: 'numeric', decimal: 2, unit: 'm²', desc: '面积(m²)2位小数' },
|
|
45
|
+
'3143': { name: 'AREA_3', length: 6, format: 'numeric', decimal: 3, unit: 'm²', desc: '面积(m²)3位小数' },
|
|
46
|
+
'3144': { name: 'AREA_4', length: 6, format: 'numeric', decimal: 4, unit: 'm²', desc: '面积(m²)4位小数' },
|
|
47
|
+
'3145': { name: 'AREA_5', length: 6, format: 'numeric', decimal: 5, unit: 'm²', desc: '面积(m²)5位小数' },
|
|
48
|
+
'3150': { name: 'NET_VOLUME_L_0', length: 6, format: 'numeric', decimal: 0, unit: 'l', desc: '净容积(l)整数' },
|
|
49
|
+
'3151': { name: 'NET_VOLUME_L_1', length: 6, format: 'numeric', decimal: 1, unit: 'l', desc: '净容积(l)1位小数' },
|
|
50
|
+
'3152': { name: 'NET_VOLUME_L_2', length: 6, format: 'numeric', decimal: 2, unit: 'l', desc: '净容积(l)2位小数' },
|
|
51
|
+
'3153': { name: 'NET_VOLUME_L_3', length: 6, format: 'numeric', decimal: 3, unit: 'l', desc: '净容积(l)3位小数' },
|
|
52
|
+
'3154': { name: 'NET_VOLUME_L_4', length: 6, format: 'numeric', decimal: 4, unit: 'l', desc: '净容积(l)4位小数' },
|
|
53
|
+
'3155': { name: 'NET_VOLUME_L_5', length: 6, format: 'numeric', decimal: 5, unit: 'l', desc: '净容积(l)5位小数' },
|
|
54
|
+
'3160': { name: 'NET_VOLUME_M3_0', length: 6, format: 'numeric', decimal: 0, unit: 'm³', desc: '净体积(m³)整数' },
|
|
55
|
+
'3161': { name: 'NET_VOLUME_M3_1', length: 6, format: 'numeric', decimal: 1, unit: 'm³', desc: '净体积(m³)1位小数' },
|
|
56
|
+
'3162': { name: 'NET_VOLUME_M3_2', length: 6, format: 'numeric', decimal: 2, unit: 'm³', desc: '净体积(m³)2位小数' },
|
|
57
|
+
'3163': { name: 'NET_VOLUME_M3_3', length: 6, format: 'numeric', decimal: 3, unit: 'm³', desc: '净体积(m³)3位小数' },
|
|
58
|
+
'3164': { name: 'NET_VOLUME_M3_4', length: 6, format: 'numeric', decimal: 4, unit: 'm³', desc: '净体积(m³)4位小数' },
|
|
59
|
+
'3165': { name: 'NET_VOLUME_M3_5', length: 6, format: 'numeric', decimal: 5, unit: 'm³', desc: '净体积(m³)5位小数' },
|
|
60
|
+
'3200': { name: 'NET_WEIGHT_LB_0', length: 6, format: 'numeric', decimal: 0, unit: 'lb', desc: '净重(lb)整数' },
|
|
61
|
+
'3201': { name: 'NET_WEIGHT_LB_1', length: 6, format: 'numeric', decimal: 1, unit: 'lb', desc: '净重(lb)1位小数' },
|
|
62
|
+
'3202': { name: 'NET_WEIGHT_LB_2', length: 6, format: 'numeric', decimal: 2, unit: 'lb', desc: '净重(lb)2位小数' },
|
|
63
|
+
'3203': { name: 'NET_WEIGHT_LB_3', length: 6, format: 'numeric', decimal: 3, unit: 'lb', desc: '净重(lb)3位小数' },
|
|
64
|
+
'3204': { name: 'NET_WEIGHT_LB_4', length: 6, format: 'numeric', decimal: 4, unit: 'lb', desc: '净重(lb)4位小数' },
|
|
65
|
+
'3205': { name: 'NET_WEIGHT_LB_5', length: 6, format: 'numeric', decimal: 5, unit: 'lb', desc: '净重(lb)5位小数' },
|
|
66
|
+
'3300': { name: 'GROSS_WEIGHT_0', length: 6, format: 'numeric', decimal: 0, unit: 'kg', desc: '毛重(kg)整数' },
|
|
67
|
+
'3301': { name: 'GROSS_WEIGHT_1', length: 6, format: 'numeric', decimal: 1, unit: 'kg', desc: '毛重(kg)1位小数' },
|
|
68
|
+
'3302': { name: 'GROSS_WEIGHT_2', length: 6, format: 'numeric', decimal: 2, unit: 'kg', desc: '毛重(kg)2位小数' },
|
|
69
|
+
'3303': { name: 'GROSS_WEIGHT_3', length: 6, format: 'numeric', decimal: 3, unit: 'kg', desc: '毛重(kg)3位小数' },
|
|
70
|
+
'3304': { name: 'GROSS_WEIGHT_4', length: 6, format: 'numeric', decimal: 4, unit: 'kg', desc: '毛重(kg)4位小数' },
|
|
71
|
+
'3305': { name: 'GROSS_WEIGHT_5', length: 6, format: 'numeric', decimal: 5, unit: 'kg', desc: '毛重(kg)5位小数' },
|
|
72
|
+
'37': { name: 'COUNT', length: 8, format: 'numeric', desc: '物流单元内商品数量' },
|
|
73
|
+
'90': { name: 'INTERNAL_1', length: 30, format: 'alphanumeric', desc: '自定义0' },
|
|
74
|
+
'91': { name: 'INTERNAL_2', length: 90, format: 'alphanumeric', desc: '自定义1' },
|
|
75
|
+
'92': { name: 'INTERNAL_3', length: 90, format: 'alphanumeric', desc: '自定义2' },
|
|
76
|
+
'93': { name: 'INTERNAL_4', length: 90, format: 'alphanumeric', desc: '自定义3' },
|
|
77
|
+
'94': { name: 'INTERNAL_5', length: 90, format: 'alphanumeric', desc: '自定义4' },
|
|
78
|
+
'95': { name: 'INTERNAL_6', length: 90, format: 'alphanumeric', desc: '自定义5' },
|
|
79
|
+
'96': { name: 'INTERNAL_7', length: 90, format: 'alphanumeric', desc: '自定义6' },
|
|
80
|
+
'97': { name: 'INTERNAL_8', length: 90, format: 'alphanumeric', desc: '自定义7' },
|
|
81
|
+
'98': { name: 'INTERNAL_9', length: 90, format: 'alphanumeric', desc: '自定义8' },
|
|
82
|
+
'99': { name: 'INTERNAL_10', length: 90, format: 'alphanumeric', desc: '自定义9' }
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const GS1_ALPHANUMERIC_REGEX = /^[A-Za-z0-9 !"*+,\-.\/:;=?_\x23\x24]+$/;
|
|
86
|
+
// ^
|
|
87
|
+
// [ # 字符集开始
|
|
88
|
+
// A-Za-z0-9 # 字母和数字
|
|
89
|
+
// \x20 # 空格(ASCII 32)
|
|
90
|
+
// !"*+,\-.\/:;=?_ # 允许的特殊字符
|
|
91
|
+
// \x23 # #(ASCII 35)
|
|
92
|
+
// \x24 # $(ASCII 36)
|
|
93
|
+
// ] # 字符集结束
|
|
94
|
+
// + # 至少一个字符
|
|
95
|
+
// $ # 字符串结束
|
|
96
|
+
const YYMMDD_REGEX = /^(\d{2})(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])$/;
|
|
97
|
+
const mts_to_yymmdd = (mts)=>{
|
|
98
|
+
////时间戳转换为YYMMDD
|
|
99
|
+
const date = new Date(mts);
|
|
100
|
+
const year = date.getFullYear() % 100; // 取年份后两位
|
|
101
|
+
const month = date.getMonth() + 1; // 月份从0开始
|
|
102
|
+
const day = date.getDate(); // 获取日期
|
|
103
|
+
|
|
104
|
+
return [
|
|
105
|
+
year.toString().padStart(2, '0'), // YY
|
|
106
|
+
month.toString().padStart(2, '0'), // MM
|
|
107
|
+
day.toString().padStart(2, '0') // DD
|
|
108
|
+
].join('');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const splitIntegerDecimal = (num) => {
|
|
112
|
+
// 转换为字符串
|
|
113
|
+
const str = String(num);
|
|
114
|
+
|
|
115
|
+
// 分割整数和小数部分
|
|
116
|
+
const parts = str.split('.');
|
|
117
|
+
|
|
118
|
+
// 处理整数部分
|
|
119
|
+
let integerPart = null;
|
|
120
|
+
const integerNum = parseInt(parts[0] || '0', 10);
|
|
121
|
+
if (integerNum !== 0) {
|
|
122
|
+
integerPart = integerNum;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// 处理小数部分
|
|
126
|
+
let decimalPart = null;
|
|
127
|
+
if (parts[1] && parts[1] !== '0') {
|
|
128
|
+
// 移除末尾的0
|
|
129
|
+
const decimalStr = parts[1].replace(/0+$/, '');
|
|
130
|
+
if (decimalStr && decimalStr !== '0') {
|
|
131
|
+
decimalPart = parseInt(decimalStr, 10);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return [integerPart, decimalPart];
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
const validate_and_fmt_int = (info,value)=>{
|
|
140
|
+
//整数
|
|
141
|
+
let num = Number(value);
|
|
142
|
+
if(!Object.is(num,NaN)) {
|
|
143
|
+
if(num<0) {return [false,`不可为负数`]}
|
|
144
|
+
if(num <=Number.MAX_SAFE_INTEGER){
|
|
145
|
+
if(parseInt(num) === num) {
|
|
146
|
+
value =String(num);
|
|
147
|
+
if(value.length<= info.length) {
|
|
148
|
+
value = '0'.repeat(info.length - value.length) + value;
|
|
149
|
+
return [true,value];
|
|
150
|
+
} else {
|
|
151
|
+
return [false,`${info.desc} 长度最多 ${info.length}`];
|
|
152
|
+
}
|
|
153
|
+
} else {
|
|
154
|
+
return [false,`${info.desc} 必须是整数数字`];
|
|
155
|
+
}
|
|
156
|
+
} else {
|
|
157
|
+
// num > Number.MAX_SAFE_INTEGER
|
|
158
|
+
num = BigInt(value);
|
|
159
|
+
value =String(num);
|
|
160
|
+
if(value.length<= info.length) {
|
|
161
|
+
value = '0'.repeat(info.length - value.length) + value;
|
|
162
|
+
return [true,value];
|
|
163
|
+
} else {
|
|
164
|
+
return [false,`${info.desc} 长度最多 ${info.length}`];
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
} else {
|
|
168
|
+
return [false,`${info.desc} 必须是整数数字`];
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const validate_and_fmt_ai_value = (ai, value)=>{
|
|
173
|
+
ai = String(ai);
|
|
174
|
+
let info = GS1_AI_SPECS[ai];
|
|
175
|
+
if(info===undefined) {
|
|
176
|
+
return [false,"非法AI"]
|
|
177
|
+
} else {
|
|
178
|
+
if(info.format === "numeric") {
|
|
179
|
+
if(info.decimal === undefined|| info.decimal ===0) {
|
|
180
|
+
//整数
|
|
181
|
+
return validate_and_fmt_int(info,value);
|
|
182
|
+
} else {
|
|
183
|
+
//浮点
|
|
184
|
+
let num = Number(value);
|
|
185
|
+
let [ii,dd] = splitIntegerDecimal(num);
|
|
186
|
+
if(ii === null) {
|
|
187
|
+
if(dd === null) {
|
|
188
|
+
return [true,'0'.repeat(info.length)]
|
|
189
|
+
} else {
|
|
190
|
+
dd = "0." + String(dd);
|
|
191
|
+
dd = Number(dd);
|
|
192
|
+
dd = dd *(10**info.decimal);
|
|
193
|
+
dd = String(dd);
|
|
194
|
+
dd = '0'.repeat(info.length - dd.length)+dd;
|
|
195
|
+
return [true,dd]
|
|
196
|
+
}
|
|
197
|
+
} else {
|
|
198
|
+
if(dd === null) {
|
|
199
|
+
return validate_and_fmt_int(info,ii);
|
|
200
|
+
} else {
|
|
201
|
+
let ilen = String(ii).length;
|
|
202
|
+
let int_max_length = info.length - info.decimal;
|
|
203
|
+
if(ilen> int_max_length) {
|
|
204
|
+
return [false,`${info.desc} 长度最多 ${info.length}`]
|
|
205
|
+
} else {
|
|
206
|
+
const intPart = String(ii).padStart(int_max_length, '0');
|
|
207
|
+
let decStr = String(dd).padEnd(info.decimal, '0').substring(0, info.decimal);
|
|
208
|
+
const result = intPart + decStr;
|
|
209
|
+
return [true, result];
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
} else if(info.format === "date") {
|
|
215
|
+
if(typeof(value)==="number") {
|
|
216
|
+
////视作时间戳
|
|
217
|
+
return [true,mts_to_yymmdd(value)]
|
|
218
|
+
} else {
|
|
219
|
+
value = String(value);
|
|
220
|
+
}
|
|
221
|
+
if(YYMMDD_REGEX.test(value)) {
|
|
222
|
+
return [true,value];
|
|
223
|
+
} else {
|
|
224
|
+
return [false,`${info.desc} 必需匹配 YYMMDD`];
|
|
225
|
+
}
|
|
226
|
+
} else {
|
|
227
|
+
//(info.format === "alphanumeric")
|
|
228
|
+
value = String(value);
|
|
229
|
+
if(GS1_ALPHANUMERIC_REGEX.test(value)) {
|
|
230
|
+
if(info.length>=value.length) {
|
|
231
|
+
return [true,value]
|
|
232
|
+
} else {
|
|
233
|
+
return [false,`${info.desc} 长度必需 <= ${info.length}`];
|
|
234
|
+
}
|
|
235
|
+
} else {
|
|
236
|
+
return [false,`${info.desc} 必需匹配 /^[A-Za-z0-9 !"%&'()*+,\-.\/:;<=>?_]+$/`];
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
let ALIAS = {
|
|
243
|
+
// 物流单元
|
|
244
|
+
'物流单元标识符': '00',
|
|
245
|
+
'物流单元': '00',
|
|
246
|
+
'运输包装代码': '00',
|
|
247
|
+
'托盘代码': '00',
|
|
248
|
+
// 商品代码
|
|
249
|
+
'商品条码': '01',
|
|
250
|
+
'商品代码': '01',
|
|
251
|
+
'产品代码': '01',
|
|
252
|
+
// 批次
|
|
253
|
+
'批次': '10',
|
|
254
|
+
'批号': '10',
|
|
255
|
+
'lot': '10',
|
|
256
|
+
'batch': '10',
|
|
257
|
+
// 日期相关
|
|
258
|
+
'生产日期': '11',
|
|
259
|
+
'制造日期': '11',
|
|
260
|
+
'保质期': '15',
|
|
261
|
+
'保鲜期': '15',
|
|
262
|
+
'有效期': '17',
|
|
263
|
+
'到期日': '17',
|
|
264
|
+
// 数量
|
|
265
|
+
'数量': '30',
|
|
266
|
+
'件数': '30',
|
|
267
|
+
'quantity': '30',
|
|
268
|
+
'物流单元内商品数量': '37',
|
|
269
|
+
'整箱数量': '37',
|
|
270
|
+
// 序列号
|
|
271
|
+
'序列号': '21',
|
|
272
|
+
'流水号': '21',
|
|
273
|
+
'serial': '21',
|
|
274
|
+
// 重量体积
|
|
275
|
+
'净重': '3100',
|
|
276
|
+
'重量': '3100',
|
|
277
|
+
'毛重': '3300',
|
|
278
|
+
'总重': '3300',
|
|
279
|
+
'长度': '3110',
|
|
280
|
+
'长': '3110',
|
|
281
|
+
'宽度': '3120',
|
|
282
|
+
'宽': '3120',
|
|
283
|
+
'高度': '3130',
|
|
284
|
+
'高': '3130',
|
|
285
|
+
'面积': '3140',
|
|
286
|
+
'体积': '3160',
|
|
287
|
+
'容积': '3150',
|
|
288
|
+
'容量': '3150',
|
|
289
|
+
|
|
290
|
+
// 包装
|
|
291
|
+
'包装内部标识': '20',
|
|
292
|
+
'包装标识': '20',
|
|
293
|
+
|
|
294
|
+
// 医疗
|
|
295
|
+
'医疗': '22',
|
|
296
|
+
'药品': '22',
|
|
297
|
+
|
|
298
|
+
// 组
|
|
299
|
+
'组号': '23',
|
|
300
|
+
'分组': '23',
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
let AI_TO_NAMES = {};
|
|
304
|
+
let NAME_TO_AI = {};
|
|
305
|
+
const reset_map = ()=> {
|
|
306
|
+
AI_TO_NAMES = {};
|
|
307
|
+
NAME_TO_AI = {};
|
|
308
|
+
for(let ai in GS1_AI_SPECS) {
|
|
309
|
+
NAME_TO_AI[ai] = ai; // 直接使用ai
|
|
310
|
+
AI_TO_NAMES[ai] = [ai];
|
|
311
|
+
let detail = GS1_AI_SPECS[ai];
|
|
312
|
+
let v = GS1_AI_SPECS[ai];
|
|
313
|
+
let name = v.name.toLowerCase(); NAME_TO_AI[name] = ai; // 使用英文名
|
|
314
|
+
////使用中文名
|
|
315
|
+
{
|
|
316
|
+
let desc = v.desc.replace('(m)', '米')
|
|
317
|
+
.replace('(m²)', '平')
|
|
318
|
+
.replace('(m³)', '方')
|
|
319
|
+
.replace('(lb)', '磅')
|
|
320
|
+
.replace('(kg)', '公斤')
|
|
321
|
+
.replace('(l)', '升')
|
|
322
|
+
.replace(/[\(\)]/g, '') // 再清理一次可能的残留括号
|
|
323
|
+
.trim()
|
|
324
|
+
.toLowerCase()
|
|
325
|
+
;
|
|
326
|
+
NAME_TO_AI[desc] = ai;
|
|
327
|
+
AI_TO_NAMES[ai].push(desc);
|
|
328
|
+
////
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
reset_map();
|
|
333
|
+
let FIXED_NAMES ;
|
|
334
|
+
const update_map = ()=> {
|
|
335
|
+
reset_map();
|
|
336
|
+
////使用ALIAS
|
|
337
|
+
{
|
|
338
|
+
for(let alias in ALIAS) {
|
|
339
|
+
let ai = ALIAS[alias];
|
|
340
|
+
if(GS1_AI_SPECS[ai]){
|
|
341
|
+
NAME_TO_AI[alias.toLowerCase()] = ai;
|
|
342
|
+
AI_TO_NAMES[ai].push(alias.toLowerCase());
|
|
343
|
+
} else {
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
FIXED_NAMES = Object.keys(NAME_TO_AI);
|
|
349
|
+
}
|
|
350
|
+
update_map();
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
const name_to_ai = (name)=>{
|
|
354
|
+
name = String(name).toLowerCase();
|
|
355
|
+
let ai = NAME_TO_AI[name];
|
|
356
|
+
return ai;
|
|
357
|
+
}
|
|
358
|
+
const ai_to_names = (ai)=>AI_TO_NAMES[ai];
|
|
359
|
+
const encd_one =(name_or_ai,value)=>{
|
|
360
|
+
let ai = name_to_ai(name_or_ai);
|
|
361
|
+
if (!ai) {
|
|
362
|
+
//无效的字段名或AI代码: "${name_or_ai} 请尝试使用 未使用的自定义ai 90-99"
|
|
363
|
+
return [null,name_to_ai];
|
|
364
|
+
}
|
|
365
|
+
let [cond,v] = validate_and_fmt_ai_value(ai,value);
|
|
366
|
+
if(cond) {
|
|
367
|
+
return [true,`(${ai})${v}`];
|
|
368
|
+
} else {
|
|
369
|
+
return [false,v];
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
////
|
|
375
|
+
const _encd = (arr) => {
|
|
376
|
+
let succs = [];let fails = [];
|
|
377
|
+
const aloc_next_unused_dyn_slot = ()=>{
|
|
378
|
+
for(let ai=90;ai<=99;++ai) {if(st.has(ai)) {} else {st.add(ai);return ai;}}
|
|
379
|
+
return null;
|
|
380
|
+
}
|
|
381
|
+
let len = arr.length;let st = new Set();
|
|
382
|
+
if(len === 0) { fails.push("参数不能为空");
|
|
383
|
+
} else {
|
|
384
|
+
if(len %2 === 0) {} else {arr.push(undefined);}
|
|
385
|
+
for(let i=0;i<arr.length;i+=2) {
|
|
386
|
+
let name_or_ai = arr[i];
|
|
387
|
+
let value = arr[i+1];
|
|
388
|
+
if(value !== undefined) {
|
|
389
|
+
let [cond,vstr] = encd_one(name_or_ai,value);
|
|
390
|
+
if(cond === null) {
|
|
391
|
+
let ai = aloc_next_unused_dyn_slot();
|
|
392
|
+
if(ai!== null) {
|
|
393
|
+
let [cond1,vstr1] = encd_one(ai,value);
|
|
394
|
+
if (cond1) {
|
|
395
|
+
succs.push(vstr1);ALIAS[String(name_or_ai).toLowerCase()] = ai;update_map();
|
|
396
|
+
} else {
|
|
397
|
+
fails.push(`${name_or_ai} : ${vstr}`)
|
|
398
|
+
}
|
|
399
|
+
} else {
|
|
400
|
+
fails.push(`${name_or_ai} 不是合法AI 并且 90-99 动态ai 都已占用`)
|
|
401
|
+
}
|
|
402
|
+
} else if(cond === true) {
|
|
403
|
+
succs.push(vstr);
|
|
404
|
+
let ai = Number(name_to_ai(name_or_ai));
|
|
405
|
+
if(ai>=90 && ai<=99 ) {
|
|
406
|
+
st.add(ai); //已经被占用
|
|
407
|
+
} else {}
|
|
408
|
+
} else {
|
|
409
|
+
fails.push(`${name_or_ai} : ${vstr}`)
|
|
410
|
+
}
|
|
411
|
+
} else {
|
|
412
|
+
fails.push(`${name_or_ai} : "值不能为空"`)
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
return [succs,fails]
|
|
417
|
+
}
|
|
418
|
+
const encd = (...args)=>{
|
|
419
|
+
if(args.length === 0) {
|
|
420
|
+
return null;
|
|
421
|
+
} else if(args.length === 1) {
|
|
422
|
+
let arr = args[0];
|
|
423
|
+
if(Array.isArray(args[0])) {
|
|
424
|
+
} else {
|
|
425
|
+
arr = Object.entries(args[0]).flat();
|
|
426
|
+
}
|
|
427
|
+
let [succs,fails] = _encd(arr);
|
|
428
|
+
let succ_str = succs.join("");
|
|
429
|
+
return succ_str===""?null:succ_str;
|
|
430
|
+
} else {
|
|
431
|
+
let [succs,fails] = _encd(args);
|
|
432
|
+
let succ_str = succs.join("");
|
|
433
|
+
return succ_str===""?null:succ_str;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
module.exports = {
|
|
438
|
+
get GS1_AI_SPECS() {return GS1_AI_SPECS;},
|
|
439
|
+
get ALIAS() {return ALIAS;},
|
|
440
|
+
set ALIAS(d) {
|
|
441
|
+
ALIAS = d;
|
|
442
|
+
update_map();
|
|
443
|
+
},
|
|
444
|
+
get NAME_TO_AI() {return NAME_TO_AI;},
|
|
445
|
+
get AI_TO_NAMES() {return AI_TO_NAMES;},
|
|
446
|
+
get FIXED_NAMES() {return FIXED_NAMES;},
|
|
447
|
+
validate_and_fmt_ai_value,
|
|
448
|
+
name_to_ai,ai_to_names,
|
|
449
|
+
encd_one,
|
|
450
|
+
_encd,
|
|
451
|
+
encd,
|
|
452
|
+
list_names: ()=>FIXED_NAMES,
|
|
453
|
+
look_requirements: (name)=>GS1_AI_SPECS[name_to_ai(name)]
|
|
454
|
+
}
|
package/index.js
CHANGED
|
@@ -1,217 +1,5 @@
|
|
|
1
|
-
const
|
|
2
|
-
code128_code_to_ptrn,
|
|
3
|
-
code128_ptrn_to_code,
|
|
4
|
-
CODE128_CODE_TO_PTRN
|
|
5
|
-
} = require("./ptrn")
|
|
6
|
-
|
|
7
|
-
const bytes_to_code128 = (bytes) => {
|
|
8
|
-
const codes = [];
|
|
9
|
-
const START_B = 104;
|
|
10
|
-
codes.push(START_B);
|
|
11
|
-
|
|
12
|
-
for (const b of bytes) {
|
|
13
|
-
codes.push(b & 0x7f); // 与 decoder 对称
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
// checksum
|
|
17
|
-
let sum = START_B;
|
|
18
|
-
codes.slice(1).forEach((c, i) => {
|
|
19
|
-
sum += c * (i + 1);
|
|
20
|
-
});
|
|
21
|
-
codes.push(sum % 103);
|
|
22
|
-
|
|
23
|
-
// STOP
|
|
24
|
-
codes.push(106);
|
|
25
|
-
|
|
26
|
-
// codes -> modules
|
|
27
|
-
return codes.flatMap(c => CODE128_CODE_TO_PTRN[c]);
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
const code128_to_bytes = (modules) => {
|
|
31
|
-
const codes = [];
|
|
32
|
-
let i = 0;
|
|
33
|
-
|
|
34
|
-
while (i < modules.length) {
|
|
35
|
-
let code = code128_ptrn_to_code(modules.slice(i, i + 6));
|
|
36
|
-
if (code >= 0) {
|
|
37
|
-
codes.push(code);
|
|
38
|
-
i += 6;
|
|
39
|
-
continue;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// 尝试 STOP
|
|
43
|
-
if (i + 7 <= modules.length) {
|
|
44
|
-
code = code128_ptrn_to_code(modules.slice(i, i + 7));
|
|
45
|
-
if (code === 106) {
|
|
46
|
-
codes.push(106);
|
|
47
|
-
i += 7;
|
|
48
|
-
break;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// 不匹配也安全跳过:+1 移动
|
|
53
|
-
i += 1;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// 安全处理 START / STOP
|
|
57
|
-
if (codes.length === 0 || codes[0] !== 104) {
|
|
58
|
-
// 没有 START_B,用空 payload
|
|
59
|
-
return new Uint8Array(0);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// 去掉 START / checksum / STOP
|
|
63
|
-
const payload = codes.slice(1, -2);
|
|
64
|
-
|
|
65
|
-
// checksum 校验可选
|
|
66
|
-
let sum = 104;
|
|
67
|
-
payload.forEach((c, i) => sum += c * (i + 1));
|
|
68
|
-
const checksum = codes[codes.length - 2] ?? 0;
|
|
69
|
-
if (sum % 103 !== checksum) {
|
|
70
|
-
// 可以返回 null 或空数组表示校验失败
|
|
71
|
-
return new Uint8Array(0);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
return Uint8Array.from(payload);
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
const normalize_runs_to_modules = (runs) => {
|
|
78
|
-
const min = Math.min(...runs);
|
|
79
|
-
return runs.map(r => Math.max(1, Math.round(r / min)));
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* transfer_ab_to_png_ab
|
|
85
|
-
* @param {ArrayBuffer} ab - 输入数据
|
|
86
|
-
* @param {number} moduleWidth - 每个最小 module 的像素宽度,默认 2
|
|
87
|
-
* @param {number} height - 条形码高度,默认 50px
|
|
88
|
-
* @returns {Promise<ArrayBuffer>} - PNG 的 ArrayBuffer
|
|
89
|
-
*/
|
|
90
|
-
const transfer_ab_to_png_ab = async (ab, moduleWidth = 2, height = 50) => {
|
|
91
|
-
const bytes = new Uint8Array(ab);
|
|
92
|
-
|
|
93
|
-
// 1️⃣ bytes -> Code128 modules
|
|
94
|
-
const modules = bytes_to_code128(bytes);
|
|
95
|
-
|
|
96
|
-
// 2️⃣ Canvas 尺寸
|
|
97
|
-
const canvasWidth = modules.reduce((acc, m) => acc + m * moduleWidth, 0);
|
|
98
|
-
const canvas = document.createElement("canvas");
|
|
99
|
-
canvas.width = canvasWidth;
|
|
100
|
-
canvas.height = height;
|
|
101
|
-
const ctx = canvas.getContext("2d");
|
|
102
|
-
|
|
103
|
-
// 3️⃣ 绘制条形码
|
|
104
|
-
let x = 0;
|
|
105
|
-
modules.forEach((m, idx) => {
|
|
106
|
-
ctx.fillStyle = idx % 2 === 0 ? "black" : "white"; // 黑白交替
|
|
107
|
-
ctx.fillRect(x, 0, m * moduleWidth, height);
|
|
108
|
-
x += m * moduleWidth;
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
// 4️⃣ Canvas -> Blob -> ArrayBuffer
|
|
112
|
-
return new Promise((resolve, reject) => {
|
|
113
|
-
canvas.toBlob((blob) => {
|
|
114
|
-
if (!blob) return reject(new Error("Canvas to Blob failed"));
|
|
115
|
-
const reader = new FileReader();
|
|
116
|
-
reader.onload = () => resolve(reader.result);
|
|
117
|
-
reader.onerror = reject;
|
|
118
|
-
reader.readAsArrayBuffer(blob);
|
|
119
|
-
}, "image/png");
|
|
120
|
-
});
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* png_ab_to_transfer_ab
|
|
125
|
-
* @param {ArrayBuffer} pngAb - PNG 数据
|
|
126
|
-
* @param {number} moduleWidthEstimate - 用于初步估算模块宽度,默认 2
|
|
127
|
-
* @returns {Promise<ArrayBuffer>} - 原始 ArrayBuffer
|
|
128
|
-
*/
|
|
129
|
-
const png_ab_to_transfer_ab = async (pngAb, moduleWidthEstimate = 2) => {
|
|
130
|
-
// 1️⃣ ArrayBuffer -> Blob -> Image
|
|
131
|
-
const blob = new Blob([pngAb], { type: "image/png" });
|
|
132
|
-
const url = URL.createObjectURL(blob);
|
|
133
|
-
|
|
134
|
-
const img = new Image();
|
|
135
|
-
img.src = url;
|
|
136
|
-
await img.decode();
|
|
137
|
-
|
|
138
|
-
// 2️⃣ Image -> Canvas
|
|
139
|
-
const canvas = document.createElement("canvas");
|
|
140
|
-
canvas.width = img.width;
|
|
141
|
-
canvas.height = img.height;
|
|
142
|
-
const ctx = canvas.getContext("2d");
|
|
143
|
-
ctx.drawImage(img, 0, 0);
|
|
144
|
-
|
|
145
|
-
// 3️⃣ 读取顶部一行像素
|
|
146
|
-
const imageData = ctx.getImageData(0, 0, canvas.width, 1).data;
|
|
147
|
-
const runs = [];
|
|
148
|
-
let lastBW = null;
|
|
149
|
-
let count = 0;
|
|
150
|
-
|
|
151
|
-
for (let x = 0; x < canvas.width; x++) {
|
|
152
|
-
const i = x * 4;
|
|
153
|
-
const r = imageData[i];
|
|
154
|
-
const g = imageData[i + 1];
|
|
155
|
-
const b = imageData[i + 2];
|
|
156
|
-
const bw = (r + g + b) / 3 < 128 ? 1 : 0; // 黑 = 1, 白 = 0
|
|
157
|
-
|
|
158
|
-
if (lastBW === null || bw === lastBW) {
|
|
159
|
-
count++;
|
|
160
|
-
} else {
|
|
161
|
-
runs.push(count);
|
|
162
|
-
count = 1;
|
|
163
|
-
}
|
|
164
|
-
lastBW = bw;
|
|
165
|
-
}
|
|
166
|
-
runs.push(count);
|
|
167
|
-
|
|
168
|
-
// 4️⃣ run-length -> modules
|
|
169
|
-
const normalize_runs_to_modules = (runs) => {
|
|
170
|
-
const min = Math.min(...runs);
|
|
171
|
-
return runs.map(r => Math.max(1, Math.round(r / min)));
|
|
172
|
-
};
|
|
173
|
-
const modules = normalize_runs_to_modules(runs);
|
|
174
|
-
|
|
175
|
-
// 5️⃣ modules -> bytes -> ArrayBuffer
|
|
176
|
-
const bytes = code128_to_bytes(modules);
|
|
177
|
-
|
|
178
|
-
URL.revokeObjectURL(url); // 清理
|
|
179
|
-
|
|
180
|
-
return bytes.buffer;
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* str2png
|
|
185
|
-
* @param {string} s - 可读文本
|
|
186
|
-
* @param {number} moduleWidth - 每个 module 的像素宽度
|
|
187
|
-
* @param {number} height - 条形码高度
|
|
188
|
-
* @returns {Promise<ArrayBuffer>} - PNG 数据
|
|
189
|
-
*/
|
|
190
|
-
const str2png = async (s, moduleWidth = 2, height = 50) => {
|
|
191
|
-
// 将字符串编码为 UTF-8 bytes
|
|
192
|
-
const encoder = new TextEncoder(); // 浏览器内置 API
|
|
193
|
-
const ab = encoder.encode(s).buffer; // Uint8Array -> ArrayBuffer
|
|
194
|
-
return transfer_ab_to_png_ab(ab, moduleWidth, height);
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* png2str
|
|
199
|
-
* @param {ArrayBuffer} pngAb - PNG 数据
|
|
200
|
-
* @param {number} moduleWidthEstimate - 用于初步估算模块宽度
|
|
201
|
-
* @returns {Promise<string>} - 解码后的文本
|
|
202
|
-
*/
|
|
203
|
-
const png2str = async (pngAb, moduleWidthEstimate = 2) => {
|
|
204
|
-
const ab = await png_ab_to_transfer_ab(pngAb, moduleWidthEstimate); // PNG -> ArrayBuffer
|
|
205
|
-
const decoder = new TextDecoder(); // 默认 utf-8
|
|
206
|
-
return decoder.decode(ab);
|
|
207
|
-
};
|
|
1
|
+
const gs1 = require("./gs1");
|
|
208
2
|
|
|
209
3
|
module.exports = {
|
|
210
|
-
|
|
211
|
-
code128_to_bytes,
|
|
212
|
-
normalize_runs_to_modules,
|
|
213
|
-
transfer_ab_to_png_ab,
|
|
214
|
-
png_ab_to_transfer_ab,
|
|
215
|
-
str2png,
|
|
216
|
-
png2str,
|
|
4
|
+
gs1
|
|
217
5
|
}
|
package/package.json
CHANGED
package/ptrn.js
DELETED
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
// Code 128 patterns (ISO/IEC 15417)
|
|
2
|
-
const CODE128_CODE_TO_PTRN = [
|
|
3
|
-
[2,1,2,2,2,2], // 0
|
|
4
|
-
[2,2,2,1,2,2], // 1
|
|
5
|
-
[2,2,2,2,2,1], // 2
|
|
6
|
-
[1,2,1,2,2,3], // 3
|
|
7
|
-
[1,2,1,3,2,2], // 4
|
|
8
|
-
[1,3,1,2,2,2], // 5
|
|
9
|
-
[1,2,2,2,1,3], // 6
|
|
10
|
-
[1,2,2,3,1,2], // 7
|
|
11
|
-
[1,3,2,2,1,2], // 8
|
|
12
|
-
[2,2,1,2,1,3], // 9
|
|
13
|
-
[2,2,1,3,1,2], // 10
|
|
14
|
-
[2,3,1,2,1,2], // 11
|
|
15
|
-
[1,1,2,2,3,2], // 12
|
|
16
|
-
[1,2,2,1,3,2], // 13
|
|
17
|
-
[1,2,2,2,3,1], // 14
|
|
18
|
-
[1,1,3,2,2,2], // 15
|
|
19
|
-
[1,2,3,1,2,2], // 16
|
|
20
|
-
[1,2,3,2,2,1], // 17
|
|
21
|
-
[2,2,3,2,1,1], // 18
|
|
22
|
-
[2,2,1,1,3,2], // 19
|
|
23
|
-
[2,2,1,2,3,1], // 20
|
|
24
|
-
[2,1,3,2,1,2], // 21
|
|
25
|
-
[2,2,3,1,1,2], // 22
|
|
26
|
-
[3,1,2,1,3,1], // 23
|
|
27
|
-
[3,1,1,2,2,2], // 24
|
|
28
|
-
[3,2,1,1,2,2], // 25
|
|
29
|
-
[3,2,1,2,2,1], // 26
|
|
30
|
-
[3,1,2,2,1,2], // 27
|
|
31
|
-
[3,2,2,1,1,2], // 28
|
|
32
|
-
[3,2,2,2,1,1], // 29
|
|
33
|
-
[2,1,2,1,2,3], // 30
|
|
34
|
-
[2,1,2,3,2,1], // 31
|
|
35
|
-
[2,3,2,1,2,1], // 32
|
|
36
|
-
[1,1,1,3,2,3], // 33
|
|
37
|
-
[1,3,1,1,2,3], // 34
|
|
38
|
-
[1,3,1,3,2,1], // 35
|
|
39
|
-
[1,1,2,3,1,3], // 36
|
|
40
|
-
[1,3,2,1,1,3], // 37
|
|
41
|
-
[1,3,2,3,1,1], // 38
|
|
42
|
-
[2,1,1,3,1,3], // 39
|
|
43
|
-
[2,3,1,1,1,3], // 40
|
|
44
|
-
[2,3,1,3,1,1], // 41
|
|
45
|
-
[1,1,2,1,3,3], // 42
|
|
46
|
-
[1,1,2,3,3,1], // 43
|
|
47
|
-
[1,3,2,1,3,1], // 44
|
|
48
|
-
[1,1,3,1,2,3], // 45
|
|
49
|
-
[1,1,3,3,2,1], // 46
|
|
50
|
-
[1,3,3,1,2,1], // 47
|
|
51
|
-
[3,1,3,1,2,1], // 48
|
|
52
|
-
[2,1,1,3,3,1], // 49
|
|
53
|
-
[2,3,1,1,3,1], // 50
|
|
54
|
-
[2,1,3,1,1,3], // 51
|
|
55
|
-
[2,1,3,3,1,1], // 52
|
|
56
|
-
[2,1,3,1,3,1], // 53
|
|
57
|
-
[3,1,1,1,2,3], // 54
|
|
58
|
-
[3,1,1,3,2,1], // 55
|
|
59
|
-
[3,3,1,1,2,1], // 56
|
|
60
|
-
[3,1,2,1,1,3], // 57
|
|
61
|
-
[3,1,2,3,1,1], // 58
|
|
62
|
-
[3,3,2,1,1,1], // 59
|
|
63
|
-
[3,1,4,1,1,1], // 60
|
|
64
|
-
[2,2,1,4,1,1], // 61
|
|
65
|
-
[4,3,1,1,1,1], // 62
|
|
66
|
-
[1,1,1,2,2,4], // 63
|
|
67
|
-
[1,1,1,4,2,2], // 64
|
|
68
|
-
[1,2,1,1,2,4], // 65
|
|
69
|
-
[1,2,1,4,2,1], // 66
|
|
70
|
-
[1,4,1,1,2,2], // 67
|
|
71
|
-
[1,4,1,2,2,1], // 68
|
|
72
|
-
[1,1,2,2,1,4], // 69
|
|
73
|
-
[1,1,2,4,1,2], // 70
|
|
74
|
-
[1,2,2,1,1,4], // 71
|
|
75
|
-
[1,2,2,4,1,1], // 72
|
|
76
|
-
[1,4,2,1,1,2], // 73
|
|
77
|
-
[1,4,2,2,1,1], // 74
|
|
78
|
-
[2,4,1,2,1,1], // 75
|
|
79
|
-
[2,2,1,1,1,4], // 76
|
|
80
|
-
[4,1,3,1,1,1], // 77
|
|
81
|
-
[2,4,1,1,1,2], // 78
|
|
82
|
-
[1,3,4,1,1,1], // 79
|
|
83
|
-
[1,1,1,2,4,2], // 80
|
|
84
|
-
[1,2,1,1,4,2], // 81
|
|
85
|
-
[1,2,1,2,4,1], // 82
|
|
86
|
-
[1,1,4,2,1,2], // 83
|
|
87
|
-
[1,2,4,1,1,2], // 84
|
|
88
|
-
[1,2,4,2,1,1], // 85
|
|
89
|
-
[4,1,1,2,1,2], // 86
|
|
90
|
-
[4,2,1,1,1,2], // 87
|
|
91
|
-
[4,2,1,2,1,1], // 88
|
|
92
|
-
[2,1,2,1,4,1], // 89
|
|
93
|
-
[2,1,4,1,2,1], // 90
|
|
94
|
-
[4,1,2,1,2,1], // 91
|
|
95
|
-
[1,1,1,1,4,3], // 92
|
|
96
|
-
[1,1,1,3,4,1], // 93
|
|
97
|
-
[1,3,1,1,4,1], // 94
|
|
98
|
-
[1,1,4,1,1,3], // 95
|
|
99
|
-
[1,1,4,3,1,1], // 96
|
|
100
|
-
[4,1,1,1,1,3], // 97
|
|
101
|
-
[4,1,1,3,1,1], // 98
|
|
102
|
-
[1,1,3,1,4,1], // 99
|
|
103
|
-
[1,1,4,1,3,1], // 100
|
|
104
|
-
[3,1,1,1,4,1], // 101
|
|
105
|
-
[4,1,1,1,3,1], // 102
|
|
106
|
-
[2,1,1,4,1,2], // 103 START A
|
|
107
|
-
[2,1,1,2,1,4], // 104 START B
|
|
108
|
-
[2,1,1,2,3,2], // 105 START C
|
|
109
|
-
[2,3,3,1,1,1,2], // 106 STOP (7 modules!)
|
|
110
|
-
];
|
|
111
|
-
|
|
112
|
-
const arr_to_intkey = (arr)=> parseInt(arr.join(""),5);
|
|
113
|
-
|
|
114
|
-
const [MIN_KEY,CODE128_PTRN_TO_CODE] = JSON.parse(JSON.stringify(
|
|
115
|
-
(() => {
|
|
116
|
-
const mp = {};
|
|
117
|
-
for (let i = 0; i < CODE128_CODE_TO_PTRN.length; i++) {
|
|
118
|
-
const arr = CODE128_CODE_TO_PTRN[i];
|
|
119
|
-
mp[arr_to_intkey(arr)]=i;
|
|
120
|
-
}
|
|
121
|
-
let ks = Object.keys(mp);
|
|
122
|
-
ks = ks.map(r=>Number(r));
|
|
123
|
-
ks.sort((a,b)=>a-b);
|
|
124
|
-
let MIN = ks[0];
|
|
125
|
-
let MAX = ks[ks.length-1];
|
|
126
|
-
let arr = [];
|
|
127
|
-
for(let i=0;i<=MAX;++i) {arr[i] = 0;}
|
|
128
|
-
for(let k of ks) { arr[k-MIN] = mp[k];}
|
|
129
|
-
return [MIN,arr];
|
|
130
|
-
})()
|
|
131
|
-
));
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
const code128_code_to_ptrn = (cd) => CODE128_CODE_TO_PTRN[cd]; //->Array<>
|
|
135
|
-
const code128_ptrn_to_code = (arr)=> {
|
|
136
|
-
const key = arr_to_intkey(arr);
|
|
137
|
-
const idx = key - MIN_KEY;
|
|
138
|
-
return CODE128_PTRN_TO_CODE[idx];
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
module.exports = {
|
|
143
|
-
CODE128_CODE_TO_PTRN,
|
|
144
|
-
MIN_KEY,
|
|
145
|
-
CODE128_PTRN_TO_CODE,
|
|
146
|
-
////
|
|
147
|
-
arr_to_intkey,
|
|
148
|
-
code128_code_to_ptrn,
|
|
149
|
-
code128_ptrn_to_code
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
if(module.id===".") {
|
|
153
|
-
for(let i=0;i<CODE128_CODE_TO_PTRN.length;++i) {
|
|
154
|
-
let expected = CODE128_CODE_TO_PTRN[i];
|
|
155
|
-
let actual = code128_ptrn_to_code(CODE128_CODE_TO_PTRN[i]);
|
|
156
|
-
if(actual!== i) {
|
|
157
|
-
throw({expected,actual})
|
|
158
|
-
} else {
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
} else {}
|