Ben Lin
2024-06-18 ebbd788fbb2c0b45d4473798efc57eec8ba74a25
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
<template>
  <div>
    <component :is="tag" ref="wrapRef" />
  </div>
</template>
<script lang="ts" setup>
  import { watch, PropType, ref, unref, onMounted } from 'vue';
  import { toCanvas, QRCodeRenderersOptions, LogoType } from './qrcodePlus';
  import { toDataURL } from 'qrcode';
  import { downloadByUrl } from '@/utils/file/download';
  import { QrcodeDoneEventParams } from './typing';
 
  defineOptions({ name: 'QrCode' });
 
  const props = defineProps({
    value: {
      type: [String, Array] as PropType<string | any[]>,
      default: null,
    },
    // 参数
    options: {
      type: Object as PropType<QRCodeRenderersOptions>,
      default: null,
    },
    // 宽度
    width: {
      type: Number as PropType<number>,
      default: 200,
    },
    // 中间logo图标
    logo: {
      type: [String, Object] as PropType<Partial<LogoType> | string>,
      default: '',
    },
    // img 不支持内嵌logo
    tag: {
      type: String as PropType<'canvas' | 'img'>,
      default: 'canvas',
      validator: (v: string) => ['canvas', 'img'].includes(v),
    },
  });
 
  const emit = defineEmits({
    done: (data: QrcodeDoneEventParams) => !!data,
    error: (error: any) => !!error,
  });
 
  const wrapRef = ref<HTMLCanvasElement | HTMLImageElement | null>(null);
  async function createQrcode() {
    try {
      const { tag, value, options = {}, width, logo } = props;
      const renderValue = String(value);
      const wrapEl = unref(wrapRef);
 
      if (!wrapEl) return;
 
      if (tag === 'canvas') {
        const url: string = await toCanvas({
          canvas: wrapEl,
          width,
          logo: logo as any,
          content: renderValue,
          options: options || {},
        });
        emit('done', { url, ctx: (wrapEl as HTMLCanvasElement).getContext('2d') });
        return;
      }
 
      if (tag === 'img') {
        const url = await toDataURL(renderValue, {
          errorCorrectionLevel: 'H',
          width,
          ...options,
        });
        (unref(wrapRef) as HTMLImageElement).src = url;
        emit('done', { url });
      }
    } catch (error) {
      emit('error', error);
    }
  }
  /**
   * file download
   */
  function download(fileName?: string) {
    let url = '';
    const wrapEl = unref(wrapRef);
    if (wrapEl instanceof HTMLCanvasElement) {
      url = wrapEl.toDataURL();
    } else if (wrapEl instanceof HTMLImageElement) {
      url = wrapEl.src;
    }
    if (!url) return;
    downloadByUrl({
      url,
      fileName,
    });
  }
 
  onMounted(createQrcode);
 
  // 监听参数变化重新生成二维码
  watch(
    props,
    () => {
      createQrcode();
    },
    {
      deep: true,
    },
  );
 
  defineExpose({ download });
</script>