uniapp 实现 微信小程序 二维码保存 uQRCode

864 阅读4分钟

快速上手 | uQRCode 中文文档

<template>
    <!-- 主要容器,包含二维码和相关信息 -->
    <view class="box">
        <!-- 绘制二维码的容器 -->
        <view id="draw" style="overflow: hidden;" ref="shellContainerRef">
            <!-- 用于绘制二维码的 canvas 元素 -->
            <canvas id="qrcode" canvas-id="qrcode" style="width: 250px;height: 250px;"></canvas>
            <!-- 显示车牌号码 -->
            <view class="carNo">{{obj.carNumber}}</view>
            <!-- 显示企业名称 -->
            <view class="item">
                <text>企业名称</text>
                <text style="font-weight: bold;">{{obj.secondCompanyName}}</text>
            </view>
            <!-- 显示客户名称 -->
            <view class="item">
                <text>客户名称</text>
                <text>{{obj.cusName}}</text>
            </view>
            <!-- 显示物料名称 -->
            <view class="item">
                <text>物料名称</text>
                <text>{{obj.materialName}}</text>
            </view>
            <!-- 显示下单重量 -->
            <view class="item">
                <text>下单重量</text>
                <text>{{obj.weight}}吨</text>
            </view>
        </view>
        <!-- 保存和分享按钮 -->
        <view class="btns">
            <u-button text="保存图片" color="#07C15F" shape="circle" @click="SaveImage"></u-button>
            <!-- 仅在 APP 和 H5 平台显示分享按钮 -->
            <!-- #ifdef APP-PLUS || H5 -->
            <u-button text="分享图片" color="#F08E00" :plain="true" shape="circle"></u-button>
            <!-- #endif -->
        </view>
    </view>

    <!-- 用于绘制保存图片的 canvas 元素 -->
    <canvas style="width: 350px; height: 500px;" canvas-id="firstCanvas" id="firstCanvas"></canvas>
</template>

<script setup>
    import { ref, reactive } from 'vue';  // 引入 ref 和 reactive 用于声明响应式数据
    import { onLoad } from '@dcloudio/uni-app';  // 引入 onLoad 生命周期钩子
    import UQRCode from 'uqrcodejs';  // 引入生成二维码的库
    import { pickQrcode } from "@/api/api.js";  // 引入获取二维码数据的 API

    // 使用 reactive 声明一个响应式对象,用于存储二维码相关数据
    let obj = reactive({});

    // 在页面加载时获取二维码数据
    onLoad(async (e) => {
        // 从 API 获取二维码数据
        let r = await pickQrcode({
            id: e.id
        });
        // 将获取到的数据合并到响应式对象 obj 中
        Object.assign(obj, r.data);

        // 使用 UQRCode 库生成二维码
        var qr = new UQRCode();
        qr.data = obj.ocrContext;  // 设置二维码数据
        qr.size = 250;  // 设置二维码大小
        qr.make();  // 生成二维码
        // 创建 canvas 上下文
        var canvasContext = uni.createCanvasContext('qrcode', this);
        qr.canvasContext = canvasContext;  // 将上下文传递给 UQRCode 实例
        qr.drawCanvas();  // 在 canvas 上绘制二维码
    });

    // 定义一个保存图片的函数
    const SaveImage = () => {
        uni.showLoading({
            title: '绘制中',
            mask: true
        });
        // 将 canvas 转换为临时文件路径
        uni.canvasToTempFilePath({
            x: 0,
            y: 0,
            width: 250,
            height: 250,
            canvasId: 'qrcode',
            success: (res) => {
                console.log(res);
                // 小程序环境下的处理逻辑
                // #ifdef MP
                const ctx = uni.createCanvasContext('firstCanvas');
                ctx.beginPath();
                ctx.setFillStyle('#ffffff');
                ctx.fillRect(0, 0, 350, 500);
                ctx.drawImage(res.tempFilePath, 50, 20, 250, 250);
                ctx.setFontSize(35);
                ctx.setFillStyle('#F08E00');
                ctx.fillText(obj.carNumber, 100, 323);

                ctx.beginPath();
                ctx.setStrokeStyle('#bbbbbb');
                ctx.setLineWidth(4);
                for (var i = 0; i < 50; i++) {
                    ctx.moveTo(10 * i, 350)
                    ctx.lineTo(10 * i + 5, 350)
                }
                ctx.stroke();

                ctx.setFontSize(14);
                ctx.setFillStyle('#868686');
                ctx.fillText('企业名称', 12, 390);
                ctx.fillText('客户名称', 12, 420);
                ctx.fillText('物料名称', 12, 450);
                ctx.fillText('下单重量', 12, 480);
                ctx.setFontSize(14);
                ctx.setFillStyle('#101010');
                ctx.setTextAlign('left');
                ctx.fillText(obj.secondCompanyName, 350 - 12 - ctx.measureText(obj.secondCompanyName).width, 390, 350 - 12 - 80);
                ctx.fillText(obj.cusName, 350 - 12 - ctx.measureText(obj.cusName).width, 420, 350 - 12 - 80);
                ctx.fillText(obj.materialName, 350 - 12 - ctx.measureText(obj.materialName).width, 450, 350 - 12 - 80);
                ctx.fillText((obj.weight) + '吨', 350 - 12 - ctx.measureText((obj.weight) + '吨').width, 480, 350 - 12 - 80);
                ctx.draw(true, () => {
                    setTimeout(() => {
                        // 将 canvas 转换成图片并保存到相册
                        uni.canvasToTempFilePath({
                            x: 0,
                            y: 0,
                            canvasId: 'firstCanvas',
                            fileType: 'png',
                            quality: 1,
                            success: (success) => {
                                savePoster(success.tempFilePath);
                            },
                            fail: (e) => {
                                uni.hideLoading();
                                uni.showToast({
                                    title: '图片生成失败',
                                    icon: 'none'
                                });
                            }
                        })
                    }, 500);
                });
                // #endif

                // App 和 H5 环境下的处理逻辑
                // #ifdef APP-PLUS || H5
                const savedFilePath = res.tempFilePath;
                const path = plus.io.convertLocalFileSystemURL(savedFilePath)
                const fileReader = new plus.io.FileReader()
                fileReader.readAsDataURL(path)
                fileReader.onloadend = (res) => {
                    const ctx = uni.createCanvasContext('firstCanvas');
                    ctx.beginPath();
                    ctx.setFillStyle('#ffffff');
                    ctx.fillRect(0, 0, 350, 500);
                    ctx.drawImage(res.target.result, 50, 20, 250, 250);
                    ctx.setFontSize(35);
                    ctx.setFillStyle('#F08E00');
                    ctx.fillText(obj.carNumber, 100, 323);

                    ctx.beginPath();
                    ctx.setStrokeStyle('#bbbbbb');
                    ctx.setLineWidth(4);
                    for (var i = 0; i < 50; i++) {
                        ctx.moveTo(10 * i, 350)
                        ctx.lineTo(10 * i + 5, 350)
                    }
                    ctx.stroke();

                    ctx.setFontSize(14);
                    ctx.setFillStyle('#868686');
                    ctx.fillText('企业名称', 12, 390);
                    ctx.fillText('客户名称', 12, 420);
                    ctx.fillText('物料名称', 12, 450);
                    ctx.fillText('下单重量', 12, 480);
                    ctx.setFontSize(14);
                    ctx.setFillStyle('#101010');
                    ctx.setTextAlign('left');
                    ctx.fillText(obj.secondCompanyName, 350 - 12 - ctx.measureText(obj.secondCompanyName).width, 390, 350 - 12 - 80);
                    ctx.fillText(obj.cusName, 350 - 12 - ctx.measureText(obj.cusName).width, 420, 350 - 12 - 80);
                    ctx.fillText(obj.materialName, 350 - 12 - ctx.measureText(obj.materialName).width, 450, 350 - 12 - 80);
                    ctx.fillText((obj.weight) + '吨', 350 - 12 - ctx.measureText((obj.weight) + '吨').width, 480, 350 - 12 - 80);
                    ctx.draw(true, () => {
                        setTimeout(() => {
                            // 将 canvas 转换成图片并保存到相册
                            uni.canvasToTempFilePath({
                                x: 0,
                                y: 0,
                                canvasId: 'firstCanvas',
                                fileType: 'png',
                                quality: 1,
                                success: (success) => {
                                    savePoster(success.tempFilePath);
                                },
                                fail: (e) => {
                                    uni.hideLoading();
                                    uni.showToast({
                                        title: '图片生成失败',
                                        icon: 'none'
                                    });
                                }
                            })
                        }, 500);
                    });
                }
                // #endif
            },
            fail: () => {
                uni.hideLoading();
                uni.showToast({
                    title: '二维码绘制失败',
                    icon: 'none'
                });
            }
        });
    };

    // 保存生成的图片到相册
    const savePoster = (posterImage) => {
        uni.saveImageToPhotosAlbum({
            filePath: posterImage,
            success: () => {
                setTimeout(() => {
                    uni.showToast({
                        title: '保存到相册成功',
                        icon: 'none'
                    });
                }, 200)
            },
            fail: () => {
                uni.showToast({
                    title: '保存失败',
                    icon: 'none'
                });
            },
            complete: () => {
                uni.hideLoading()
            }
        })
    }
</script>

  • UQRCode 是用于生成二维码的库。

  • pickQrcode 是一个获取二维码数据的 API。

  • obj 是一个响应式对象,用于存储从 API 获取的数据。

  • SaveImage 函数用于将生成的二维码保存为图片,并支持在不同平台(小程序、APP、H5)上的保存逻辑。

  • savePoster 函数用于将生成的图片保存到相册

<style scoped lang="scss">
	.box {
		margin: 20rpx 24rpx 0;
		background-color: #fff;
		border-radius: 12rpx;
		overflow: hidden;

		#qrcode {
			margin: 54rpx auto;
		}

		.carNo {
			margin-bottom: 30rpx;
			padding: 18rpx 0;
			text-align: center;
			font-size: 56rpx;
			color: #F08E00;
			border-bottom: 4rpx dashed #bbb;
		}

		.item {
			padding: 0 26rpx;
			display: flex;
			justify-content: space-between;
			align-items: center;

			text {
				padding: 14rpx 0;
				color: #868686;
				font-size: 28rpx;
			}

			text:last-child {
				color: #101010;
			}
		}

		.btns {
			margin-top: 70rpx;
			padding-bottom: 50rpx;
			display: flex;
			align-items: center;

			:deep(.u-button) {
				width: 230rpx;
			}
		}
	}

	#firstCanvas {
		position: fixed;
		top: -500px;
		left: -350px;
		opacity: 0;
	}
</style>