ai-c/pages/promote-poster/promote-poster.js
2026-02-02 18:21:32 +08:00

248 lines
9.4 KiB
JavaScript
Raw Permalink 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.

const api = require('../../utils/api')
const app = getApp()
Page({
data: {
statusBarHeight: 44,
navBarHeight: 44,
totalNavHeight: 88,
posters: [],
currentPosterIndex: 0,
qrCodeUrl: '',
referralCode: '',
canvasWidth: 1080,
canvasHeight: 1920,
isLoading: true,
userInfo: null
},
onLoad(options) {
const systemInfo = wx.getSystemInfoSync()
const statusBarHeight = systemInfo.statusBarHeight || 44
const menuButton = wx.getMenuButtonBoundingClientRect()
const navBarHeight = menuButton.height + (menuButton.top - statusBarHeight) * 2
const totalNavHeight = statusBarHeight + navBarHeight
this.setData({
statusBarHeight,
navBarHeight,
totalNavHeight,
userInfo: app.globalData.userInfo || wx.getStorageSync('user_info')
})
this.loadData();
},
async loadData() {
try {
// 1. 获取推荐码
const statsRes = await api.commission.getStats();
let referralCode = 'default';
if (statsRes.success && statsRes.data) {
referralCode = statsRes.data.referralCode;
this.setData({ referralCode });
}
// 2. 设置小程序二维码地址
// scene 格式必须为 r=XXX 才能被 app.js 正确解析
const baseUrl = app.globalData.baseUrl || 'https://ai-c.maimanji.com';
const qrCodeUrl = `${baseUrl}/api/user/qrcode?scene=r=${referralCode}&page=pages/index/index`;
this.setData({ qrCodeUrl });
// 3. 获取动态海报背景列表
const assetRes = await api.pageAssets.getAssets('posters');
if (assetRes && assetRes.success && assetRes.data && assetRes.data.length > 0) {
const posters = assetRes.data.map(item => {
let url = (item.asset_url || '').trim();
// 如果是相对路径,补充完整域名
if (url && !url.startsWith('http')) {
url = baseUrl + (url.startsWith('/') ? '' : '/') + url;
}
return {
id: item.asset_key,
url: url,
qrBottom: 4.5, // 对应 1920 高度下的位置
qrRight: 8 // 对应 1080 宽度下的位置
};
});
this.setData({ posters, isLoading: false });
} else {
// 兜底默认海报
this.setData({
posters: [
{ id: 'default', url: 'https://ai-c.maimanji.com/uploads/assets/poster-1.png', qrBottom: 4.5, qrRight: 8 }
],
isLoading: false
});
}
} catch (err) {
console.error('[promote-poster] loadData failed:', err);
this.setData({ isLoading: false });
}
},
onPosterChange(e) {
this.setData({ currentPosterIndex: e.detail.current })
},
onImageError(e) {
console.error('[promote-poster] 海报图加载失败:', e.detail.errMsg);
wx.showToast({ title: '海报背景加载失败', icon: 'none' });
},
onQrError(e) {
console.error('[promote-poster] 二维码加载失败:', e.detail.errMsg);
wx.showToast({ title: '二维码加载失败', icon: 'none' });
},
/**
* 下载文件辅助函数
*/
downloadFile(url) {
return new Promise((resolve, reject) => {
wx.downloadFile({
url,
success: res => {
if (res.statusCode === 200) resolve(res.tempFilePath);
else reject(new Error('Download failed: ' + url));
},
fail: err => {
console.error('Download failed:', url, err);
reject(err);
}
});
});
},
async savePoster() {
if (this.data.posters.length === 0) return;
wx.showLoading({ title: '生成中...', mask: true });
try {
const template = this.data.posters[this.data.currentPosterIndex];
// 1. 下载资源
const [bgPath, qrPath] = await Promise.all([
this.downloadFile(template.url),
this.downloadFile(this.data.qrCodeUrl)
]);
// 2. 初始化 Canvas
const query = wx.createSelectorQuery()
query.select('#posterCanvas')
.fields({ node: true, size: true })
.exec(async (res) => {
if (!res[0] || !res[0].node) {
wx.hideLoading();
wx.showToast({ title: 'Canvas初始化失败', icon: 'none' });
return;
}
const canvas = res[0].node
const ctx = canvas.getContext('2d')
const dpr = wx.getSystemInfoSync().pixelRatio
const canvasW = 1080;
const canvasH = 1920;
canvas.width = canvasW * dpr
canvas.height = canvasH * dpr
ctx.scale(dpr, dpr)
// 3. 绘制背景
const bgImg = canvas.createImage();
bgImg.src = bgPath;
await new Promise((resolve, reject) => {
bgImg.onload = resolve;
bgImg.onerror = reject;
});
ctx.drawImage(bgImg, 0, 0, canvasW, canvasH);
// 4. 绘制二维码
const qrImg = canvas.createImage();
qrImg.src = qrPath;
await new Promise((resolve, reject) => {
qrImg.onload = resolve;
qrImg.onerror = reject;
});
// 识别白框位置:根据 1080x1920 设计稿,白框在右下角
// 二维码尺寸 260x260
const qrW = 260;
const qrX = 720; // 1080 - 100 - 260
const qrY = 1580; // 1920 - 80 - 260
// 绘制二维码背景(白框内可能需要微调,这里先绘制一个纯白背景确保清晰)
ctx.fillStyle = '#FFFFFF';
ctx.beginPath();
this.roundRect(ctx, qrX - 5, qrY - 5, qrW + 10, qrW + 10, 10);
ctx.fill();
ctx.drawImage(qrImg, qrX, qrY, qrW, qrW);
// 6. 导出
setTimeout(() => {
wx.canvasToTempFilePath({
canvas: canvas,
success: (fileRes) => {
wx.saveImageToPhotosAlbum({
filePath: fileRes.tempFilePath,
success: () => {
wx.hideLoading();
wx.showToast({ title: '已保存到相册', icon: 'success' })
},
fail: (err) => {
wx.hideLoading();
if (err.errMsg.indexOf('auth deny') !== -1) {
wx.showModal({
title: '提示',
content: '请授权保存图片到相册',
success: (sm) => {
if (sm.confirm) wx.openSetting();
}
});
} else {
wx.showToast({ title: '保存失败', icon: 'none' })
}
}
})
},
fail: (err) => {
console.error('canvasToTempFilePath fail:', err);
wx.hideLoading();
wx.showToast({ title: '生成图片失败', icon: 'none' });
}
})
}, 300);
})
} catch (err) {
console.error('[promote-poster] savePoster error:', err);
wx.hideLoading();
wx.showToast({ title: '海报资源加载失败', icon: 'none' });
}
},
roundRect(ctx, x, y, w, h, r) {
ctx.moveTo(x + r, y);
ctx.arcTo(x + w, y, x + w, y + h, r);
ctx.arcTo(x + w, y + h, x, y + h, r);
ctx.arcTo(x, y + h, x, y, r);
ctx.arcTo(x, y, x + w, y, r);
},
onShareAppMessage() {
const title = "发现一个超赞的AI情感陪伴官快来看看吧";
const imageUrl = this.data.posters[this.data.currentPosterIndex]?.url || '';
return {
title: title,
path: `/pages/index/index?referralCode=${this.data.referralCode}`,
imageUrl: imageUrl
}
},
goBack() {
wx.navigateBack();
}
});