当前位置: 首页 > news >正文

Uniapp微信小程序:自定义海报生成方案。支持保存到本地,二维码生成,富文本解析(个人学习记录)

示例代码:

<template> <view class="page" :style="{ paddingTop: 180 + 'rpx' }"> <!-- 页面主体内容 --> <view class="page-content"> <!-- 标题区域 --> <view class="title">邀请好友加入龙集</view> <view class="sub-title">邀请好友扫码有惊喜</view> <!-- 海报生成容器(l-painter) --> <l-painter css="width:680rpx; height: 800rpx; position: absolute;top:0rpx; zIndex: 1;background: #FFFFFF;" @fail="handlePainterFail" @done="handlePainterDone" pathType="url" ref="posterRef" performance> <!-- 海报背景图 --> <!-- <l-painter-image src="../static/icons/qrcode_bg.png" css="width: 680rpx; height: 800rpx;position: absolute; top: 50%; left: 50%;transform: translate(-50%,-50%); zIndex: 1" /> --> <!-- Logo --> <!-- <l-painter-image src="../static/icons/会员卡.png" css="width: 82px; height: 82rpx; position: absolute; top: 44rpx; left: 50%; transform: translateX(-50%); zIndex: 1" /> --> <!-- 主标题 --> <l-painter-text text="龙集" css="font-size: 44rpx; line-height: 64rpx; font-weight: 600; color: #333; position: absolute; top: 138rpx; left: 50%; transform: translateX(-50%);zIndex: 1" /> <!-- 副标题 --> <l-painter-text text="邀请好友扫码有惊喜" css="font-size: 28rpx; line-height: 40rpx; color: #999; position: absolute; top: 214rpx; left: 50%; transform: translateX(-50%);" /> <!-- 二维码 --> <l-painter-qrcode css="position: absolute;top:350rpx;zIndex: 9;width: 260rpx; height: 260rpx;margin:0 auto;" :text="qrcodeContent" :options="{ useDynamicSize: true, areaColor: 'transparent', backgroundColor: 'transparent', foregroundColor: '#400303', margin: 30 }" /> </l-painter> <!-- 操作按钮区域 --> <view class="qr-option"> <view class="item"> <view class="saveIamge" @click="handleSavePoster">保存图片</view> </view> </view> </view> </view> </template> <script setup> import { ref } from 'vue'; // 二维码内容 const qrcodeContent = ref('code:58086544'); // 海报相关状态 const posterRef = ref(null); // 海报组件引用 const isPosterRendered = ref(false); // 海报是否绘制完成 const isSavingPoster = ref(false); // 是否正在保存海报 const tempPosterPath = ref(''); // 海报临时文件路径 /** * 海报绘制失败回调 */ const handlePainterFail = () => { console.error('[海报绘制] 失败'); isPosterRendered.value = false; uni.showToast({ title: '海报生成失败', icon: 'none' }); }; /** * 海报绘制完成回调 */ const handlePainterDone = () => { console.log('[海报绘制] 完成'); isPosterRendered.value = true; // 标记绘制完成 }; /** * 保存海报到相册 */ const handleSavePoster = async () => { // 校验1:海报未绘制完成 if (!isPosterRendered.value) { uni.showToast({ title: '海报正在生成,请稍后', icon: 'none', duration: 1500 }); return; } // 校验2:正在保存中,防止重复点击 if (isSavingPoster.value) return; try { isSavingPoster.value = true; uni.showLoading({ title: '保存中...', mask: true }); // 生成海报临时文件路径 const posterRes = await posterRef.value.canvasToTempFilePath({ fileType: 'jpg', quality: 1, pathType: 'url' }); // 校验临时路径是否生成成功 if (posterRes && posterRes.tempFilePath) { tempPosterPath.value = posterRes.tempFilePath; await saveImageToAlbum(); // 保存到相册 } else { uni.showToast({ title: '获取海报临时路径失败', icon: 'none' }); } } catch (error) { console.error('[海报保存] 生成临时路径失败:', error); uni.showToast({ title: '海报保存失败', icon: 'none', duration: 2000 }); } finally { isSavingPoster.value = false; // 解锁保存状态 uni.hideLoading(); // 隐藏加载提示 } }; /** * 保存图片到相册(权限处理) */ const saveImageToAlbum = () => { return new Promise((resolve, reject) => { // #ifndef H5 // 1. 获取用户授权设置 uni.getSetting({ success: (settingRes) => { // 已授权相册权限 if (settingRes.authSetting['scope.writePhotosAlbum']) { doSaveToAlbum(resolve, reject); } // 未授权,发起授权请求 else { uni.authorize({ scope: 'scope.writePhotosAlbum', success: () => doSaveToAlbum(resolve, reject), fail: () => handleAuthFail(resolve, reject) }); } }, fail: () => { uni.showToast({ title: '获取授权状态失败', icon: 'none', duration: 2000 }); reject('获取授权状态失败'); } }); // #endif }); }; /** * 处理相册授权失败逻辑 */ const handleAuthFail = (resolve, reject) => { uni.showModal({ title: '授权提示', content: '需要授权相册权限才能保存图片,请前往设置开启', confirmText: '去设置', cancelText: '取消', success: (modalRes) => { if (modalRes.confirm) { // 打开设置页面 uni.openSetting({ success: (openRes) => { if (openRes.authSetting['scope.writePhotosAlbum']) { doSaveToAlbum(resolve, reject); } else { uni.showToast({ title: '未开启授权,保存失败', icon: 'none' }); reject('用户未开启相册授权'); } }, fail: () => { uni.showToast({ title: '打开设置失败', icon: 'none' }); reject('打开设置页面失败'); } }); } else { reject('用户取消授权'); } }, fail: () => reject('模态框调用失败') }); }; /** * 实际执行保存到相册操作 */ const doSaveToAlbum = (resolve, reject) => { uni.saveImageToPhotosAlbum({ filePath: tempPosterPath.value, success: () => { resolve(); uni.showToast({ title: '已保存到相册', icon: 'success', duration: 2000 }); }, fail: (error) => { console.error('[相册保存] 失败:', error); reject(error); uni.showToast({ title: '保存相册失败', icon: 'none', duration: 2000 }); } }); }; </script> <style lang="scss" scoped> .page { min-height: 100vh; background-color: #F8F8FA; box-sizing: border-box; position: relative; } .page-content { text-align: center; padding: 35rpx; .title { font-weight: 600; font-size: 48rpx; color: #3D3D3D; line-height: 70rpx; margin-top: 54rpx; } .sub-title { font-weight: 400; font-size: 24rpx; color: #333333; line-height: 34rpx; margin-top: 12rpx; margin-bottom: 30rpx; } .qr-option { margin-top: 60rpx; display: flex; padding: 0 40rpx; justify-content: space-around; .item { display: flex; flex-direction: column; align-items: center; gap: 6rpx; .saveIamge { font-size: 36rpx; line-height: 52rpx; color: #3D3D3D; width: 322rpx; height: 118rpx; background: #FFFFFF; display: flex; justify-content: center; align-items: center; border-radius: 100rpx; } } } } </style>

依赖插件:

https://ext.dcloud.net.cn/plugin?id=2389
http://www.jsqmd.com/news/449558/

相关文章:

  • Legal RAG Bench:当检索拖了后腿,大模型再聪明也白搭
  • Qwen-Image-2512-SDNQ Web服务部署教程:防火墙端口开放与公网访问安全配置
  • 虚拟机常见问题
  • Janus-Pro-7B企业实操:客服中心图片工单理解+标准化回复生成
  • 9K 条数据训 4B 模型,逼近 DeepSeek-R1?CHIMERA 用合成数据破解推理冷启动难题
  • 学长亲荐!千笔AI,研究生论文写作神器
  • 安晋捷运(深圳)国际物流有限公司安井株式会社日本专线物流服务
  • prometheus告警-以CPU使用率告警为例
  • 查重35%、AI概率80%?别删内容!百考通用语义重构双降达标
  • 独立开发者出海收款指南:用 Wise 打通 App Store 海外收入
  • 【LLM】Labor market impacts of AI
  • 小爱AIAPI连接方法python
  • Windows 11 安装AIRI踩坑指北
  • Spring_couplet_generation 结合MySQL存储用户生成记录:安装配置与集成实战
  • 研发电脑防止拍照 公司防拍照泄密的Top5实用防护方案
  • 深度拆解:零门店无代理的半年6000万营收策略
  • 科技越发达,内心的平静反而越珍贵
  • OpenClaw:通过飞书发送文件的完整教程
  • ## RV1126B MIPI 接口适配 SC233HGS 控制列表调试
  • OpenClaw安装配置
  • 【四旋翼】基于反步控制和滑模控制SMC实现四旋翼在存在风扰动态环境中的稳定性,一种针对四旋翼无人机的抗干扰非线性控制策略实现附matlab代码
  • 【2025最新】基于SpringBoot+Vue的饮食分享平台管理系统源码+MyBatis+MySQL
  • 贡献法+容斥原理,abc248G - GCD cost on the tree
  • 亲测分享权威geo渠道实践经验
  • 人工智能+AI的微信小程序 高校新生报到管理系统
  • 广度看偏好、深度看对错——Mix-GRM 用 8B 模型打败一众开源奖励模型
  • c语言函数的学习记录
  • 2026年文本转语音软件深度测评:从新手到专业,这6款不踩坑
  • Qwen3-ASR-0.6B效果对比:相同音频下比Whisper-small快2.7倍,CER低1.8%
  • 前后端分离榆林特色旅游网站系统|SpringBoot+Vue+MyBatis+MySQL完整源码+部署教程