ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

qrcode+vue根据上传的文件和链接动态生成带水印的二维码

qrcode+vue根据上传的文件和链接动态生成带水印的二维码 废话不多说直接上干活template v-ifactiveName1 el-form refqrForm :modelqrForm :rulesrules label-width80px classqr-form !-- 链接输入框 -- el-form-item label目标链接 propurl el-input stylewidth: 80%; typetextarea :rows5 v-modelqrForm.url placeholder请输入需要生成二维码的链接 clearable inputhandleUrlChange /el-input /el-form-item !-- Logo上传区域 -- el-form-item labelLogo图片 el-upload classlogo-uploader action :auto-uploadfalse :on-changehandleLogoChange :show-file-listfalse acceptimage/* div el-button sizesmall typeprimary i classel-icon-upload/i 选择Logo /el-button /div /el-upload !-- Logo预览 -- div v-iflogoPreview classlogo-preview el-image :srclogoPreview stylewidth: 100px; height: 100px; margin-top: 10px template #error div classimage-error图片加载失败/div /template /el-image el-button sizemini typetext classremove-logo clickremoveLogo 移除 /el-button /div /el-form-item !-- 生成按钮 -- el-form-item el-button typeprimary clickgenerateQRCode :loadingloading span生成二维码/span /el-button /el-form-item /el-form !-- 二维码结果展示 -- div v-ifqrCodeUrl classqr-result el-divider content-positionleft生成结果/el-divider div classqr-image-container el-image :srcqrCodeUrl :preview-src-list[qrCodeUrl] fitcontain classqr-image template #error div classimage-error二维码生成失败/div /template /el-image /div el-button sizemini typetext classdownload-btn clickdownloadQRCode el-iconi classel-icon-download/i/el-icon 下载二维码 /el-button /div /template /el-cardimport QRCode from ‘qrcode’;import { getName, getPhone } from “/utils/auth”;export default {name: ‘QRCodeGenerator’,data() {return {// 表单数据qrForm: {url: ‘’},// 表单验证规则rules: {url: [{ required: true, message: ‘请输入链接’, trigger: ‘blur’ },{ type: ‘url’, message: ‘请输入有效的URL’, trigger: ‘blur’ }]},// 状态管理logoFile: null, // 上传的Logo文件流logoPreview: null, // Logo预览地址qrCodeUrl: null, // 生成的二维码地址loading: false , // 生成加载状态activeName:‘1’};},mounted(){this.w a t e r m a r k . s e t ( ‘ watermark.set(watermark.set(‘{getName()}KaTeX parse error: Expected EOF, got } at position 26: …)});// 添加水印 }̲, destroyed()…watermark.set(“”);},methods: {// 处理链接变化handleUrlChange() {// 链接变化时可选择清除之前的二维码this.qrCodeUrl null;},// 处理Logo上传 handleLogoChange(file) { // 验证文件类型 if (!file.raw.type.startsWith(image/)) { this.$message.error(请上传图片格式的文件); return; } // 验证文件大小限制5MB以内 if (file.size 5 * 1024 * 1024) { this.$message.error(Logo文件大小不能超过5MB); return; } this.logoFile file.raw; // 生成Logo预览 const reader new FileReader(); reader.onload (e) { this.logoPreview e.target.result; }; reader.readAsDataURL(file.raw); }, // 移除Logo removeLogo() { this.logoFile null; this.logoPreview null; this.qrCodeUrl null; }, // 生成带Logo的二维码 async generateQRCode() { // 表单验证 this.$refs.qrForm.validate(async (valid) { if (!valid) return; // if (!this.logoFile) { // this.$message.warning(请先上传Logo图片); // return; // } this.loading true; try { const qrSize 300; // 二维码尺寸 const logoRatio 0.2; // Logo占比20% // 1. 生成基础二维码 const qrBase64 await QRCode.toDataURL(this.qrForm.url, { width: qrSize, margin: 1, color: { dark: #333333, light: #ffffff } }); if(!this.logoFile){ this.qrCodeUrl qrBase64; return false } // 2. 转换Logo文件流为Image对象 const logoImage await this.fileToImage(this.logoFile); // 3. 合并二维码和Logo const qrWithLogo await this.combineQrAndLogo( qrBase64, logoImage, qrSize, logoRatio ); this.qrCodeUrl qrWithLogo; this.$message.success(二维码生成成功); } catch (error) { console.error(生成二维码失败:, error); this.$message.error(二维码生成失败请重试); } finally { this.loading false; } }); }, // 将文件流转为Image对象 fileToImage(file) { return new Promise((resolve, reject) { const image new Image(); const reader new FileReader(); reader.onload (e) { image.onload () resolve(image); image.onerror () reject(new Error(Logo图片加载失败)); image.src e.target.result; }; reader.onerror () reject(new Error(文件读取失败)); reader.readAsDataURL(file); }); }, // 合并二维码和Logo combineQrAndLogo(qrBase64, logoImage, qrSize, logoRatio) { return new Promise((resolve, reject) { const canvas document.createElement(canvas); const ctx canvas.getContext(2d); canvas.width qrSize; canvas.height qrSize; // 绘制二维码 const qrImage new Image(); qrImage.onload () { ctx.drawImage(qrImage, 0, 0, qrSize, qrSize); // 计算Logo尺寸和位置 const logoSize qrSize * logoRatio; const x (qrSize - logoSize) / 2; const y (qrSize - logoSize) / 2; // 添加白色边框增强识别度 ctx.fillStyle #ffffff; ctx.fillRect(x - 3, y - 3, logoSize 6, logoSize 6); // 绘制Logo ctx.drawImage(logoImage, x, y, logoSize, logoSize); // 转为PNG格式返回 resolve(canvas.toDataURL(image/png)); }; qrImage.onerror () reject(new Error(二维码绘制失败)); qrImage.src qrBase64; }); }, // 下载二维码 downloadQRCode() { if (!this.qrCodeUrl) return; const link document.createElement(a); link.download qrcode.png; link.href this.qrCodeUrl; link.click(); }}};::v-deep .el-card__header{padding-bottom: 0 !important;}.toolBox{padding: 30px 10px 10px 10px;}.qr-generator-card {width: 100%;/* margin: 20px auto; */}.header-icon {margin-right: 8px;color: #409EFF;}.header-title {font-size: 16px;font-weight: 500;}.qr-form {margin-top: 15px;}.logo-uploader {margin-top: 5px;}.logo-preview {position: relative;display: inline-block;}.remove-logo {/* position: absolute;//bottom: -20px;left: 0; */color: #F56C6C;padding: 0;}.qr-result {max-width: 300px;margin-top: 20px;display: flex;justify-content: flex-start;flex-direction: column;margin-left: 34px;}.qr-image-container {display: flex;/* justify-content: center; */padding: 20px;}.qr-image {max-width: 300px;max-height: 300px;box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);padding: 10px;background: #fff;}.download-btn {display: block;color: #409EFF;}.image-error {width: 100%;height: 100%;display: flex;align-items: center;justify-content: center;background: #f5f5f5;color: #999;}
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进