uview-ultra 4.5.46 → 4.5.48

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.
@@ -31,12 +31,12 @@
31
31
  </view>
32
32
  </template>
33
33
 
34
- <script setup lang="uts">
35
- import { computed, getCurrentInstance, nextTick, onMounted, ref, watch } from 'vue'
36
-
37
- function stringValue(value: any | null): string {
38
- return value == null ? '' : value.toString()
39
- }
34
+ <script setup lang="uts">
35
+ import { computed, getCurrentInstance, nextTick, onMounted, ref, watch } from 'vue'
36
+
37
+ function stringValue(value: any | null): string {
38
+ return value == null ? '' : value.toString()
39
+ }
40
40
 
41
41
  function normalizeNumber(value: any | null, defaultValue: number): number {
42
42
  if (typeof value === 'number') {
@@ -155,626 +155,627 @@ function code39Map(): UTSJSONObject {
155
155
  } as UTSJSONObject
156
156
  }
157
157
 
158
- defineOptions({
159
- styleIsolation: 'app-and-page',
160
- name: 'up-barcode'
161
- })
162
-
163
- const props = defineProps({
164
- value: {
165
- type: [String, Number],
166
- default: ''
167
- },
168
- format: {
169
- type: String,
170
- default: 'auto'
171
- },
172
- width: {
173
- type: Number,
174
- default: 200
175
- },
176
- height: {
177
- type: Number,
178
- default: 80
179
- },
180
- displayValue: {
181
- type: Boolean,
182
- default: true
183
- },
184
- text: {
185
- type: String,
186
- default: ''
187
- },
188
- fontOptions: {
189
- type: String,
190
- default: ''
191
- },
192
- font: {
193
- type: String,
194
- default: 'monospace'
195
- },
196
- textAlign: {
197
- type: String,
198
- default: 'center'
199
- },
200
- textPosition: {
201
- type: String,
202
- default: 'bottom'
203
- },
204
- textMargin: {
205
- type: Number,
206
- default: 2
207
- },
208
- fontSize: {
209
- type: Number,
210
- default: 14
211
- },
212
- background: {
213
- type: String,
214
- default: '#ffffff'
215
- },
216
- lineColor: {
217
- type: String,
218
- default: '#000000'
219
- },
220
- margin: {
221
- type: Number,
222
- default: 10
223
- },
224
- marginTop: {
225
- type: Number,
226
- default: -1
227
- },
228
- marginBottom: {
229
- type: Number,
230
- default: -1
231
- },
232
- marginLeft: {
233
- type: Number,
234
- default: -1
235
- },
236
- marginRight: {
237
- type: Number,
238
- default: -1
239
- },
240
- useCanvas: {
241
- type: Boolean,
242
- default: true
243
- }
244
- })
245
-
246
- const emit = defineEmits(['rendered', 'error'])
247
- const instance = getCurrentInstance()!.proxy!
248
-
249
- const canvasId = ref('up-barcode-' + Date.now().toString())
250
- const barcodeImage = ref('')
251
- const showCanvas = ref(false)
252
- const canvasContext = ref<CanvasContext | null>(null)
253
- const ctx = ref<CanvasRenderingContext2D | null>(null)
254
- const bars = ref<Array<UTSJSONObject>>([] as Array<UTSJSONObject>)
255
- const canvasWidth = ref(200)
256
- const canvasHeight = ref(80)
257
- const error = ref('')
258
-
259
- const marginTopValue = computed((): number => {
260
- return props.marginTop >= 0 ? props.marginTop : props.margin
261
- })
262
-
263
- const marginBottomValue = computed((): number => {
264
- return props.marginBottom >= 0 ? props.marginBottom : props.margin
265
- })
266
-
267
- const marginLeftValue = computed((): number => {
268
- return props.marginLeft >= 0 ? props.marginLeft : props.margin
269
- })
270
-
271
- const marginRightValue = computed((): number => {
272
- return props.marginRight >= 0 ? props.marginRight : props.margin
273
- })
274
-
275
- const displayText = computed((): string => {
276
- return props.text.length > 0 ? props.text : stringValue(props.value)
277
- })
278
-
279
- const showTopText = computed((): boolean => {
280
- return props.displayValue && props.textPosition == 'top' && error.value.length == 0
281
- })
282
-
283
- const showBottomText = computed((): boolean => {
284
- return props.displayValue && props.textPosition != 'top' && error.value.length == 0
285
- })
286
-
287
- const showImage = computed((): boolean => {
288
- return props.useCanvas == false && barcodeImage.value.length > 0 && error.value.length == 0
289
- })
290
-
291
- const rootStyle = computed((): UTSJSONObject => {
292
- return {
293
- width: canvasWidth.value.toString() + 'px',
294
- minHeight: canvasHeight.value.toString() + 'px',
295
- paddingTop: marginTopValue.value.toString() + 'px',
296
- paddingBottom: marginBottomValue.value.toString() + 'px',
297
- paddingLeft: marginLeftValue.value.toString() + 'px',
298
- paddingRight: marginRightValue.value.toString() + 'px',
299
- backgroundColor: props.background
300
- } as UTSJSONObject
301
- })
302
-
303
- const barcodeStyle = computed((): UTSJSONObject => {
304
- return {
305
- width: props.width.toString() + 'px',
306
- height: props.height.toString() + 'px',
307
- backgroundColor: props.background
308
- } as UTSJSONObject
309
- })
310
-
311
- const canvasStyle = computed((): UTSJSONObject => {
312
- return {
313
- width: canvasWidth.value.toString() + 'px',
314
- height: canvasHeight.value.toString() + 'px'
315
- } as UTSJSONObject
316
- })
317
-
318
- const imageStyle = computed((): UTSJSONObject => {
319
- return {
320
- width: canvasWidth.value.toString() + 'px',
321
- height: canvasHeight.value.toString() + 'px'
322
- } as UTSJSONObject
323
- })
324
-
325
- const textStyle = computed((): UTSJSONObject => {
326
- const style = {
327
- fontSize: props.fontSize.toString() + 'px',
328
- lineHeight: (props.fontSize + props.textMargin).toString() + 'px',
329
- color: props.lineColor,
330
- textAlign: props.textAlign
331
- } as UTSJSONObject
332
- return style
333
- })
334
-
335
- const getBarStyle = (bar: UTSJSONObject): UTSJSONObject => {
336
- return {
337
- width: normalizeNumber(bar['width'], 1).toString() + 'px',
338
- height: props.height.toString() + 'px',
339
- backgroundColor: stringValue(bar['color'])
340
- } as UTSJSONObject
341
- }
342
-
343
- const initCanvas = (): Promise<void> => {
344
- return new Promise((resolve, reject) => {
345
- uni.createCanvasContextAsync({
346
- id: canvasId.value.toString() as string.IDString,
347
- component: instance,
348
- success: (context: CanvasContext) => {
349
- canvasContext.value = context
350
- ctx.value = context.getContext('2d')
351
- resolve()
352
- },
353
- fail: (err: UniError) => {
354
- reject(err)
355
- }
356
- })
357
- })
358
- }
359
-
360
- const getErrorMessage = (err: any | null): string => {
361
- if (err == null) {
362
- return ''
363
- }
364
- const errObj = err as UTSJSONObject
365
- const message = errObj['message']
366
- return message == null ? err.toString() : message.toString()
367
- }
368
-
369
- const createBars = (bits: string): Array<UTSJSONObject> => {
370
- const list = [] as Array<UTSJSONObject>
371
- if (bits.length == 0) {
372
- return list
373
- }
374
- const moduleWidth = props.width / bits.length
375
- let current = bits.substring(0, 1)
376
- let count = 1
377
- for (let i = 1; i < bits.length; i++) {
378
- const bit = bits.substring(i, i + 1)
379
- if (bit == current) {
380
- count++
381
- } else {
382
- list.push({
383
- color: current == '1' ? props.lineColor : props.background,
384
- width: count * moduleWidth
385
- } as UTSJSONObject)
386
- current = bit
387
- count = 1
388
- }
389
- }
390
- list.push({
391
- color: current == '1' ? props.lineColor : props.background,
392
- width: count * moduleWidth
393
- } as UTSJSONObject)
394
- return list
395
- }
396
-
397
- const encodeCode128 = (data: string): string => {
398
- const chars = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
399
- const codes = [] as Array<number>
400
- let checksum = 104
401
- codes.push(104)
402
- for (let i = 0; i < data.length; i++) {
403
- const code = chars.indexOf(data.substring(i, i + 1))
404
- if (code < 0) {
405
- throw new Error('Invalid character in CODE128')
406
- }
407
- codes.push(code)
408
- checksum += code * (i + 1)
409
- }
410
- codes.push(checksum % 103)
411
- codes.push(106)
412
- const patterns = code128Patterns()
413
- let barcode = ''
414
- for (let i = 0; i < codes.length; i++) {
415
- barcode += patterns[codes[i]]
416
- }
417
- return barcode + '00000'
418
- }
419
-
420
- const encodeCode39 = (data: string): string => {
421
- const codes = code39Map()
422
- const value = data.toUpperCase()
423
- let barcode = stringValue(codes['*'])
424
- for (let i = 0; i < value.length; i++) {
425
- const char = value.substring(i, i + 1)
426
- const code = codes[char]
427
- if (code == null) {
428
- throw new Error('Invalid character in CODE39')
429
- }
430
- barcode += '0' + code.toString()
431
- }
432
- return barcode + '0' + stringValue(codes['*'])
433
- }
434
-
435
- const encodeEAN13 = (data: string): string => {
436
- if (!/^\d{13}$/.test(data)) {
437
- throw new Error('EAN13 must be 13 digits')
438
- }
439
- let sum = 0
440
- for (let i = 0; i < 12; i++) {
441
- const digit = digitAt(data, i)
442
- sum += i % 2 == 0 ? digit : digit * 3
443
- }
444
- const checkDigit = (10 - (sum % 10)) % 10
445
- if (digitAt(data, 12) != checkDigit) {
446
- throw new Error('Invalid EAN13 check digit')
447
- }
448
- const leftData = data.substring(1, 7)
449
- const rightData = data.substring(7, 13)
450
- const parity = [
451
- 'LLLLLL', 'LLGLGG', 'LLGGLG', 'LLGGGL', 'LGLLGG',
452
- 'LGGLLG', 'LGGGLL', 'LGLGLG', 'LGLGGL', 'LGGLGL'
453
- ] as Array<string>
454
- const odd = leftOddCodes()
455
- const even = leftEvenCodes()
456
- const right = rightCodes()
457
- const pattern = parity[digitAt(data, 0)]
458
- let barcode = '101'
459
- for (let i = 0; i < leftData.length; i++) {
460
- const digit = digitAt(leftData, i)
461
- barcode += pattern.substring(i, i + 1) == 'L' ? odd[digit] : even[digit]
462
- }
463
- barcode += '01010'
464
- for (let i = 0; i < rightData.length; i++) {
465
- barcode += right[digitAt(rightData, i)]
466
- }
467
- return barcode + '101'
468
- }
469
-
470
- const encodeEAN8 = (data: string): string => {
471
- if (!/^\d{8}$/.test(data)) {
472
- throw new Error('EAN8 must be 8 digits')
473
- }
474
- let sum = 0
475
- for (let i = 0; i < 7; i++) {
476
- const digit = digitAt(data, i)
477
- sum += digit * (i % 2 == 0 ? 3 : 1)
478
- }
479
- const checkDigit = (10 - (sum % 10)) % 10
480
- if (digitAt(data, 7) != checkDigit) {
481
- throw new Error('Invalid EAN8 check digit')
482
- }
483
- const odd = leftOddCodes()
484
- const right = rightCodes()
485
- let barcode = '101'
486
- for (let i = 0; i < 4; i++) {
487
- barcode += odd[digitAt(data, i)]
488
- }
489
- barcode += '01010'
490
- for (let i = 4; i < 8; i++) {
491
- barcode += right[digitAt(data, i)]
492
- }
493
- return barcode + '101'
494
- }
495
-
496
- const encodeEAN52 = (data: string, format: string): string => {
497
- const length = format == 'EAN5' ? 5 : 2
498
- if (data.length != length || !/^\d+$/.test(data)) {
499
- throw new Error(format + ' must be ' + length.toString() + ' digits')
500
- }
501
- const codes = leftOddCodes()
502
- let barcode = '1011'
503
- for (let i = 0; i < data.length; i++) {
504
- if (i > 0) {
505
- barcode += '01'
506
- }
507
- barcode += codes[digitAt(data, i)]
508
- }
509
- return barcode
510
- }
511
-
512
- const encodeUPCA = (data: string): string => {
513
- let value = data
514
- if (/^\d{11}$/.test(value)) {
515
- let sum = 0
516
- for (let i = 0; i < 11; i++) {
517
- const digit = digitAt(value, i)
518
- sum += i % 2 == 0 ? digit * 3 : digit
519
- }
520
- value += ((10 - (sum % 10)) % 10).toString()
521
- }
522
- if (!/^\d{12}$/.test(value)) {
523
- throw new Error('UPC-A must be 11 or 12 digits')
524
- }
525
- return encodeEAN13('0' + value)
526
- }
527
-
528
- const encodeUPCE = (data: string): string => {
529
- let value = data
530
- if (/^\d{7}$/.test(value)) {
531
- let sum = 0
532
- for (let i = 0; i < 7; i++) {
533
- const digit = digitAt(value, i)
534
- sum += i % 2 == 0 ? digit * 3 : digit
535
- }
536
- value += ((10 - (sum % 10)) % 10).toString()
537
- }
538
- if (!/^\d{8}$/.test(value)) {
539
- throw new Error('UPC-E must be 7 or 8 digits')
540
- }
541
- const odd = leftOddCodes()
542
- const even = leftEvenCodes()
543
- const middle = value.substring(1, 7)
544
- const checkDigit = value.substring(7, 8)
545
- let pattern = 'EEOOOO'
546
- if (checkDigit == '0' || checkDigit == '1' || checkDigit == '2' || checkDigit == '3') {
547
- pattern = 'EEEEOO'
548
- } else if (checkDigit == '4') {
549
- pattern = 'EEEOOO'
550
- }
551
- let barcode = '101'
552
- for (let i = 0; i < middle.length; i++) {
553
- const digit = digitAt(middle, i)
554
- barcode += pattern.substring(i, i + 1) == 'E' ? even[digit] : odd[digit]
555
- }
556
- return barcode + '010101101'
557
- }
558
-
559
- const encodeBarcode = (value: string, format: string): string => {
560
- switch (format) {
561
- case 'CODE39':
562
- return encodeCode39(value)
563
- case 'EAN13':
564
- return encodeEAN13(value)
565
- case 'EAN8':
566
- return encodeEAN8(value)
567
- case 'EAN5':
568
- case 'EAN2':
569
- return encodeEAN52(value, format)
570
- case 'UPC':
571
- case 'UPCA':
572
- return encodeUPCA(value)
573
- case 'UPCE':
574
- return encodeUPCE(value)
575
- case 'CODE128':
576
- case 'auto':
577
- default:
578
- return encodeCode128(value)
579
- }
580
- }
581
-
582
- const drawCanvasText = (context: CanvasRenderingContext2D, barcodeY: number, textHeight: number): void => {
583
- const text = displayText.value
584
- const textX = props.textAlign == 'left'
585
- ? marginLeftValue.value
586
- : (props.textAlign == 'right' ? canvasWidth.value - marginRightValue.value : canvasWidth.value / 2)
587
- let textY = props.textPosition == 'top'
588
- ? marginTopValue.value + props.fontSize
589
- : barcodeY + props.height + props.textMargin + props.fontSize
590
- if (textY > canvasHeight.value - marginBottomValue.value) {
591
- textY = canvasHeight.value - marginBottomValue.value - 2
592
- }
593
- context.fillStyle = props.lineColor
594
- context.font = props.fontOptions.length > 0
595
- ? props.fontOptions + ' ' + props.fontSize.toString() + 'px ' + props.font
596
- : props.fontSize.toString() + 'px ' + props.font
597
- context.textAlign = props.textAlign == 'left' || props.textAlign == 'right' ? props.textAlign : 'center'
598
- context.fillText(text, textX, textY)
599
- }
600
-
601
- const drawCanvasBarcode = (): Promise<void> => {
602
- return new Promise((resolve, reject) => {
603
- const context = ctx.value
604
- if (context == null) {
605
- reject(new Error('Canvas is not ready'))
606
- return
607
- }
608
- try {
609
- const bits = encodeBarcode(stringValue(props.value), props.format)
610
- context.clearRect(0, 0, canvasWidth.value, canvasHeight.value)
611
- context.fillStyle = props.background
612
- context.fillRect(0, 0, canvasWidth.value, canvasHeight.value)
613
- let barcodeY = marginTopValue.value
614
- const textHeight = props.displayValue ? props.fontSize + props.textMargin : 0
615
- if (props.displayValue && props.textPosition == 'top') {
616
- barcodeY += textHeight
617
- }
618
- const moduleWidth = props.width / Math.max(1, bits.length)
619
- context.fillStyle = props.lineColor
620
- let x = marginLeftValue.value
621
- for (let i = 0; i < bits.length; i++) {
622
- if (bits.substring(i, i + 1) == '1') {
623
- context.fillRect(x, barcodeY, moduleWidth + 0.5, props.height)
624
- }
625
- x += moduleWidth
626
- }
627
- if (props.displayValue) {
628
- drawCanvasText(context, barcodeY, textHeight)
629
- }
630
- context.draw()
631
- resolve()
632
- } catch (err) {
633
- reject(err)
634
- }
635
- })
636
- }
637
-
638
- const exportImage = (): Promise<string> => {
639
- return new Promise((resolve, reject) => {
640
- showCanvas.value = true
641
- nextTick(() => {
642
- initCanvas().then(() => {
643
- drawCanvasBarcode().then(() => {
644
- const canvas = canvasContext.value
645
- if (canvas == null) {
646
- showCanvas.value = false
647
- reject(new Error('Canvas is not ready'))
648
- return
649
- }
650
- const dataUrl = canvas.toDataURL('image/png', 1)
651
- showCanvas.value = false
652
- emit('rendered', { type: 'image', value: props.value, path: dataUrl } as UTSJSONObject)
653
- resolve(dataUrl)
654
- }).catch((err: any | null) => {
655
- showCanvas.value = false
656
- reject(err)
657
- })
658
- }).catch((err: any | null) => {
659
- showCanvas.value = false
660
- reject(err)
661
- })
662
- })
663
- })
664
- }
665
-
666
- const generateBarcode = (): void => {
667
- try {
668
- error.value = ''
669
- const bits = encodeBarcode(stringValue(props.value), props.format)
670
- bars.value = createBars(bits)
671
- const textHeight = props.displayValue ? props.fontSize + props.textMargin : 0
672
- canvasWidth.value = props.width + marginLeftValue.value + marginRightValue.value
673
- canvasHeight.value = props.height + marginTopValue.value + marginBottomValue.value + textHeight
674
- if (props.useCanvas == false) {
675
- nextTick(() => {
676
- exportImage().then((path: string) => {
677
- barcodeImage.value = path
678
- }).catch((err: any | null) => {
679
- emit('error', err)
680
- })
681
- })
682
- } else {
683
- barcodeImage.value = ''
684
- }
685
- emit('rendered', {
686
- type: props.useCanvas == false ? 'image' : 'view',
687
- value: props.value,
688
- format: props.format,
689
- path: barcodeImage.value
690
- } as UTSJSONObject)
691
- } catch (err) {
692
- const message = getErrorMessage(err)
693
- error.value = message.length > 0 ? message : '生成条码失败'
694
- bars.value = [] as Array<UTSJSONObject>
695
- emit('error', {
696
- message: error.value,
697
- value: props.value,
698
- format: props.format
699
- } as UTSJSONObject)
700
- }
701
- }
702
-
703
- function toTempFilePath(options: UTSJSONObject = {} as UTSJSONObject): void {
704
- const success = options['success'] as ((res: UTSJSONObject) => void) | null
705
- const fail = options['fail'] as ((err: any | null) => void) | null
706
- exportImage().then((path: string) => {
707
- if (success != null) {
708
- success({ tempFilePath: path, path } as UTSJSONObject)
709
- }
710
- }).catch((err: any | null) => {
711
- if (fail != null) {
712
- fail(err)
713
- } else {
714
- emit('error', err)
715
- }
716
- })
717
- }
718
-
719
- watch((): any => props.value, () => { generateBarcode() })
720
- watch((): string => props.format, () => { generateBarcode() })
721
- watch((): number => props.width, () => { generateBarcode() })
722
- watch((): number => props.height, () => { generateBarcode() })
723
- watch((): boolean => props.displayValue, () => { generateBarcode() })
724
- watch((): string => props.text, () => { generateBarcode() })
725
- watch((): string => props.textPosition, () => { generateBarcode() })
726
- watch((): number => props.textMargin, () => { generateBarcode() })
727
- watch((): number => props.fontSize, () => { generateBarcode() })
728
- watch((): string => props.background, () => { generateBarcode() })
729
- watch((): string => props.lineColor, () => { generateBarcode() })
730
- watch((): number => props.margin, () => { generateBarcode() })
731
-
732
- onMounted((): void => {
733
- generateBarcode()
734
- })
735
-
736
- defineExpose({
737
- canvasId,
738
- barcodeImage,
739
- showCanvas,
740
- canvasContext,
741
- ctx,
742
- bars,
743
- canvasWidth,
744
- canvasHeight,
745
- error,
746
- marginTopValue,
747
- marginBottomValue,
748
- marginLeftValue,
749
- marginRightValue,
750
- displayText,
751
- showTopText,
752
- showBottomText,
753
- showImage,
754
- rootStyle,
755
- barcodeStyle,
756
- canvasStyle,
757
- imageStyle,
758
- textStyle,
759
- getBarStyle,
760
- generateBarcode,
761
- toTempFilePath,
762
- exportImage,
763
- initCanvas,
764
- drawCanvasBarcode,
765
- drawCanvasText,
766
- getErrorMessage,
767
- createBars,
768
- encodeBarcode,
769
- encodeCode128,
770
- encodeCode39,
771
- encodeEAN13,
772
- encodeEAN8,
773
- encodeEAN52,
774
- encodeUPCA,
775
- encodeUPCE
776
- })
777
- </script>
158
+ defineOptions({
159
+ styleIsolation: 'app-and-page',
160
+ name: 'up-barcode'
161
+ })
162
+
163
+ const props = defineProps({
164
+ value: {
165
+ type: [String, Number],
166
+ default: ''
167
+ },
168
+ format: {
169
+ type: String,
170
+ default: 'auto'
171
+ },
172
+ width: {
173
+ type: Number,
174
+ default: 200
175
+ },
176
+ height: {
177
+ type: Number,
178
+ default: 80
179
+ },
180
+ displayValue: {
181
+ type: Boolean,
182
+ default: true
183
+ },
184
+ text: {
185
+ type: String,
186
+ default: ''
187
+ },
188
+ fontOptions: {
189
+ type: String,
190
+ default: ''
191
+ },
192
+ font: {
193
+ type: String,
194
+ default: 'monospace'
195
+ },
196
+ textAlign: {
197
+ type: String,
198
+ default: 'center'
199
+ },
200
+ textPosition: {
201
+ type: String,
202
+ default: 'bottom'
203
+ },
204
+ textMargin: {
205
+ type: Number,
206
+ default: 2
207
+ },
208
+ fontSize: {
209
+ type: Number,
210
+ default: 14
211
+ },
212
+ background: {
213
+ type: String,
214
+ default: '#ffffff'
215
+ },
216
+ lineColor: {
217
+ type: String,
218
+ default: '#000000'
219
+ },
220
+ margin: {
221
+ type: Number,
222
+ default: 10
223
+ },
224
+ marginTop: {
225
+ type: Number,
226
+ default: -1
227
+ },
228
+ marginBottom: {
229
+ type: Number,
230
+ default: -1
231
+ },
232
+ marginLeft: {
233
+ type: Number,
234
+ default: -1
235
+ },
236
+ marginRight: {
237
+ type: Number,
238
+ default: -1
239
+ },
240
+ useCanvas: {
241
+ type: Boolean,
242
+ default: true
243
+ }
244
+ })
245
+
246
+ const emit = defineEmits(['rendered', 'error'])
247
+ const instance = getCurrentInstance()!.proxy!
248
+
249
+ const canvasId = ref('up-barcode-' + Date.now().toString())
250
+ const barcodeImage = ref('')
251
+ const showCanvas = ref(false)
252
+ const canvasContext = ref<CanvasContext | null>(null)
253
+ const ctx = ref<CanvasRenderingContext2D | null>(null)
254
+ const bars = ref<Array<UTSJSONObject>>([] as Array<UTSJSONObject>)
255
+ const canvasWidth = ref(200)
256
+ const canvasHeight = ref(80)
257
+ const error = ref('')
258
+
259
+ const marginTopValue = computed((): number => {
260
+ return props.marginTop >= 0 ? props.marginTop : props.margin
261
+ })
262
+
263
+ const marginBottomValue = computed((): number => {
264
+ return props.marginBottom >= 0 ? props.marginBottom : props.margin
265
+ })
266
+
267
+ const marginLeftValue = computed((): number => {
268
+ return props.marginLeft >= 0 ? props.marginLeft : props.margin
269
+ })
270
+
271
+ const marginRightValue = computed((): number => {
272
+ return props.marginRight >= 0 ? props.marginRight : props.margin
273
+ })
274
+
275
+ const displayText = computed((): string => {
276
+ return props.text.length > 0 ? props.text : stringValue(props.value)
277
+ })
278
+
279
+ const showTopText = computed((): boolean => {
280
+ return props.displayValue && props.textPosition == 'top' && error.value.length == 0
281
+ })
282
+
283
+ const showBottomText = computed((): boolean => {
284
+ return props.displayValue && props.textPosition != 'top' && error.value.length == 0
285
+ })
286
+
287
+ const showImage = computed((): boolean => {
288
+ return props.useCanvas == false && barcodeImage.value.length > 0 && error.value.length == 0
289
+ })
290
+
291
+ const rootStyle = computed((): UTSJSONObject => {
292
+ return {
293
+ width: canvasWidth.value.toString() + 'px',
294
+ minHeight: canvasHeight.value.toString() + 'px',
295
+ paddingTop: marginTopValue.value.toString() + 'px',
296
+ paddingBottom: marginBottomValue.value.toString() + 'px',
297
+ paddingLeft: marginLeftValue.value.toString() + 'px',
298
+ paddingRight: marginRightValue.value.toString() + 'px',
299
+ backgroundColor: props.background
300
+ } as UTSJSONObject
301
+ })
302
+
303
+ const barcodeStyle = computed((): UTSJSONObject => {
304
+ return {
305
+ width: props.width.toString() + 'px',
306
+ height: props.height.toString() + 'px',
307
+ backgroundColor: props.background
308
+ } as UTSJSONObject
309
+ })
310
+
311
+ const canvasStyle = computed((): UTSJSONObject => {
312
+ return {
313
+ width: canvasWidth.value.toString() + 'px',
314
+ height: canvasHeight.value.toString() + 'px'
315
+ } as UTSJSONObject
316
+ })
317
+
318
+ const imageStyle = computed((): UTSJSONObject => {
319
+ return {
320
+ width: canvasWidth.value.toString() + 'px',
321
+ height: canvasHeight.value.toString() + 'px'
322
+ } as UTSJSONObject
323
+ })
324
+
325
+ const textStyle = computed((): UTSJSONObject => {
326
+ const style = {
327
+ fontSize: props.fontSize.toString() + 'px',
328
+ lineHeight: (props.fontSize + props.textMargin).toString() + 'px',
329
+ color: props.lineColor,
330
+ textAlign: props.textAlign
331
+ } as UTSJSONObject
332
+ return style
333
+ })
334
+
335
+ const getBarStyle = (bar: UTSJSONObject): UTSJSONObject => {
336
+ return {
337
+ width: normalizeNumber(bar['width'], 1).toString() + 'px',
338
+ height: props.height.toString() + 'px',
339
+ backgroundColor: stringValue(bar['color'])
340
+ } as UTSJSONObject
341
+ }
342
+
343
+ const initCanvas = (): Promise<void> => {
344
+ return new Promise((resolve, reject) => {
345
+ uni.createCanvasContextAsync({
346
+ id: canvasId.value.toString() as string.IDString,
347
+ component: instance,
348
+ success: (context: CanvasContext) => {
349
+ canvasContext.value = context
350
+ ctx.value = context.getContext('2d')
351
+ resolve()
352
+ },
353
+ fail: (err: UniError) => {
354
+ reject(err)
355
+ }
356
+ })
357
+ })
358
+ }
359
+
360
+ const getErrorMessage = (err: any | null): string => {
361
+ if (err == null) {
362
+ return ''
363
+ }
364
+ const errObj = err as UTSJSONObject
365
+ const message = errObj['message']
366
+ return message == null ? err.toString() : message.toString()
367
+ }
368
+
369
+ const createBars = (bits: string): Array<UTSJSONObject> => {
370
+ const list = [] as Array<UTSJSONObject>
371
+ if (bits.length == 0) {
372
+ return list
373
+ }
374
+ const moduleWidth = props.width / bits.length
375
+ let current = bits.substring(0, 1)
376
+ let count = 1
377
+ for (let i = 1; i < bits.length; i++) {
378
+ const bit = bits.substring(i, i + 1)
379
+ if (bit == current) {
380
+ count++
381
+ } else {
382
+ list.push({
383
+ color: current == '1' ? props.lineColor : props.background,
384
+ width: count * moduleWidth
385
+ } as UTSJSONObject)
386
+ current = bit
387
+ count = 1
388
+ }
389
+ }
390
+ list.push({
391
+ color: current == '1' ? props.lineColor : props.background,
392
+ width: count * moduleWidth
393
+ } as UTSJSONObject)
394
+ return list
395
+ }
396
+
397
+ const encodeCode128 = (data: string): string => {
398
+ const chars = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
399
+ const codes = [] as Array<number>
400
+ let checksum = 104
401
+ codes.push(104)
402
+ for (let i = 0; i < data.length; i++) {
403
+ const code = chars.indexOf(data.substring(i, i + 1))
404
+ if (code < 0) {
405
+ throw new Error('Invalid character in CODE128')
406
+ }
407
+ codes.push(code)
408
+ checksum += code * (i + 1)
409
+ }
410
+ codes.push(checksum % 103)
411
+ codes.push(106)
412
+ const patterns = code128Patterns()
413
+ let barcode = ''
414
+ for (let i = 0; i < codes.length; i++) {
415
+ barcode += patterns[codes[i]]
416
+ }
417
+ return barcode + '00000'
418
+ }
419
+
420
+ const encodeCode39 = (data: string): string => {
421
+ const codes = code39Map()
422
+ const value = data.toUpperCase()
423
+ let barcode = stringValue(codes['*'])
424
+ for (let i = 0; i < value.length; i++) {
425
+ const char = value.substring(i, i + 1)
426
+ const code = codes[char]
427
+ if (code == null) {
428
+ throw new Error('Invalid character in CODE39')
429
+ }
430
+ barcode += '0' + code.toString()
431
+ }
432
+ return barcode + '0' + stringValue(codes['*'])
433
+ }
434
+
435
+ const encodeEAN13 = (data: string): string => {
436
+ if (!/^\d{13}$/.test(data)) {
437
+ throw new Error('EAN13 must be 13 digits')
438
+ }
439
+ let sum = 0
440
+ for (let i = 0; i < 12; i++) {
441
+ const digit = digitAt(data, i)
442
+ sum += i % 2 == 0 ? digit : digit * 3
443
+ }
444
+ const checkDigit = (10 - (sum % 10)) % 10
445
+ if (digitAt(data, 12) != checkDigit) {
446
+ throw new Error('Invalid EAN13 check digit')
447
+ }
448
+ const leftData = data.substring(1, 7)
449
+ const rightData = data.substring(7, 13)
450
+ const parity = [
451
+ 'LLLLLL', 'LLGLGG', 'LLGGLG', 'LLGGGL', 'LGLLGG',
452
+ 'LGGLLG', 'LGGGLL', 'LGLGLG', 'LGLGGL', 'LGGLGL'
453
+ ] as Array<string>
454
+ const odd = leftOddCodes()
455
+ const even = leftEvenCodes()
456
+ const right = rightCodes()
457
+ const pattern = parity[digitAt(data, 0)]
458
+ let barcode = '101'
459
+ for (let i = 0; i < leftData.length; i++) {
460
+ const digit = digitAt(leftData, i)
461
+ barcode += pattern.substring(i, i + 1) == 'L' ? odd[digit] : even[digit]
462
+ }
463
+ barcode += '01010'
464
+ for (let i = 0; i < rightData.length; i++) {
465
+ barcode += right[digitAt(rightData, i)]
466
+ }
467
+ return barcode + '101'
468
+ }
469
+
470
+ const encodeEAN8 = (data: string): string => {
471
+ if (!/^\d{8}$/.test(data)) {
472
+ throw new Error('EAN8 must be 8 digits')
473
+ }
474
+ let sum = 0
475
+ for (let i = 0; i < 7; i++) {
476
+ const digit = digitAt(data, i)
477
+ sum += digit * (i % 2 == 0 ? 3 : 1)
478
+ }
479
+ const checkDigit = (10 - (sum % 10)) % 10
480
+ if (digitAt(data, 7) != checkDigit) {
481
+ throw new Error('Invalid EAN8 check digit')
482
+ }
483
+ const odd = leftOddCodes()
484
+ const right = rightCodes()
485
+ let barcode = '101'
486
+ for (let i = 0; i < 4; i++) {
487
+ barcode += odd[digitAt(data, i)]
488
+ }
489
+ barcode += '01010'
490
+ for (let i = 4; i < 8; i++) {
491
+ barcode += right[digitAt(data, i)]
492
+ }
493
+ return barcode + '101'
494
+ }
495
+
496
+ const encodeEAN52 = (data: string, format: string): string => {
497
+ const length = format == 'EAN5' ? 5 : 2
498
+ if (data.length != length || !/^\d+$/.test(data)) {
499
+ throw new Error(format + ' must be ' + length.toString() + ' digits')
500
+ }
501
+ const codes = leftOddCodes()
502
+ let barcode = '1011'
503
+ for (let i = 0; i < data.length; i++) {
504
+ if (i > 0) {
505
+ barcode += '01'
506
+ }
507
+ barcode += codes[digitAt(data, i)]
508
+ }
509
+ return barcode
510
+ }
511
+
512
+ const encodeUPCA = (data: string): string => {
513
+ let value = data
514
+ if (/^\d{11}$/.test(value)) {
515
+ let sum = 0
516
+ for (let i = 0; i < 11; i++) {
517
+ const digit = digitAt(value, i)
518
+ sum += i % 2 == 0 ? digit * 3 : digit
519
+ }
520
+ value += ((10 - (sum % 10)) % 10).toString()
521
+ }
522
+ if (!/^\d{12}$/.test(value)) {
523
+ throw new Error('UPC-A must be 11 or 12 digits')
524
+ }
525
+ return encodeEAN13('0' + value)
526
+ }
527
+
528
+ const encodeUPCE = (data: string): string => {
529
+ let value = data
530
+ if (/^\d{7}$/.test(value)) {
531
+ let sum = 0
532
+ for (let i = 0; i < 7; i++) {
533
+ const digit = digitAt(value, i)
534
+ sum += i % 2 == 0 ? digit * 3 : digit
535
+ }
536
+ value += ((10 - (sum % 10)) % 10).toString()
537
+ }
538
+ if (!/^\d{8}$/.test(value)) {
539
+ throw new Error('UPC-E must be 7 or 8 digits')
540
+ }
541
+ const odd = leftOddCodes()
542
+ const even = leftEvenCodes()
543
+ const middle = value.substring(1, 7)
544
+ const checkDigit = value.substring(7, 8)
545
+ let pattern = 'EEOOOO'
546
+ if (checkDigit == '0' || checkDigit == '1' || checkDigit == '2' || checkDigit == '3') {
547
+ pattern = 'EEEEOO'
548
+ } else if (checkDigit == '4') {
549
+ pattern = 'EEEOOO'
550
+ }
551
+ let barcode = '101'
552
+ for (let i = 0; i < middle.length; i++) {
553
+ const digit = digitAt(middle, i)
554
+ barcode += pattern.substring(i, i + 1) == 'E' ? even[digit] : odd[digit]
555
+ }
556
+ return barcode + '010101101'
557
+ }
558
+
559
+ const encodeBarcode = (value: string, format: string): string => {
560
+ switch (format) {
561
+ case 'CODE39':
562
+ return encodeCode39(value)
563
+ case 'EAN13':
564
+ return encodeEAN13(value)
565
+ case 'EAN8':
566
+ return encodeEAN8(value)
567
+ case 'EAN5':
568
+ case 'EAN2':
569
+ return encodeEAN52(value, format)
570
+ case 'UPC':
571
+ case 'UPCA':
572
+ return encodeUPCA(value)
573
+ case 'UPCE':
574
+ return encodeUPCE(value)
575
+ case 'CODE128':
576
+ case 'auto':
577
+ default:
578
+ return encodeCode128(value)
579
+ }
580
+ }
581
+
582
+ const drawCanvasText = (context: CanvasRenderingContext2D, barcodeY: number, textHeight: number): void => {
583
+ const text = displayText.value
584
+ const textX = props.textAlign == 'left'
585
+ ? marginLeftValue.value
586
+ : (props.textAlign == 'right' ? canvasWidth.value - marginRightValue.value : canvasWidth.value / 2)
587
+ let textY = props.textPosition == 'top'
588
+ ? marginTopValue.value + props.fontSize
589
+ : barcodeY + props.height + props.textMargin + props.fontSize
590
+ if (textY > canvasHeight.value - marginBottomValue.value) {
591
+ textY = canvasHeight.value - marginBottomValue.value - 2
592
+ }
593
+ context.fillStyle = props.lineColor
594
+ context.font = props.fontOptions.length > 0
595
+ ? props.fontOptions + ' ' + props.fontSize.toString() + 'px ' + props.font
596
+ : props.fontSize.toString() + 'px ' + props.font
597
+ context.textAlign = props.textAlign == 'left' || props.textAlign == 'right' ? props.textAlign : 'center'
598
+ context.fillText(text, textX, textY)
599
+ }
600
+
601
+ const drawCanvasBarcode = (): Promise<void> => {
602
+ return new Promise((resolve, reject) => {
603
+ const context = ctx.value
604
+ if (context == null) {
605
+ reject(new Error('Canvas is not ready'))
606
+ return
607
+ }
608
+ try {
609
+ const bits = encodeBarcode(stringValue(props.value), props.format)
610
+ context.clearRect(0, 0, canvasWidth.value, canvasHeight.value)
611
+ context.fillStyle = props.background
612
+ context.fillRect(0, 0, canvasWidth.value, canvasHeight.value)
613
+ let barcodeY = marginTopValue.value
614
+ const textHeight = props.displayValue ? props.fontSize + props.textMargin : 0
615
+ if (props.displayValue && props.textPosition == 'top') {
616
+ barcodeY += textHeight
617
+ }
618
+ const moduleWidth = props.width / Math.max(1, bits.length)
619
+ context.fillStyle = props.lineColor
620
+ let x = marginLeftValue.value
621
+ for (let i = 0; i < bits.length; i++) {
622
+ if (bits.substring(i, i + 1) == '1') {
623
+ context.fillRect(x, barcodeY, moduleWidth + 0.5, props.height)
624
+ }
625
+ x += moduleWidth
626
+ }
627
+ if (props.displayValue) {
628
+ drawCanvasText(context, barcodeY, textHeight)
629
+ }
630
+ context.draw()
631
+ resolve()
632
+ } catch (err) {
633
+ reject(err)
634
+ }
635
+ })
636
+ }
637
+
638
+ const exportImage = (): Promise<string> => {
639
+ return new Promise((resolve, reject) => {
640
+ showCanvas.value = true
641
+ // 等待条件渲染的画布提交到组件宿主视图后再查询节点。
642
+ instance.$nextTick(() => {
643
+ initCanvas().then(() => {
644
+ drawCanvasBarcode().then(() => {
645
+ const canvas = canvasContext.value
646
+ if (canvas == null) {
647
+ showCanvas.value = false
648
+ reject(new Error('Canvas is not ready'))
649
+ return
650
+ }
651
+ const dataUrl = canvas.toDataURL('image/png', 1)
652
+ showCanvas.value = false
653
+ emit('rendered', { type: 'image', value: props.value, path: dataUrl } as UTSJSONObject)
654
+ resolve(dataUrl)
655
+ }).catch((err: any | null) => {
656
+ showCanvas.value = false
657
+ reject(err)
658
+ })
659
+ }).catch((err: any | null) => {
660
+ showCanvas.value = false
661
+ reject(err)
662
+ })
663
+ })
664
+ })
665
+ }
666
+
667
+ const generateBarcode = (): void => {
668
+ try {
669
+ error.value = ''
670
+ const bits = encodeBarcode(stringValue(props.value), props.format)
671
+ bars.value = createBars(bits)
672
+ const textHeight = props.displayValue ? props.fontSize + props.textMargin : 0
673
+ canvasWidth.value = props.width + marginLeftValue.value + marginRightValue.value
674
+ canvasHeight.value = props.height + marginTopValue.value + marginBottomValue.value + textHeight
675
+ if (props.useCanvas == false) {
676
+ nextTick(() => {
677
+ exportImage().then((path: string) => {
678
+ barcodeImage.value = path
679
+ }).catch((err: any | null) => {
680
+ emit('error', err)
681
+ })
682
+ })
683
+ } else {
684
+ barcodeImage.value = ''
685
+ }
686
+ emit('rendered', {
687
+ type: props.useCanvas == false ? 'image' : 'view',
688
+ value: props.value,
689
+ format: props.format,
690
+ path: barcodeImage.value
691
+ } as UTSJSONObject)
692
+ } catch (err) {
693
+ const message = getErrorMessage(err)
694
+ error.value = message.length > 0 ? message : '生成条码失败'
695
+ bars.value = [] as Array<UTSJSONObject>
696
+ emit('error', {
697
+ message: error.value,
698
+ value: props.value,
699
+ format: props.format
700
+ } as UTSJSONObject)
701
+ }
702
+ }
703
+
704
+ function toTempFilePath(options: UTSJSONObject = {} as UTSJSONObject): void {
705
+ const success = options['success'] as ((res: UTSJSONObject) => void) | null
706
+ const fail = options['fail'] as ((err: any | null) => void) | null
707
+ exportImage().then((path: string) => {
708
+ if (success != null) {
709
+ success({ tempFilePath: path, path } as UTSJSONObject)
710
+ }
711
+ }).catch((err: any | null) => {
712
+ if (fail != null) {
713
+ fail(err)
714
+ } else {
715
+ emit('error', err)
716
+ }
717
+ })
718
+ }
719
+
720
+ watch((): any => props.value, () => { generateBarcode() })
721
+ watch((): string => props.format, () => { generateBarcode() })
722
+ watch((): number => props.width, () => { generateBarcode() })
723
+ watch((): number => props.height, () => { generateBarcode() })
724
+ watch((): boolean => props.displayValue, () => { generateBarcode() })
725
+ watch((): string => props.text, () => { generateBarcode() })
726
+ watch((): string => props.textPosition, () => { generateBarcode() })
727
+ watch((): number => props.textMargin, () => { generateBarcode() })
728
+ watch((): number => props.fontSize, () => { generateBarcode() })
729
+ watch((): string => props.background, () => { generateBarcode() })
730
+ watch((): string => props.lineColor, () => { generateBarcode() })
731
+ watch((): number => props.margin, () => { generateBarcode() })
732
+
733
+ onMounted((): void => {
734
+ generateBarcode()
735
+ })
736
+
737
+ defineExpose({
738
+ canvasId,
739
+ barcodeImage,
740
+ showCanvas,
741
+ canvasContext,
742
+ ctx,
743
+ bars,
744
+ canvasWidth,
745
+ canvasHeight,
746
+ error,
747
+ marginTopValue,
748
+ marginBottomValue,
749
+ marginLeftValue,
750
+ marginRightValue,
751
+ displayText,
752
+ showTopText,
753
+ showBottomText,
754
+ showImage,
755
+ rootStyle,
756
+ barcodeStyle,
757
+ canvasStyle,
758
+ imageStyle,
759
+ textStyle,
760
+ getBarStyle,
761
+ generateBarcode,
762
+ toTempFilePath,
763
+ exportImage,
764
+ initCanvas,
765
+ drawCanvasBarcode,
766
+ drawCanvasText,
767
+ getErrorMessage,
768
+ createBars,
769
+ encodeBarcode,
770
+ encodeCode128,
771
+ encodeCode39,
772
+ encodeEAN13,
773
+ encodeEAN8,
774
+ encodeEAN52,
775
+ encodeUPCA,
776
+ encodeUPCE
777
+ })
778
+ </script>
778
779
 
779
780
  <style scoped lang="scss">
780
781
  .up-barcode {