diff --git a/components-diy/diy-recommend-code.vue b/components-diy/diy-recommend-code.vue
index 8024c65..94b1016 100644
--- a/components-diy/diy-recommend-code.vue
+++ b/components-diy/diy-recommend-code.vue
@@ -327,6 +327,9 @@
+
+
@@ -732,12 +735,197 @@ export default {
onShareAppMessage() {
const v = this.nc;
const title = v.shareTitle || v.title || '邀您入驻低空经济产业平台';
+ // 小程序环境下,生成「仅推荐码组件」的截图海报作为分享图
+ // H5 平台不进入 canvas 路径,直接用 diyData.shareImageUrl
+ if (!this.isH5Platform) {
+ return new Promise((resolve) => {
+ // 先生成海报;失败或非小程序时回退到默认 imageUrl
+ this.generateSharePoster()
+ .then((posterPath) => {
+ resolve({
+ title,
+ path: this.extractMiniProgramPath(),
+ imageUrl: posterPath || (v.shareImageUrl || '')
+ });
+ })
+ .catch(() => {
+ resolve({
+ title,
+ path: this.extractMiniProgramPath(),
+ imageUrl: v.shareImageUrl || ''
+ });
+ });
+ });
+ }
return {
title,
path: this.extractMiniProgramPath(),
imageUrl: v.shareImageUrl || ''
};
},
+ /**
+ * 生成推荐码组件的分享海报
+ * 画布内容:白底 + 标题 + 推荐码大字 + 二维码(如果有) + 简短提示
+ * 返回 Promise
+ */
+ generateSharePoster() {
+ const v = this.nc;
+ const code = this.code || v.placeholderCode || 'ABCD1234';
+ const title = v.title || '我的推荐码';
+ const W = 375;
+ const H = 480;
+ return new Promise((resolve, reject) => {
+ try {
+ const ctx = uni.createCanvasContext('sharePosterCanvas', this);
+ if (!ctx) return reject(new Error('createCanvasContext 失败'));
+ // 1. 白底
+ ctx.setFillStyle('#FFFFFF');
+ ctx.fillRect(0, 0, W, H);
+ // 2. 顶部标题
+ ctx.setFontSize(18);
+ ctx.setFillStyle('#1A1A1A');
+ ctx.setTextAlign('left');
+ ctx.fillText(title, 24, 44);
+ // 3. 提示文字
+ ctx.setFontSize(12);
+ ctx.setFillStyle('#999999');
+ ctx.fillText('分享给好友,邀请入驻获得奖励', 24, 70);
+ // 4. 米黄背景区
+ ctx.setFillStyle('#FFF8F0');
+ const blockY = 90, blockH = 120, blockX = 24, blockW = W - 48;
+ this._roundRect(ctx, blockX, blockY, blockW, blockH, 12);
+ ctx.fill();
+ // 5. 推荐码(居中)
+ ctx.setFontSize(36);
+ ctx.setFillStyle(v.codeColor || '#DC143C');
+ ctx.setTextAlign('center');
+ ctx.setFontWeight('bold');
+ ctx.fillText(code, W / 2, blockY + blockH / 2 + 12);
+ // 6. 二维码(如果有 url)
+ const qr = this.qrcodeRenderUrl;
+ if (qr) {
+ // 先下载到本地临时路径
+ this._downloadImage(qr).then((localPath) => {
+ // 二维码绘制在中部下方,正方形 180x180
+ const qrSize = 180;
+ const qrX = (W - qrSize) / 2;
+ const qrY = blockY + blockH + 24;
+ // 白色边框区
+ ctx.setFillStyle('#FFFFFF');
+ this._roundRect(ctx, qrX - 6, qrY - 6, qrSize + 12, qrSize + 12, 12);
+ ctx.fill();
+ ctx.drawImage(localPath, qrX, qrY, qrSize, qrSize);
+ // 7. 底部提示
+ ctx.setFontSize(11);
+ ctx.setFillStyle('#999999');
+ ctx.setTextAlign('center');
+ ctx.fillText(v.posterQrTip || '扫码进入并填写入驻信息', W / 2, qrY + qrSize + 24);
+ this._exportCanvas(W, H).then(resolve).catch(reject);
+ }).catch(() => {
+ // 二维码加载失败,直接导出无二维码版
+ this._exportCanvas(W, H).then(resolve).catch(reject);
+ });
+ } else {
+ this._exportCanvas(W, H).then(resolve).catch(reject);
+ }
+ } catch (e) {
+ reject(e);
+ }
+ });
+ },
+ /**
+ * canvas 转图片(uni.canvasToTempFilePath Promise 化)
+ */
+ _exportCanvas(W, H) {
+ return new Promise((resolve, reject) => {
+ // 微信小程序: type=2d canvas,需要传入 canvas 实例
+ // 旧版非 type=2d: 用 canvasId 字符串
+ // 这里两种方式都尝试
+ const query = uni.createSelectorQuery().in(this);
+ query.select('#sharePosterCanvas')
+ .fields({ node: true, size: true })
+ .exec((res) => {
+ if (res && res[0] && res[0].node) {
+ // 新版 type=2d canvas
+ uni.canvasToTempFilePath({
+ canvas: res[0].node,
+ width: W,
+ height: H,
+ destWidth: W * 2,
+ destHeight: H * 2,
+ fileType: 'png',
+ quality: 1,
+ success: (r) => resolve(r.tempFilePath),
+ fail: (err) => {
+ // 退化到旧 API
+ uni.canvasToTempFilePath({
+ canvasId: 'sharePosterCanvas',
+ width: W,
+ height: H,
+ destWidth: W * 2,
+ destHeight: H * 2,
+ fileType: 'png',
+ quality: 1,
+ success: (r2) => resolve(r2.tempFilePath),
+ fail: (err2) => reject(err2 || err)
+ }, this);
+ }
+ }, this);
+ } else {
+ // 旧版 canvas:ctx.draw(false, ...) 后导出
+ const ctx = uni.createCanvasContext('sharePosterCanvas', this);
+ if (ctx && ctx.draw) {
+ ctx.draw(false, () => {
+ uni.canvasToTempFilePath({
+ canvasId: 'sharePosterCanvas',
+ width: W,
+ height: H,
+ destWidth: W * 2,
+ destHeight: H * 2,
+ fileType: 'png',
+ quality: 1,
+ success: (r) => resolve(r.tempFilePath),
+ fail: (err) => reject(err)
+ }, this);
+ });
+ } else {
+ reject(new Error('canvas 初始化失败'));
+ }
+ }
+ });
+ });
+ },
+ /**
+ * 圆角矩形路径(canvas API)
+ */
+ _roundRect(ctx, x, y, w, h, r) {
+ ctx.beginPath();
+ 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);
+ ctx.closePath();
+ },
+ /**
+ * 下载图片到临时路径(Promise 化)
+ * 支持本地路径直接返回
+ */
+ _downloadImage(url) {
+ return new Promise((resolve, reject) => {
+ if (!url) return reject(new Error('url 为空'));
+ // 已是临时/本地路径
+ if (!/^https?:\/\//i.test(url)) return resolve(url);
+ uni.downloadFile({
+ url,
+ success: (res) => {
+ if (res.statusCode === 200 && res.tempFilePath) resolve(res.tempFilePath);
+ else reject(new Error('下载失败:' + res.statusCode));
+ },
+ fail: reject
+ });
+ });
+ },
onShareTimeline() {
const v = this.nc;
// 从H5分享链接中提取完整查询参数,与分享给好友保持一致