// pages/zuzhanghaibao/zuzhanghaibao.js import request from '../../utils/request.js'; const app = getApp(); Page({ data: { qrcodeUrl: '', // 二维码完整URL yaoqingma: '', // 组长邀请码字符串 isLoading: false, showPosterCanvas: false, posterImageTemp: '', // 生成的海报临时路径 avatarUrl: '', // 用户头像完整URL uid: '', nickName: '', }, onLoad() { this.loadFromCache(); this.loadUserInfo(); }, onShow() { this.registerNotificationComponent(); }, registerNotificationComponent() { const notificationComp = this.selectComponent('#global-notification'); if (notificationComp && notificationComp.showNotification) { app.globalData.globalNotification = { show: (data) => notificationComp.showNotification(data), hide: () => notificationComp.hideNotification() }; } }, /** * 从缓存加载二维码URL和邀请码 * 缓存键名使用独特拼音,避免与管事页面冲突 */ loadFromCache() { try { const relativeUrl = wx.getStorageSync('zuzhang_haibao_url'); if (relativeUrl) { const fullUrl = this.getFullImageUrl(relativeUrl); this.setData({ qrcodeUrl: fullUrl }); } const yaoqingma = wx.getStorageSync('zuzhang_yaoqingma'); if (yaoqingma) { this.setData({ yaoqingma }); } } catch (error) { console.error('读取缓存失败', error); } }, /** * 加载用户头像、昵称、UID */ loadUserInfo() { const touxiang = wx.getStorageSync('touxiang'); const ossUrl = app.globalData.ossImageUrl || ''; const defaultAvatar = ossUrl + (app.globalData.morentouxiang || 'avatar/default.jpg'); let avatarUrl = defaultAvatar; if (touxiang && typeof touxiang === 'string' && touxiang.trim() !== '') { avatarUrl = this.getFullImageUrl(touxiang); } const uid = wx.getStorageSync('uid') || ''; // 组长昵称可能从全局变量或缓存获取 let nickName = app.globalData.nicheng || wx.getStorageSync('nicheng') || '组长'; this.setData({ avatarUrl, uid, nickName }); }, /** * 拼接完整图片URL */ getFullImageUrl(url) { if (!url) return ''; const ossUrl = app.globalData.ossImageUrl || ''; return url.startsWith('http') ? url : ossUrl + url; }, /** * 刷新/获取二维码和邀请码 * 调用 /peizhi/zuzhanghb 接口 */ refreshQRCode() { if (this.data.isLoading) return; this.setData({ isLoading: true }); request({ url: '/peizhi/zuzhanghb', method: 'POST', }) .then(res => { if (res.data && res.data.code === 0) { const { url: relativeUrl, yaoqingma } = res.data.data; // 假设返回字段为 url 和 yaoqingma if (relativeUrl) { wx.setStorageSync('zuzhang_haibao_url', relativeUrl); const fullUrl = this.getFullImageUrl(relativeUrl); this.setData({ qrcodeUrl: fullUrl }); } if (yaoqingma) { wx.setStorageSync('zuzhang_yaoqingma', yaoqingma); this.setData({ yaoqingma }); } wx.showToast({ title: '获取成功', icon: 'success' }); } else { wx.showToast({ title: res.data.msg || '获取失败', icon: 'none' }); } }) .catch(err => { console.error('获取组长海报失败', err); wx.showToast({ title: '网络错误', icon: 'none' }); }) .finally(() => { this.setData({ isLoading: false }); }); }, /** * 保存二维码到相册(先下载网络图片) */ saveToAlbum() { if (!this.data.qrcodeUrl) { wx.showToast({ title: '暂无二维码', icon: 'none' }); return; } wx.showLoading({ title: '保存中...', mask: true }); wx.downloadFile({ url: this.data.qrcodeUrl, success: (res) => { wx.hideLoading(); if (res.statusCode === 200) { this.downloadAndSave(res.tempFilePath); } else { wx.showToast({ title: '下载失败', icon: 'none' }); } }, fail: (err) => { wx.hideLoading(); console.error('下载二维码失败', err); wx.showToast({ title: '下载失败', icon: 'none' }); } }); }, /** * 复制邀请码到剪贴板 */ copyInviteCode() { if (!this.data.yaoqingma) { wx.showToast({ title: '暂无邀请码', icon: 'none' }); return; } wx.setClipboardData({ data: this.data.yaoqingma, success: () => { wx.showToast({ title: '复制成功', icon: 'success' }); }, fail: () => { wx.showToast({ title: '复制失败', icon: 'none' }); } }); }, /** * 生成专属海报(包含头像、二维码、昵称、标语) */ generateMyPoster() { if (!this.data.qrcodeUrl) { wx.showToast({ title: '请先生成二维码', icon: 'none' }); return; } wx.showLoading({ title: '生成海报中...', mask: true }); const ossUrl = app.globalData.ossImageUrl || ''; // 🔥 组长海报背景图路径:beijing/zuzhangbeijing.jpg const bgUrl = ossUrl + 'beijing/zuzhangbeijing.jpg'; const urls = [bgUrl, this.data.qrcodeUrl, this.data.avatarUrl]; // 并行加载所有图片 const promises = urls.map(url => this.loadImage(url)); Promise.all(promises) .then((images) => { wx.hideLoading(); this.setData({ showPosterCanvas: true }, () => { // 确保 canvas 已渲染 setTimeout(() => { this.drawPoster(images[0], images[1], images[2]); }, 200); }); }) .catch((err) => { wx.hideLoading(); wx.showToast({ title: '图片加载失败', icon: 'none' }); console.error('图片加载失败', err); }); }, /** * 加载图片为本地路径(用于 canvas 绘制) */ loadImage(url) { return new Promise((resolve, reject) => { wx.getImageInfo({ src: url, success: (res) => resolve(res.path), fail: reject }); }); }, /** * 绘制海报 * @param {string} bgPath 背景图本地路径 * @param {string} qrPath 二维码本地路径 * @param {string} avatarPath 头像本地路径 */ drawPoster(bgPath, qrPath, avatarPath) { const ctx = wx.createCanvasContext('posterCanvas', this); const query = wx.createSelectorQuery().in(this); query.select('.poster-canvas').boundingClientRect(rect => { if (!rect) { wx.showToast({ title: '画布获取失败', icon: 'none' }); return; } const canvasWidth = rect.width; const canvasHeight = rect.height; // 1. 绘制背景 ctx.drawImage(bgPath, 0, 0, canvasWidth, canvasHeight); // 🔥【位置调整】二维码向右上角移动一丢丢 (增加 margin 从 5 到 15) const qrSize = 100; const margin = 15; // 原来 5,现在 15,向右下各移一点,但看起来更协调 const qrX = canvasWidth - qrSize - margin; const qrY = canvasHeight - qrSize - margin; ctx.drawImage(qrPath, qrX, qrY, qrSize, qrSize); // 🔥【位置调整】头像也向右上移动一丢丢 (原来 margin 5,现在 margin 15) const avatarSize = 60; const avatarMargin = 15; const avatarX = avatarMargin + 10; // 原来 margin 5+20,现在 15+20,相当于右移10 const avatarY = canvasHeight - avatarSize - avatarMargin; // 圆形头像裁剪 ctx.save(); ctx.beginPath(); ctx.arc(avatarX + avatarSize/2, avatarY + avatarSize/2, avatarSize/2, 0, 2 * Math.PI); ctx.clip(); ctx.drawImage(avatarPath, avatarX, avatarY, avatarSize, avatarSize); ctx.restore(); // 绘制昵称(右侧) ctx.setFontSize(16); ctx.setFillStyle('#333333'); // 根据背景图颜色可调整 ctx.setTextAlign('left'); let nick = this.data.nickName || '组长'; if (nick.length > 3) { nick = nick.substring(0, 3) + '...'; } const nameText = nick; const textX = avatarX + avatarSize + 8; const textY = avatarY + avatarSize/2 + 6; ctx.fillText(nameText, textX, textY); // 绘制邀请语 ctx.setFontSize(18); ctx.setFillStyle('#9333ea'); // 金黄色,呼应组长主题 ctx.setTextAlign('left'); const slogan = '加入我的团队 ✦'; const sloganX = avatarX; const sloganY = avatarY - 12; ctx.fillText(slogan, sloganX, sloganY); // 异步绘制 ctx.draw(false, () => { wx.canvasToTempFilePath({ canvasId: 'posterCanvas', success: (res) => { this.setData({ posterImageTemp: res.tempFilePath }); }, fail: (err) => { console.error('生成海报临时文件失败', err); } }, this); }); }).exec(); }, /** * 保存生成的海报 */ savePoster() { if (!this.data.posterImageTemp) { wx.showToast({ title: '海报尚未生成', icon: 'none' }); return; } this.downloadAndSave(this.data.posterImageTemp); }, /** * 隐藏海报画布,返回主界面 */ hidePoster() { this.setData({ showPosterCanvas: false, posterImageTemp: '' }); }, /** * 通用保存图片到相册(带权限检查) */ downloadAndSave(filePath) { wx.getSetting({ success: (res) => { if (!res.authSetting['scope.writePhotosAlbum']) { wx.authorize({ scope: 'scope.writePhotosAlbum', success: () => { this._saveImage(filePath); }, fail: () => { wx.showModal({ title: '提示', content: '需要您授权保存到相册', success: (modalRes) => { if (modalRes.confirm) { wx.openSetting(); } } }); } }); } else { this._saveImage(filePath); } } }); }, _saveImage(filePath) { wx.saveImageToPhotosAlbum({ filePath: filePath, success: () => { wx.showToast({ title: '保存成功', icon: 'success' }); }, fail: (err) => { console.error('保存失败', err); wx.showToast({ title: '保存失败', icon: 'none' }); } }); }, });