Files
WAI_Project_UNIX/uni_modules/cool-ui/components/cl-qrcode/cl-qrcode.uvue
2025-07-21 16:47:04 +08:00

254 lines
5.0 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view
ref="qrcodeRef"
:style="{ width: getPx(props.width) + 'px', height: getPx(props.height) + 'px' }"
>
<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,
onUnmounted
} from "vue";
import { drawQrcode, type QrcodeOptions } from "./draw";
import { getPx, isHarmony, uuid } from "@/cool";
import type { ClQrcodeMode } from "../../types";
import { base64ToBlob } from "./utils";
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());
// 二维码组件元素
const qrcodeRef = ref<UniElement | null>(null);
// 二维码组件画布
const canvasRef = ref(null);
/**
* 主绘制方法,根据当前 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> {
return new Promise((resolve) => {
// #ifdef APP
qrcodeRef.value!.takeSnapshot({
success(res) {
resolve(res.tempFilePath);
},
fail(err) {
console.error(err);
resolve("");
}
});
// #endif
// #ifdef H5
const url = URL.createObjectURL(
base64ToBlob(
(canvasRef.value as unknown as HTMLCanvasElement)
.querySelector("canvas")
?.toDataURL("image/png", 1) ?? ""
)
);
resolve(url);
// #endif
// #ifdef MP-WEIXIN
uni.createCanvasContextAsync({
id: qrcodeId.value,
component: proxy,
success(context) {
// 获取2D绘图上下文
const ctx = context.getContext("2d")!;
const canvas = ctx.canvas;
// 将canvas转换为base64格式的PNG图片数据
const data = canvas.toDataURL("image/png", 1);
// 获取base64数据部分(去掉data:image/png;base64,前缀)
const bdataBase64 = data.split(",")[1];
// 获取文件系统管理器
const fileMg = uni.getFileSystemManager();
// 生成临时文件路径
// @ts-ignore
const filepath = `${wx.env.USER_DATA_PATH}/${uuid()}.png`;
// 将base64数据写入文件
fileMg.writeFile({
filePath: filepath,
data: bdataBase64,
encoding: "base64",
success() {
// 写入成功返回文件路径
resolve(filepath);
},
fail() {
// 写入失败返回空字符串
resolve("");
}
});
},
fail(err) {
console.error(err);
resolve("");
}
});
// #endif
});
}
// 自动重绘
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>