nv-img-barcode-bw 1.0.1

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/css.js ADDED
@@ -0,0 +1,170 @@
1
+ module.exports = `
2
+ <style>
3
+ :host {
4
+ display:inline-block;
5
+ overflow: hidden;
6
+ vertical-align: top;
7
+ }
8
+ :host([hide-button="true"]) {
9
+ /* 1. 绝对定位脱离文档流,不占位 */
10
+ position: absolute;
11
+ /* 2. 缩到无限小 */
12
+ width: 0 !important;
13
+ height: 0 !important;
14
+ }
15
+ .barcode-btn {
16
+ padding: 12px 24px;
17
+ background: var(--barcode-btn-background);
18
+ color: var(--barcode-btn-color);
19
+ border: none;
20
+ border-radius: var(--barcode-btn-border-radius);
21
+ font-size: var(--barcode-btn-font-size);
22
+ font-weight: 600;
23
+ cursor: pointer;
24
+ transition: all 0.3s ease;
25
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
26
+ display: inline-flex;
27
+ align-items: center;
28
+ justify-content: center;
29
+ gap: 8px;
30
+ }
31
+ .barcode-btn:hover {
32
+ transform: translateY(-2px);
33
+ box-shadow: 0 5px 15px rgba(0,0,0,0.2);
34
+ }
35
+ .barcode-btn:active {
36
+ transform: translateY(0);
37
+ }
38
+ .barcode-btn:disabled {
39
+ opacity: 0.6;
40
+ cursor: not-allowed;
41
+ }
42
+
43
+
44
+ .loading {
45
+ display: inline-block;
46
+ width: 20px;
47
+ height: 20px;
48
+ border: 3px solid rgba(255,255,255,0.3);
49
+ border-top: 3px solid var(--barcode-btn-color);
50
+ border-radius: 50%;
51
+ animation: spin 1s linear infinite;
52
+ }
53
+
54
+ @keyframes spin {
55
+ 0% { transform: rotate(0deg); }
56
+ 100% { transform: rotate(360deg); }
57
+ }
58
+
59
+ /* 弹窗样式 */
60
+ .barcode-popup {
61
+ position: fixed;
62
+ top: 0;
63
+ left: 0;
64
+ right: 0;
65
+ bottom: 0;
66
+ background: rgba(0,0,0,0.8);
67
+ display: flex;
68
+ align-items: center;
69
+ justify-content: center;
70
+ z-index: 10000;
71
+ }
72
+
73
+ .popup-content {
74
+ background: white;
75
+ padding: 30px;
76
+ border-radius: 15px;
77
+ max-width: 90%;
78
+ max-height: 90%;
79
+ overflow: auto;
80
+ text-align: center;
81
+ box-shadow: 0 20px 60px rgba(0,0,0,0.3);
82
+ position: relative;
83
+ }
84
+
85
+ .popup-close {
86
+ position: absolute;
87
+ top: 15px;
88
+ right: 15px;
89
+ background: none;
90
+ border: none;
91
+ font-size: 28px;
92
+ color: #666;
93
+ cursor: pointer;
94
+ padding: 5px;
95
+ line-height: 1;
96
+ width: 40px;
97
+ height: 40px;
98
+ display: flex;
99
+ align-items: center;
100
+ justify-content: center;
101
+ border-radius: 50%;
102
+ }
103
+
104
+ .popup-close:hover {
105
+ background: #f7fafc;
106
+ color: #e53e3e;
107
+ }
108
+
109
+ .barcode-container {
110
+ margin: 20px 0;
111
+ padding: 20px;
112
+ background: var(--barcode-container-background);
113
+ border-radius: 8px;
114
+ display: inline-block;
115
+ }
116
+
117
+ .barcode-image {
118
+ max-width: 100%;
119
+ max-height: 300px;
120
+ }
121
+
122
+ .popup-info {
123
+ margin: 20px 0;
124
+ padding: 15px;
125
+ background: #f7fafc;
126
+ border-radius: 8px;
127
+ text-align: left;
128
+ }
129
+ .barcode-line-color {
130
+ color: var(--barcode-line-color);
131
+ }
132
+
133
+
134
+ .popup-actions {
135
+ display: flex;
136
+ gap: 10px;
137
+ justify-content: center;
138
+ margin-top: 20px;
139
+ flex-wrap: wrap;
140
+ }
141
+
142
+ .action-btn {
143
+ padding: 10px 20px;
144
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
145
+ color: white;
146
+ border: none;
147
+ border-radius: 6px;
148
+ font-size: 14px;
149
+ font-weight: 600;
150
+ cursor: pointer;
151
+ transition: all 0.3s;
152
+ display: flex;
153
+ align-items: center;
154
+ gap: 8px;
155
+ }
156
+
157
+ .action-btn:hover {
158
+ transform: translateY(-2px);
159
+ box-shadow: 0 5px 15px rgba(0,0,0,0.2);
160
+ }
161
+
162
+ .action-btn.secondary {
163
+ background: linear-gradient(135deg, #4fd1c7 0%, #319795 100%);
164
+ }
165
+
166
+ .action-btn.tertiary {
167
+ background: linear-gradient(135deg, #fc8181 0%, #c53030 100%);
168
+ }
169
+ </style>
170
+ `;
package/decd.js ADDED
@@ -0,0 +1,270 @@
1
+ const { svg2str } = require("./eng");
2
+
3
+
4
+ module.exports = async (o=null, barcodeType = null,show_dialog=false)=> {
5
+ let svgContent = "";
6
+ let fileName = "unknown";
7
+ let fileSize = 0;
8
+
9
+ // Helper: 从blob/arraybuffer/file创建svg
10
+ async function createSvgFromImageSource(source, mimeType = "") {
11
+ return new Promise((resolve, reject) => {
12
+ const reader = new FileReader();
13
+ const img = new Image();
14
+
15
+ reader.onload = (e) => {
16
+ img.onload = () => {
17
+ const svg = `
18
+ <svg xmlns="http://www.w3.org/2000/svg"
19
+ xmlns:xlink="http://www.w3.org/1999/xlink"
20
+ width="${img.width}"
21
+ height="${img.height}"
22
+ viewBox="0 0 ${img.width} ${img.height}">
23
+ <image xlink:href="${e.target.result}"
24
+ width="${img.width}"
25
+ height="${img.height}"
26
+ preserveAspectRatio="none"/>
27
+ </svg>`;
28
+ resolve(svg);
29
+ };
30
+ img.onerror = () => reject(new Error("图片加载失败"));
31
+ img.src = e.target.result;
32
+ };
33
+ reader.onerror = () => reject(new Error("文件读取失败"));
34
+
35
+ if (source instanceof Blob || source instanceof File) {
36
+ reader.readAsDataURL(source);
37
+ } else if (source instanceof ArrayBuffer) {
38
+ const blob = new Blob([source], { type: mimeType || "image/png" });
39
+ reader.readAsDataURL(blob);
40
+ } else if (ArrayBuffer.isView(source)) {
41
+ const blob = new Blob([source], { type: mimeType || "image/png" });
42
+ reader.readAsDataURL(blob);
43
+ } else {
44
+ reject(new Error("不支持的图片源类型"));
45
+ }
46
+ });
47
+ }
48
+
49
+ // Helper: 读取文件为文本
50
+ async function readFileAsText(file) {
51
+ return new Promise((resolve, reject) => {
52
+ const reader = new FileReader();
53
+ reader.onload = (e) => resolve(e.target.result);
54
+ reader.onerror = () => reject(new Error("文件读取失败"));
55
+ reader.readAsText(file);
56
+ });
57
+ }
58
+
59
+ // Helper: 通过文件选择器选择文件
60
+ async function selectFileViaPicker() {
61
+ if (!window.showOpenFilePicker) {
62
+ throw new Error("您的浏览器不支持 showOpenFilePicker API");
63
+ }
64
+
65
+ try {
66
+ const [fileHandle] = await window.showOpenFilePicker({
67
+ types: [{
68
+ description: "图片文件",
69
+ accept: {
70
+ "image/*": [".png", ".jpg", ".jpeg", ".webp", ".gif", ".bmp", ".svg"]
71
+ }
72
+ }],
73
+ multiple: false
74
+ });
75
+
76
+ const file = await fileHandle.getFile();
77
+ fileName = file.name;
78
+ fileSize = file.size;
79
+
80
+ if (file.type === "image/svg+xml" || file.name.endsWith(".svg")) {
81
+ return await readFileAsText(file);
82
+ } else {
83
+ return await createSvgFromImageSource(file);
84
+ }
85
+ } catch (error) {
86
+ if (error.name === "AbortError") {
87
+ throw new Error("用户取消选择");
88
+ }
89
+ throw error;
90
+ }
91
+ }
92
+
93
+ // Helper: 显示结果对话框
94
+ function showResultDialog(decodedText) {
95
+ return new Promise((resolve) => {
96
+ const dialog = document.createElement('div');
97
+ dialog.style.cssText = `
98
+ position: fixed;
99
+ top: 0;
100
+ left: 0;
101
+ width: 100%;
102
+ height: 100%;
103
+ background: rgba(0,0,0,0.5);
104
+ display: flex;
105
+ justify-content: center;
106
+ align-items: center;
107
+ z-index: 10000;
108
+ `;
109
+
110
+ const card = document.createElement('div');
111
+ card.style.cssText = `
112
+ background: white;
113
+ padding: 20px;
114
+ border-radius: 8px;
115
+ box-shadow: 0 4px 20px rgba(0,0,0,0.2);
116
+ min-width: 300px;
117
+ max-width: 500px;
118
+ max-height: 80vh;
119
+ overflow-y: auto;
120
+ `;
121
+
122
+ const title = document.createElement('h3');
123
+ title.textContent = '解码结果';
124
+ title.style.cssText = 'margin: 0 0 15px 0; color: #333;';
125
+
126
+ const textArea = document.createElement('textarea');
127
+ textArea.value = decodedText;
128
+ textArea.style.cssText = `
129
+ width: 100%;
130
+ min-height: 100px;
131
+ padding: 10px;
132
+ border: 1px solid #ddd;
133
+ border-radius: 4px;
134
+ font-family: monospace;
135
+ font-size: 14px;
136
+ resize: vertical;
137
+ margin-bottom: 15px;
138
+ box-sizing: border-box;
139
+ `;
140
+
141
+ const buttonContainer = document.createElement('div');
142
+ buttonContainer.style.cssText = 'display: flex; justify-content: flex-end; gap: 10px;';
143
+
144
+ const copyBtn = document.createElement('button');
145
+ copyBtn.textContent = '复制';
146
+ copyBtn.style.cssText = `
147
+ background: #1890ff;
148
+ color: white;
149
+ border: none;
150
+ padding: 8px 20px;
151
+ border-radius: 4px;
152
+ cursor: pointer;
153
+ font-size: 14px;
154
+ `;
155
+
156
+ const closeBtn = document.createElement('button');
157
+ closeBtn.textContent = '关闭';
158
+ closeBtn.style.cssText = `
159
+ background: #f5f5f5;
160
+ color: #333;
161
+ border: 1px solid #d9d9d9;
162
+ padding: 8px 20px;
163
+ border-radius: 4px;
164
+ cursor: pointer;
165
+ font-size: 14px;
166
+ `;
167
+
168
+ copyBtn.onclick = () => {
169
+ textArea.select();
170
+ document.execCommand('copy');
171
+ const originalText = copyBtn.textContent;
172
+ copyBtn.textContent = '已复制!';
173
+ copyBtn.style.background = '#52c41a';
174
+ setTimeout(() => {
175
+ copyBtn.textContent = originalText;
176
+ copyBtn.style.background = '#1890ff';
177
+ }, 2000);
178
+ };
179
+
180
+ closeBtn.onclick = () => {
181
+ document.body.removeChild(dialog);
182
+ resolve();
183
+ };
184
+
185
+ card.appendChild(title);
186
+ card.appendChild(textArea);
187
+ buttonContainer.appendChild(copyBtn);
188
+ buttonContainer.appendChild(closeBtn);
189
+ card.appendChild(buttonContainer);
190
+ dialog.appendChild(card);
191
+ document.body.appendChild(dialog);
192
+
193
+ // 点击背景关闭
194
+ dialog.onclick = (e) => {
195
+ if (e.target === dialog) {
196
+ document.body.removeChild(dialog);
197
+ resolve();
198
+ }
199
+ };
200
+
201
+ // 自动选择文本
202
+ textArea.select();
203
+ });
204
+ }
205
+
206
+ try {
207
+ // 1. HTMLInputElement (type="file")
208
+ if (o instanceof HTMLInputElement && o.type === 'file') {
209
+ if (!o.files || o.files.length === 0) {
210
+ throw new Error("请先选择文件");
211
+ }
212
+ const file = o.files[0];
213
+ fileName = file.name;
214
+ fileSize = file.size;
215
+
216
+ if (file.type === "image/svg+xml" || file.name.endsWith(".svg")) {
217
+ svgContent = await readFileAsText(file);
218
+ } else {
219
+ svgContent = await createSvgFromImageSource(file);
220
+ }
221
+ }
222
+ // 2. Blob对象
223
+ else if (o instanceof Blob) {
224
+ fileName = o.name || "blob";
225
+ fileSize = o.size;
226
+
227
+ if (o.type === "image/svg+xml") {
228
+ svgContent = await readFileAsText(o);
229
+ } else {
230
+ svgContent = await createSvgFromImageSource(o);
231
+ }
232
+ }
233
+ // 3. ArrayBuffer
234
+ else if (o instanceof ArrayBuffer) {
235
+ fileName = "arraybuffer";
236
+ fileSize = o.byteLength;
237
+ svgContent = await createSvgFromImageSource(o);
238
+ }
239
+ // 4. TypedArray (Uint8Array, etc.)
240
+ else if (ArrayBuffer.isView(o)) {
241
+ fileName = "typedarray";
242
+ fileSize = o.byteLength;
243
+ svgContent = await createSvgFromImageSource(o);
244
+ }
245
+ // 5. 字符串 (假设是SVG字符串)
246
+ else if (typeof(o) === "string") {
247
+ svgContent = o;
248
+ }
249
+ // 6. 其他情况 (undefined/null/无参数) - 使用文件选择器
250
+ else {
251
+ svgContent = await selectFileViaPicker();
252
+ }
253
+
254
+ // 解码SVG
255
+ const decodedText = await svg2str(svgContent, barcodeType);
256
+ // 显示结果对话框
257
+ if(show_dialog) {
258
+ await showResultDialog(decodedText);
259
+ } else {}
260
+ return [true, decodedText]
261
+
262
+ } catch (error) {
263
+ alert(`解码失败: ${error.message}`);
264
+ console.error("解码失败:", error);
265
+ return [false,error]
266
+ }
267
+ };
268
+
269
+
270
+