2025-07-21 16:47:04 +08:00
|
|
|
|
<template>
|
2025-07-29 13:26:31 +08:00
|
|
|
|
<view :style="{ width: getPx(props.width) + 'px', height: getPx(props.height) + 'px' }">
|
2025-07-21 16:47:04 +08:00
|
|
|
|
<canvas
|
|
|
|
|
|
ref="canvasRef"
|
|
|
|
|
|
:canvas-id="qrcodeId"
|
|
|
|
|
|
type="2d"
|
|
|
|
|
|
:id="qrcodeId"
|
|
|
|
|
|
:style="{ width: getPx(props.width) + 'px', height: getPx(props.height) + 'px' }"
|
|
|
|
|
|
></canvas>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script lang="ts" setup>
|
|
|
|
|
|
import {
|
|
|
|
|
|
ref,
|
|
|
|
|
|
watch,
|
|
|
|
|
|
onMounted,
|
|
|
|
|
|
getCurrentInstance,
|
|
|
|
|
|
nextTick,
|
|
|
|
|
|
computed,
|
|
|
|
|
|
type PropType,
|
2025-07-29 13:26:31 +08:00
|
|
|
|
onUnmounted,
|
|
|
|
|
|
shallowRef
|
2025-07-21 16:47:04 +08:00
|
|
|
|
} from "vue";
|
|
|
|
|
|
|
|
|
|
|
|
import { drawQrcode, type QrcodeOptions } from "./draw";
|
2025-07-29 13:26:31 +08:00
|
|
|
|
import { canvasToPng, getPx, isHarmony, uuid } from "@/cool";
|
2025-07-21 16:47:04 +08:00
|
|
|
|
import type { ClQrcodeMode } from "../../types";
|
|
|
|
|
|
|
|
|
|
|
|
defineOptions({
|
|
|
|
|
|
name: "cl-qrcode"
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const props = defineProps({
|
|
|
|
|
|
// 二维码宽度,支持 px/rpx 单位
|
|
|
|
|
|
width: {
|
|
|
|
|
|
type: String,
|
|
|
|
|
|
default: "200px"
|
|
|
|
|
|
},
|
|
|
|
|
|
// 二维码高度,支持 px/rpx 单位
|
|
|
|
|
|
height: {
|
|
|
|
|
|
type: String,
|
|
|
|
|
|
default: "200px"
|
|
|
|
|
|
},
|
|
|
|
|
|
// 二维码前景色
|
|
|
|
|
|
foreground: {
|
|
|
|
|
|
type: String,
|
|
|
|
|
|
default: "#131313"
|
|
|
|
|
|
},
|
|
|
|
|
|
// 二维码背景色
|
|
|
|
|
|
background: {
|
|
|
|
|
|
type: String,
|
|
|
|
|
|
default: "#FFFFFF"
|
|
|
|
|
|
},
|
|
|
|
|
|
// 定位点颜色,不填写时与前景色一致
|
|
|
|
|
|
pdColor: {
|
|
|
|
|
|
type: String as PropType<string | null>,
|
|
|
|
|
|
default: null
|
|
|
|
|
|
},
|
|
|
|
|
|
// 定位图案圆角半径,为0时绘制直角矩形
|
|
|
|
|
|
pdRadius: {
|
|
|
|
|
|
type: Number,
|
|
|
|
|
|
default: 10
|
|
|
|
|
|
},
|
|
|
|
|
|
// 二维码内容
|
|
|
|
|
|
text: {
|
|
|
|
|
|
type: String,
|
|
|
|
|
|
default: "https://cool-js.com/"
|
|
|
|
|
|
},
|
|
|
|
|
|
// logo 图片地址,支持网络、本地路径
|
|
|
|
|
|
logo: {
|
|
|
|
|
|
type: String,
|
|
|
|
|
|
default: ""
|
|
|
|
|
|
},
|
|
|
|
|
|
// logo 大小,支持 px/rpx 单位
|
|
|
|
|
|
logoSize: {
|
|
|
|
|
|
type: String,
|
|
|
|
|
|
default: "50px"
|
|
|
|
|
|
},
|
|
|
|
|
|
// 二维码边距,单位 px
|
|
|
|
|
|
padding: {
|
|
|
|
|
|
type: Number,
|
|
|
|
|
|
default: 5
|
|
|
|
|
|
},
|
|
|
|
|
|
// 二维码样式:rect 普通矩形、circular 小圆点、line 线条、rectSmall 小方格
|
|
|
|
|
|
mode: {
|
|
|
|
|
|
type: String as PropType<ClQrcodeMode>,
|
|
|
|
|
|
default: "circular"
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const { proxy } = getCurrentInstance()!;
|
|
|
|
|
|
|
|
|
|
|
|
// 二维码组件id
|
|
|
|
|
|
const qrcodeId = ref<string>("cl-qrcode-" + uuid());
|
|
|
|
|
|
|
|
|
|
|
|
// 二维码组件画布
|
2025-07-29 13:26:31 +08:00
|
|
|
|
const canvasRef = shallowRef<UniElement | null>(null);
|
2025-07-21 16:47:04 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 主绘制方法,根据当前 props 生成二维码并绘制到 canvas。
|
|
|
|
|
|
* 支持多平台(APP、H5、微信小程序),自动适配高分屏。
|
|
|
|
|
|
* 内部调用 drawQrcode 进行二维码点阵绘制。
|
|
|
|
|
|
*/
|
|
|
|
|
|
function drawer() {
|
|
|
|
|
|
const data = {
|
|
|
|
|
|
text: props.text,
|
|
|
|
|
|
size: getPx(props.width),
|
|
|
|
|
|
foreground: props.foreground,
|
|
|
|
|
|
background: props.background,
|
|
|
|
|
|
padding: props.padding,
|
|
|
|
|
|
logo: props.logo,
|
|
|
|
|
|
logoSize: getPx(props.logoSize),
|
|
|
|
|
|
ecc: "H", // 使用最高纠错级别
|
|
|
|
|
|
mode: props.mode,
|
|
|
|
|
|
pdColor: props.pdColor,
|
|
|
|
|
|
pdRadius: props.pdRadius
|
|
|
|
|
|
} as QrcodeOptions;
|
|
|
|
|
|
|
|
|
|
|
|
nextTick(() => {
|
|
|
|
|
|
// #ifdef APP || MP-WEIXIN
|
|
|
|
|
|
uni.createCanvasContextAsync({
|
|
|
|
|
|
id: qrcodeId.value,
|
|
|
|
|
|
component: proxy,
|
|
|
|
|
|
success(context) {
|
|
|
|
|
|
drawQrcode(context, data);
|
|
|
|
|
|
},
|
|
|
|
|
|
fail(err) {
|
|
|
|
|
|
console.error(err);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
// #endif
|
|
|
|
|
|
|
|
|
|
|
|
// #ifdef H5
|
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
|
drawQrcode(canvasRef.value, data);
|
|
|
|
|
|
// #endif
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取当前二维码图片的临时文件地址
|
|
|
|
|
|
* @param call 回调函数,返回图片路径,失败返回空字符串
|
|
|
|
|
|
*/
|
|
|
|
|
|
function toPng(): Promise<string> {
|
2025-08-02 17:17:13 +08:00
|
|
|
|
return canvasToPng(canvasRef.value!);
|
2025-07-21 16:47:04 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 自动重绘
|
|
|
|
|
|
const stopWatch = watch(
|
|
|
|
|
|
computed(() => [
|
|
|
|
|
|
props.pdColor,
|
|
|
|
|
|
props.pdRadius,
|
|
|
|
|
|
props.foreground,
|
|
|
|
|
|
props.background,
|
|
|
|
|
|
props.text,
|
|
|
|
|
|
props.logo,
|
|
|
|
|
|
props.logoSize,
|
|
|
|
|
|
props.mode,
|
|
|
|
|
|
props.padding
|
|
|
|
|
|
]),
|
|
|
|
|
|
() => {
|
|
|
|
|
|
drawer();
|
|
|
|
|
|
}
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
|
setTimeout(
|
|
|
|
|
|
() => {
|
|
|
|
|
|
drawer();
|
|
|
|
|
|
},
|
|
|
|
|
|
isHarmony() ? 50 : 0
|
|
|
|
|
|
);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
onUnmounted(() => {
|
|
|
|
|
|
stopWatch();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
defineExpose({
|
|
|
|
|
|
toPng
|
|
|
|
|
|
});
|
|
|
|
|
|
</script>
|