Vue3 + ts 二维码(QRCode)

3,849 阅读2分钟

下载依赖npm install --save qrcode
引入qrcode
如果报错 Could not find a declaration file for module 'qrcode'. 则只需在src下新建shims-vue.d.ts文件写入declare module 'qrcode'即可

准备一个canvas

<template>
    <canvas ref="canvas"></canvas>
</template>

定义类型

interface Props {
    value: string,                                   // 二维码的内容值。
    size?: number,                                   // 二维码大小。
    margin?: number,                                 // 定义空白区的宽度应该是多少。
    errorCorrectionLevel?: 'L' | 'M' | 'Q' | 'H',    // 维码的容错能力等级,取值为 'L', 'M', 'Q', 'H' 之一。
    dark?: string,                                   
    light?: string,                                  
}

纠错等级也叫纠错率,就是指二维码可以被遮挡后还能正常扫描,而这个能被遮挡的最大面积就是纠错率。 通常情况下二维码分为 4 个纠错级别:L级 可纠正约 7% 错误、M级 可纠正约 15% 错误、Q级 可纠正约 25% 错误、H级 可纠正约30% 错误。 并不是所有位置都可以缺损,像最明显的三个角上的方框,直接影响初始定位。中间零散的部分是内容编码,可以容忍缺损。 当二维码的内容编码携带信息比较少的时候,也就是链接比较短的时候,设置不同的纠错等级,生成的图片不会发生变化。

设置props默认值

const props = withDefaults(defineProps<Props>(), {
    value: '',
    size: 200,
    margin: 5,
    level: 'M',
    light: '#fff',
    dark: '#000',
})

获取canvas元素,需要在onMonuted之后才能获取得到canvas元素

const canvas = ref<HTMLCanvasElement | null>(null)

onMounted(() => {
    const canvasDom = canvas.value as HTMLCanvasElement
    // 第一个参数为 canvas画板
    // 第二个参数为 我们想要把字符串转二维码的 字符串
    // 第三个参数为 配置对象
    // 第四个参数为 一个完成后的回调函数
    QRCode.toCanvas(canvasDom, props.value, {
        errorCorrectionLevel:props.errorCorrectionLevel,
        width: props.size,
        margin: props.margin,
        color: {
            dark: props.dark,
            light: props.light
        }
    }, (error: any) => {
        if (error) console.error(error)
        console.log('success!');
    })
})

详细配置可参照官网: qrcode - npm (npmjs.com)

QRCode组件

<template>
    <canvas ref="canvas"></canvas>
</template>

<script setup lang='ts'>
import QRCode from 'qrcode'
import { onUpdated, ref, defineProps } from 'vue';

interface Props {
    value: string,                                   // 二维码的内容值。
    size?: number,                                   // 二维码大小。
    margin?: number,                                 // 定义空白区的宽度应该是多少。
    errorCorrectionLevel?: 'L' | 'M' | 'Q' | 'H',    // 维码的容错能力等级,取值为 'L', 'M', 'Q', 'H' 之一。
    dark?: string,                                   // 二维码前景颜色。
    light?: string,                                  // 二维码背景颜色。
}

const props = withDefaults(defineProps<Props>(), {
    value: '',
    size: 200,
    margin: 5,
    level: 'M',
    light: '#fff',
    dark: '#000',
})

const canvas = ref<HTMLCanvasElement | null>(null)

onUpdated(() => {
    const canvasDom = canvas.value as HTMLCanvasElement
    console.log(props.value, 'props.value');
    QRCode.toCanvas(canvasDom, props.value, {
        errorCorrectionLevel:props.errorCorrectionLevel,
        width: props.size,
        margin: props.margin,
        color: {
            dark: props.dark,
            light: props.light
        }
    }, (error: any) => {
        if (error) console.error(error)
        console.log('success!');
    })
})
</script>

在父组件中引用QRCode组件

<template>
    <div class="qr-code-container">
       <Qrcode v-bind="QRStyle"></Qrcode>
     </div>
    <div class="qr-code-info">打开网易云音乐APP扫码登录</div>
</template>

<script setup lang='ts'>
import { onBeforeMount, reactive, watch } from 'vue'
import { loginQrCodeKey, loginQrCodeCreate } from '@/api/auth'
import Qrcode from '@/components/Qrcode.vue'

// 二维码样式
const QRStyle = reactive({
  value: '',  // 生成二维码的字符串
  width: '200px',
  height: '200px',
  margin: 0,
  dark: '#335eea',
  light: '#00000000',
})

onBeforeMount(async () => {
  const getKey = await loginQrCodeKey()
  const key: string = getKey.data.data.unikey
  const result = await loginQrCodeCreate({ key })   // 请求得到二维码的字符串
  QRStyle.value = result.data.data.qrurl
})

watch(()=>QRStyle.value , (val) => {  // 监听二维码字符串的变化
  QRStyle.value = val
})

</script>

<style scoped lang="scss">
 .qr-code-container {
   background-color: var(--color-primary-bg);
   padding: 24px 24px 21px 24px;
   border-radius: 1.25rem;
   margin-bottom: 12px;
 }

 .qr-code-info {
   color: var(--color-text);
   text-align: center;
   margin-bottom: 28px;
 }
</style>

最后生成的效果图

image.png