taro-bluetooth-print 2.3.1 → 2.4.0

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.
Files changed (35) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +6 -1
  3. package/dist/index.cjs.js +1 -1
  4. package/dist/index.es.js +1 -1
  5. package/dist/index.umd.js +1 -1
  6. package/dist/types/config/PrinterConfigManager.d.ts +206 -0
  7. package/dist/types/config/index.d.ts +8 -0
  8. package/dist/types/device/MultiPrinterManager.d.ts +164 -0
  9. package/dist/types/device/index.d.ts +2 -0
  10. package/dist/types/index.d.ts +5 -1
  11. package/dist/types/services/BatchPrintManager.d.ts +205 -0
  12. package/dist/types/services/PrintHistory.d.ts +142 -0
  13. package/dist/types/services/PrintJobManager.d.ts +28 -4
  14. package/dist/types/services/PrinterStatus.d.ts +97 -0
  15. package/dist/types/services/index.d.ts +3 -0
  16. package/package.json +2 -2
  17. package/src/adapters/AlipayAdapter.ts +1 -0
  18. package/src/adapters/BaiduAdapter.ts +1 -0
  19. package/src/adapters/ByteDanceAdapter.ts +1 -0
  20. package/src/adapters/TaroAdapter.ts +1 -0
  21. package/src/adapters/WebBluetoothAdapter.ts +1 -1
  22. package/src/config/PrinterConfigManager.ts +519 -0
  23. package/src/config/index.ts +15 -0
  24. package/src/device/MultiPrinterManager.ts +470 -0
  25. package/src/device/index.ts +8 -0
  26. package/src/encoding/gbk-lite.ts +81 -76
  27. package/src/encoding/gbk-table.ts +14 -14
  28. package/src/index.ts +16 -1
  29. package/src/services/BatchPrintManager.ts +500 -0
  30. package/src/services/ConnectionManager.ts +4 -1
  31. package/src/services/PrintHistory.ts +336 -0
  32. package/src/services/PrintJobManager.ts +69 -9
  33. package/src/services/PrinterStatus.ts +267 -0
  34. package/src/services/index.ts +6 -0
  35. package/src/template/TemplateEngine.ts +4 -1
@@ -0,0 +1,519 @@
1
+ /**
2
+ * Printer Configuration Manager
3
+ *
4
+ * Manages persistent printer configurations including:
5
+ * - Saved devices and their settings
6
+ * - Default print parameters
7
+ * - User preferences
8
+ *
9
+ * Supports localStorage and file-based storage backends.
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * const configManager = new PrinterConfigManager();
14
+ *
15
+ * // Save a printer configuration
16
+ * configManager.savePrinter({
17
+ * id: 'my-printer',
18
+ * deviceId: 'XX:XX:XX:XX:XX:XX',
19
+ * name: 'Kitchen Printer',
20
+ * isDefault: true
21
+ * });
22
+ *
23
+ * // Get all saved printers
24
+ * const printers = configManager.getSavedPrinters();
25
+ *
26
+ * // Load configuration for a printer
27
+ * const config = configManager.loadPrinterConfig('my-printer');
28
+ * ```
29
+ */
30
+
31
+ import { Logger } from '@/utils/logger';
32
+
33
+ /**
34
+ * Print configuration for a device
35
+ */
36
+ export interface PrintConfig {
37
+ /** Encoding (default: 'GBK') */
38
+ encoding?: string;
39
+ /** Chunk size for data transfer */
40
+ chunkSize?: number;
41
+ /** Delay between chunks in ms */
42
+ chunkDelay?: number;
43
+ /** Number of retries */
44
+ retries?: number;
45
+ }
46
+
47
+ /**
48
+ * Saved printer configuration
49
+ */
50
+ export interface SavedPrinter {
51
+ /** Unique printer ID */
52
+ id: string;
53
+ /** Bluetooth device ID */
54
+ deviceId: string;
55
+ /** Friendly name */
56
+ name: string;
57
+ /** Printer model/type */
58
+ model?: string;
59
+ /** Whether this is the default printer */
60
+ isDefault?: boolean;
61
+ /** Print configuration */
62
+ printConfig?: PrintConfig;
63
+ /** Auto-reconnect on disconnect */
64
+ autoReconnect?: boolean;
65
+ /** Last connected timestamp */
66
+ lastConnected?: number;
67
+ /** Creation timestamp */
68
+ createdAt: number;
69
+ /** Update timestamp */
70
+ updatedAt: number;
71
+ }
72
+
73
+ /**
74
+ * Global configuration
75
+ */
76
+ export interface GlobalConfig {
77
+ /** Default encoding */
78
+ defaultEncoding: string;
79
+ /** Default chunk size */
80
+ defaultChunkSize: number;
81
+ /** Default chunk delay */
82
+ defaultChunkDelay: number;
83
+ /** Default retries */
84
+ defaultRetries: number;
85
+ /** Auto-reconnect by default */
86
+ defaultAutoReconnect: boolean;
87
+ /** Scan timeout in ms */
88
+ scanTimeout: number;
89
+ /** Enable logging */
90
+ enableLogging: boolean;
91
+ }
92
+
93
+ /**
94
+ * Configuration storage interface
95
+ */
96
+ export interface IConfigStorage {
97
+ get<T>(key: string, defaultValue: T): T;
98
+ set<T>(key: string, value: T): void;
99
+ remove(key: string): void;
100
+ clear(): void;
101
+ }
102
+
103
+ /**
104
+ * LocalStorage-based implementation
105
+ */
106
+ export class LocalStorage implements IConfigStorage {
107
+ private prefix: string;
108
+
109
+ constructor(prefix = 'taro_bt_print_') {
110
+ this.prefix = prefix;
111
+ }
112
+
113
+ get<T>(key: string, defaultValue: T): T {
114
+ try {
115
+ const stored = localStorage.getItem(this.prefix + key);
116
+ if (stored === null) {
117
+ return defaultValue;
118
+ }
119
+ return JSON.parse(stored) as T;
120
+ } catch {
121
+ return defaultValue;
122
+ }
123
+ }
124
+
125
+ set<T>(key: string, value: T): void {
126
+ try {
127
+ localStorage.setItem(this.prefix + key, JSON.stringify(value));
128
+ } catch (error) {
129
+ Logger.scope('ConfigStorage').error(`Failed to save "${key}":`, error);
130
+ }
131
+ }
132
+
133
+ remove(key: string): void {
134
+ try {
135
+ localStorage.removeItem(this.prefix + key);
136
+ } catch (error) {
137
+ Logger.scope('ConfigStorage').error(`Failed to remove "${key}":`, error);
138
+ }
139
+ }
140
+
141
+ clear(): void {
142
+ try {
143
+ const keysToRemove: string[] = [];
144
+ for (let i = 0; i < localStorage.length; i++) {
145
+ const key = localStorage.key(i);
146
+ if (key?.startsWith(this.prefix)) {
147
+ keysToRemove.push(key);
148
+ }
149
+ }
150
+ keysToRemove.forEach(key => localStorage.removeItem(key));
151
+ } catch (error) {
152
+ Logger.scope('ConfigStorage').error('Failed to clear storage:', error);
153
+ }
154
+ }
155
+ }
156
+
157
+ /**
158
+ * Default global configuration
159
+ */
160
+ const DEFAULT_GLOBAL_CONFIG: GlobalConfig = {
161
+ defaultEncoding: 'GBK',
162
+ defaultChunkSize: 20,
163
+ defaultChunkDelay: 20,
164
+ defaultRetries: 3,
165
+ defaultAutoReconnect: true,
166
+ scanTimeout: 15000,
167
+ enableLogging: true,
168
+ };
169
+
170
+ /**
171
+ * Storage keys
172
+ */
173
+ const KEYS = {
174
+ PRINTERS: 'printers',
175
+ GLOBAL_CONFIG: 'global_config',
176
+ LAST_USED_PRINTER: 'last_used_printer',
177
+ } as const;
178
+
179
+ /**
180
+ * Printer Configuration Manager
181
+ */
182
+ export class PrinterConfigManager {
183
+ private readonly logger = Logger.scope('PrinterConfigManager');
184
+ private readonly storage: IConfigStorage;
185
+ private printers: Map<string, SavedPrinter> = new Map();
186
+ private globalConfig: GlobalConfig;
187
+ private lastUsedPrinterId: string | null = null;
188
+
189
+ /**
190
+ * Creates a new PrinterConfigManager instance
191
+ *
192
+ * @param storage - Storage backend (defaults to LocalStorage)
193
+ */
194
+ constructor(storage?: IConfigStorage) {
195
+ this.storage = storage || new LocalStorage();
196
+ this.globalConfig = { ...DEFAULT_GLOBAL_CONFIG };
197
+ this.load();
198
+ }
199
+
200
+ /**
201
+ * Load configuration from storage
202
+ */
203
+ private load(): void {
204
+ try {
205
+ // Load printers
206
+ const printers = this.storage.get<SavedPrinter[]>(KEYS.PRINTERS, []);
207
+ this.printers.clear();
208
+ for (const printer of printers) {
209
+ if (printer.id) {
210
+ this.printers.set(printer.id, printer);
211
+ }
212
+ }
213
+
214
+ // Load global config
215
+ const globalConfig = this.storage.get<GlobalConfig | null>(KEYS.GLOBAL_CONFIG, null);
216
+ if (globalConfig) {
217
+ this.globalConfig = { ...DEFAULT_GLOBAL_CONFIG, ...globalConfig };
218
+ }
219
+
220
+ // Load last used
221
+ this.lastUsedPrinterId = this.storage.get<string | null>(KEYS.LAST_USED_PRINTER, null);
222
+
223
+ this.logger.debug(`Loaded ${this.printers.size} saved printers`);
224
+ } catch (error) {
225
+ this.logger.error('Failed to load configuration:', error);
226
+ }
227
+ }
228
+
229
+ /**
230
+ * Save configuration to storage
231
+ */
232
+ private save(): void {
233
+ try {
234
+ // Save printers
235
+ const printersArray = Array.from(this.printers.values());
236
+ this.storage.set(KEYS.PRINTERS, printersArray);
237
+
238
+ // Save global config
239
+ this.storage.set(KEYS.GLOBAL_CONFIG, this.globalConfig);
240
+
241
+ // Save last used
242
+ if (this.lastUsedPrinterId) {
243
+ this.storage.set(KEYS.LAST_USED_PRINTER, this.lastUsedPrinterId);
244
+ } else {
245
+ this.storage.remove(KEYS.LAST_USED_PRINTER);
246
+ }
247
+ } catch (error) {
248
+ this.logger.error('Failed to save configuration:', error);
249
+ }
250
+ }
251
+
252
+ /**
253
+ * Save a printer configuration
254
+ *
255
+ * @param printer - Printer configuration to save
256
+ * @returns The saved printer ID
257
+ */
258
+ savePrinter(printer: SavedPrinter): string {
259
+ const now = Date.now();
260
+ const existing = this.printers.get(printer.id);
261
+
262
+ const savedPrinter: SavedPrinter = {
263
+ ...printer,
264
+ id: printer.id,
265
+ createdAt: existing?.createdAt ?? now,
266
+ updatedAt: now,
267
+ };
268
+
269
+ // If setting as default, unset other defaults
270
+ if (savedPrinter.isDefault) {
271
+ for (const [id, p] of this.printers.entries()) {
272
+ if (id !== printer.id && p.isDefault) {
273
+ this.printers.set(id, { ...p, isDefault: false });
274
+ }
275
+ }
276
+ }
277
+
278
+ this.printers.set(printer.id, savedPrinter);
279
+ this.save();
280
+
281
+ this.logger.info(`Saved printer: ${printer.id} (${printer.name})`);
282
+ return printer.id;
283
+ }
284
+
285
+ /**
286
+ * Get a saved printer by ID
287
+ */
288
+ getPrinter(id: string): SavedPrinter | undefined {
289
+ return this.printers.get(id);
290
+ }
291
+
292
+ /**
293
+ * Get all saved printers
294
+ */
295
+ getSavedPrinters(): SavedPrinter[] {
296
+ return Array.from(this.printers.values());
297
+ }
298
+
299
+ /**
300
+ * Get default printer
301
+ */
302
+ getDefaultPrinter(): SavedPrinter | undefined {
303
+ for (const printer of this.printers.values()) {
304
+ if (printer.isDefault) {
305
+ return printer;
306
+ }
307
+ }
308
+
309
+ // Fall back to last used
310
+ if (this.lastUsedPrinterId) {
311
+ return this.printers.get(this.lastUsedPrinterId);
312
+ }
313
+
314
+ return undefined;
315
+ }
316
+
317
+ /**
318
+ * Remove a saved printer
319
+ */
320
+ removePrinter(id: string): boolean {
321
+ const deleted = this.printers.delete(id);
322
+ if (deleted) {
323
+ this.save();
324
+ this.logger.info(`Removed printer: ${id}`);
325
+
326
+ if (this.lastUsedPrinterId === id) {
327
+ this.lastUsedPrinterId = null;
328
+ this.save();
329
+ }
330
+ }
331
+ return deleted;
332
+ }
333
+
334
+ /**
335
+ * Set printer as default
336
+ */
337
+ setDefaultPrinter(id: string): void {
338
+ const printer = this.printers.get(id);
339
+ if (!printer) {
340
+ throw new Error(`Printer not found: ${id}`);
341
+ }
342
+
343
+ // Unset other defaults
344
+ for (const [pid, p] of this.printers.entries()) {
345
+ if (p.isDefault && pid !== id) {
346
+ this.printers.set(pid, { ...p, isDefault: false });
347
+ }
348
+ }
349
+
350
+ // Set this one as default
351
+ this.printers.set(id, { ...printer, isDefault: true });
352
+ this.save();
353
+
354
+ this.logger.info(`Set default printer: ${id}`);
355
+ }
356
+
357
+ /**
358
+ * Update last used printer
359
+ */
360
+ setLastUsed(id: string): void {
361
+ if (!this.printers.has(id)) {
362
+ this.logger.warn(`Cannot set last used: printer not found ${id}`);
363
+ return;
364
+ }
365
+
366
+ this.lastUsedPrinterId = id;
367
+
368
+ // Also update lastConnected timestamp
369
+ const printer = this.printers.get(id);
370
+ if (printer) {
371
+ this.printers.set(id, { ...printer, lastConnected: Date.now() });
372
+ }
373
+
374
+ this.save();
375
+ }
376
+
377
+ /**
378
+ * Get last used printer ID
379
+ */
380
+ getLastUsedId(): string | null {
381
+ return this.lastUsedPrinterId;
382
+ }
383
+
384
+ /**
385
+ * Get global configuration
386
+ */
387
+ getGlobalConfig(): GlobalConfig {
388
+ return { ...this.globalConfig };
389
+ }
390
+
391
+ /**
392
+ * Update global configuration
393
+ */
394
+ updateGlobalConfig(updates: Partial<GlobalConfig>): void {
395
+ this.globalConfig = { ...this.globalConfig, ...updates };
396
+ this.save();
397
+ this.logger.debug('Global config updated');
398
+ }
399
+
400
+ /**
401
+ * Reset global config to defaults
402
+ */
403
+ resetGlobalConfig(): void {
404
+ this.globalConfig = { ...DEFAULT_GLOBAL_CONFIG };
405
+ this.save();
406
+ this.logger.info('Global config reset to defaults');
407
+ }
408
+
409
+ /**
410
+ * Load print config for a printer (with defaults applied)
411
+ */
412
+ loadPrinterConfig(printerId: string): PrintConfig {
413
+ const printer = this.printers.get(printerId);
414
+ const printConfig = printer?.printConfig || {};
415
+
416
+ return {
417
+ encoding: printConfig.encoding ?? this.globalConfig.defaultEncoding,
418
+ chunkSize: printConfig.chunkSize ?? this.globalConfig.defaultChunkSize,
419
+ chunkDelay: printConfig.chunkDelay ?? this.globalConfig.defaultChunkDelay,
420
+ retries: printConfig.retries ?? this.globalConfig.defaultRetries,
421
+ };
422
+ }
423
+
424
+ /**
425
+ * Export all configuration as JSON
426
+ */
427
+ export(): string {
428
+ return JSON.stringify({
429
+ printers: Array.from(this.printers.values()),
430
+ globalConfig: this.globalConfig,
431
+ lastUsedPrinterId: this.lastUsedPrinterId,
432
+ exportedAt: Date.now(),
433
+ }, null, 2);
434
+ }
435
+
436
+ /**
437
+ * Import configuration from JSON
438
+ *
439
+ * @param json - JSON string to import
440
+ * @param merge - If true, merge with existing config; if false, replace
441
+ * @returns Number of printers imported
442
+ */
443
+ import(json: string, merge = false): number {
444
+ try {
445
+ const data = JSON.parse(json) as {
446
+ printers?: SavedPrinter[];
447
+ globalConfig?: GlobalConfig;
448
+ lastUsedPrinterId?: string;
449
+ };
450
+
451
+ let imported = 0;
452
+
453
+ if (!merge) {
454
+ this.printers.clear();
455
+ }
456
+
457
+ if (data.printers && Array.isArray(data.printers)) {
458
+ for (const printer of data.printers) {
459
+ if (printer.id) {
460
+ this.printers.set(printer.id, printer);
461
+ imported++;
462
+ }
463
+ }
464
+ }
465
+
466
+ if (data.globalConfig) {
467
+ this.globalConfig = { ...DEFAULT_GLOBAL_CONFIG, ...data.globalConfig };
468
+ }
469
+
470
+ if (data.lastUsedPrinterId && this.printers.has(data.lastUsedPrinterId)) {
471
+ this.lastUsedPrinterId = data.lastUsedPrinterId;
472
+ }
473
+
474
+ this.save();
475
+ this.logger.info(`Imported ${imported} printers`);
476
+
477
+ return imported;
478
+ } catch (error) {
479
+ this.logger.error('Failed to import configuration:', error);
480
+ return 0;
481
+ }
482
+ }
483
+
484
+ /**
485
+ * Clear all configuration
486
+ */
487
+ clear(): void {
488
+ this.printers.clear();
489
+ this.lastUsedPrinterId = null;
490
+ this.storage.clear();
491
+ this.logger.info('All configuration cleared');
492
+ }
493
+
494
+ /**
495
+ * Get statistics
496
+ */
497
+ getStats(): {
498
+ printerCount: number;
499
+ hasDefault: boolean;
500
+ lastUsed: string | null;
501
+ } {
502
+ let hasDefault = false;
503
+ for (const printer of this.printers.values()) {
504
+ if (printer.isDefault) {
505
+ hasDefault = true;
506
+ break;
507
+ }
508
+ }
509
+
510
+ return {
511
+ printerCount: this.printers.size,
512
+ hasDefault,
513
+ lastUsed: this.lastUsedPrinterId,
514
+ };
515
+ }
516
+ }
517
+
518
+ // Export singleton for convenience
519
+ export const printerConfigManager = new PrinterConfigManager();
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Config Module
3
+ * 配置模块 - 提供打印机配置管理
4
+ */
5
+
6
+ export { PrinterConfigManager, printerConfigManager } from './PrinterConfigManager';
7
+ export type {
8
+ SavedPrinter,
9
+ PrintConfig,
10
+ GlobalConfig,
11
+ IConfigStorage,
12
+ } from './PrinterConfigManager';
13
+
14
+ export { DEFAULT_CONFIG, mergeConfig } from './PrinterConfig';
15
+ export type { PrinterConfig, AdapterConfig, DriverConfig, LoggingConfig } from './PrinterConfig';