实现方式
1:
## 问题标题
小程序拼单分享 `onShareAppMessage` 已返回 `imageUrl`,但聊天卡片仍灰色占位(体验版)
## 业务场景
拼单分享页点击“分享”按钮,分享给微信好友。
目标:卡片显示自定义标题 + 商品封面图。
---
## 代码实现(可直接复现)
### 1)拼单列表页分享入口
文件:`pages/sharebill/sharebillList/sharebillList.js`
const {
buildShareAppMessageWithLocalImage,
SHARE_FALLBACK_IMAGE_URL,
pushShareDebugLog
} = require('../../../utils/shareCover.js');
const { getStaticFileBaseForRelativePath } = require('../../../utils/staticFileBase.js');
Page({
onShareButtonTap(e) {
const dataset = e.currentTarget.dataset;
const orderId = dataset.orderId;
const shareBillNumber = dataset.shareBillNumber || '';
const productImage = dataset.productImage || '';
if (orderId) {
this.setData({
sharingMode: 'sharebill',
sharingOrderId: orderId,
sharingShareBillNumber: shareBillNumber,
sharingImageUrl: productImage
});
}
},
onShareAppMessage(options) {
let mode = this.data.sharingMode || 'sharebill';
let productImageRaw = this.data.sharingImageUrl || '';
let orderId = this.data.sharingOrderId;
let shareBillNumber = this.data.sharingShareBillNumber || '';
let productId = this.data.sharingGroupProductId || '';
let groupId = this.data.sharingGroupId || '';
// open-type="share" 走 button 时,优先从 options.target.dataset 取值
if (options && options.from === 'button' && options.target) {
const d = options.target.dataset || {};
const pid = d.productId;
const gid = d.groupId;
if (pid != null && pid !== '' && gid != null && gid !== '') {
mode = 'groupbuy';
productId = pid;
groupId = gid;
productImageRaw = d.productImage != null ? d.productImage : '';
} else if (d.orderId != null && d.orderId !== '') {
mode = 'sharebill';
orderId = d.orderId;
shareBillNumber = d.shareBillNumber != null ? d.shareBillNumber : '';
productImageRaw = d.productImage != null ? d.productImage : '';
}
}
const imageUrl = this.resolveShareImageUrl(productImageRaw || '');
pushShareDebugLog('sharebill.list.onShareAppMessage', {
from: options && options.from ? options.from : '',
mode,
orderId: orderId != null ? String(orderId) : '',
shareBillNumber: shareBillNumber || '',
productId: productId != null ? String(productId) : '',
groupId: groupId != null ? String(groupId) : '',
productImageRaw: productImageRaw || '',
resolvedImageUrl: imageUrl
});
if (mode === 'groupbuy') {
const sharePath = `/pages/goods/goods?id=${encodeURIComponent(String(productId))}&groupId=${encodeURIComponent(String(groupId))}`;
return buildShareAppMessageWithLocalImage({
title: '拼团邀请就差你了',
path: sharePath,
imageUrl
});
}
const sharePath = `/pages/sharebill/sharebillDetail/sharebillDetail?shareType=shareBillType&share_bill_number=${shareBillNumber}&order_id=${orderId != null ? orderId : ''}`;
return buildShareAppMessageWithLocalImage({
title: '拼单返现就差你了',
path: sharePath,
imageUrl
});
},
resolveShareImageUrl(url) {
let u = url && typeof url === 'string' ? url.trim() : '';
if (/^\/\//.test(u)) u = `https:${u}`;
if (u && /^https?:\/\//i.test(u)) return u;
const site = getStaticFileBaseForRelativePath(u);
if (u && site) {
const normalizedSite = site.endsWith('/') ? site.slice(0, -1) : site;
const normalizedPath = u.startsWith('/') ? u : `/${u}`;
return `${normalizedSite}${normalizedPath}`;
}
return SHARE_FALLBACK_IMAGE_URL;
}
});
2:分享工具方法
文件:utils/shareCover.js
const SHARE_FALLBACK_IMAGE_URL =
'https://ling.sanjuhealth.com/system_imge/4e9c5ba6-315d-4259-9a2d-d1b7df8bc457.png';
const SHARE_DEBUG_KEY = '__share_debug_logs__';
const SHARE_USE_PROMISE_IMAGE_FLOW = false;
function pushShareDebugLog(stage, payload) {
const row = { ts: new Date().toISOString(), stage, payload: payload || {} };
console.log('[share-debug]', row);
try {
const old = wx.getStorageSync(SHARE_DEBUG_KEY);
const list = Array.isArray(old) ? old : [];
list.push(row);
while (list.length > 80) list.shift();
wx.setStorageSync(SHARE_DEBUG_KEY, list);
} catch (e) {}
}
function coerceImageUrlForShareCard(url) {
if (!url || typeof url !== 'string') return SHARE_FALLBACK_IMAGE_URL;
const t = url.trim();
if (!t) return SHARE_FALLBACK_IMAGE_URL;
if (/\.webp(\?|#|$)/i.test(t)) return SHARE_FALLBACK_IMAGE_URL;
return t;
}
function buildShareAppMessageWithLocalImage(payload) {
const title = payload.title || '活龄小程序';
const path = payload.path || '/pages/main/main';
const imageUrl = coerceImageUrlForShareCard(payload.imageUrl);
pushShareDebugLog('share.appmessage.payload', { title, path, imageUrl });
// 兼容优先:同步返回 object(不走 promise + tempFile)
if (!SHARE_USE_PROMISE_IMAGE_FLOW) {
return { title, path, imageUrl };
}
// 备选:promise 流(目前已关闭)
return {
title,
path,
imageUrl
};
}
module.exports = {
SHARE_FALLBACK_IMAGE_URL,
SHARE_DEBUG_KEY,
pushShareDebugLog,
buildShareAppMessageWithLocalImage
};
3:全局包装(Page 重写)
文件:app.js
const ENABLE_SHARE_LOGIN_TIP = true;
const originalPage = Page;
Page = function(options) {
const getCurrentUserId = function() {
try {
const userInfoStr = wx.getStorageSync('userInfo');
if (userInfoStr) {
const userInfo = typeof userInfoStr === 'string' ? JSON.parse(userInfoStr) : userInfoStr;
return userInfo.id || userInfo.userId || null;
}
} catch (e) {}
return null;
};
const processSharePath = function(path) {
if (!path) return path;
const referrerId = getCurrentUserId();
if (!referrerId) return path;
if (path.includes('referrer_id=')) return path;
const separator = path.includes('?') ? '&' : '?';
return `${path}${separator}referrer_id=${referrerId}`;
};
const maybeShowShareLoginTip = function(shareConfig) {
if (!ENABLE_SHARE_LOGIN_TIP) return shareConfig;
if (getCurrentUserId()) return shareConfig;
return new Promise(function(resolve, reject) {
wx.showModal({
title: '提示',
content: '您还未登录,分享链接将不携带您的邀请人ID,新用户将无法成为您的下级。',
confirmText: '分享',
cancelText: '去登录',
success: function(res) {
if (res.confirm) resolve(shareConfig);
else {
wx.navigateTo({ url: '/pages/login/login' });
reject();
}
}
});
});
};
const applyShareAppMessageWrap = function(shareConfig) {
if (!shareConfig || typeof shareConfig !== 'object') {
return maybeShowShareLoginTip(shareConfig);
}
if (shareConfig.path) {
shareConfig.path = processSharePath(shareConfig.path);
}
return maybeShowShareLoginTip(shareConfig);
};
if (options.onShareAppMessage) {
const originalOnShareAppMessage = options.onShareAppMessage;
options.onShareAppMessage = function(shareOptions) {
const raw = originalOnShareAppMessage.call(this, shareOptions);
if (raw && typeof raw.then === 'function') {
return raw.then(applyShareAppMessageWrap);
}
return applyShareAppMessageWrap(raw);
};
}
return originalPage(options);
};
日志
[share-debug] stage: sharebill.list.onShareAppMessage
payload:
from: "button"
mode: "sharebill"
orderId: "63"
shareBillNumber: "SB1776..."
productImageRaw: "https://ling.sanjuhealth.com/product_img/5fefe53f-1c0c-4963-8fe0-92423c9435b5.png"
resolvedImageUrl: "https://ling.sanjuhealth.com/product_img/5fefe53f-1c0c-4963-8fe0-92423c9435b5.png"
[share-debug] stage: share.appmessage.payload
payload:
title: "拼单返现就差你了"
path: "/pages/sharebill/sharebillDetail/sharebillDetail?shareType=shareBillType&share_bill_number=SB1776...&order_id=63"
imageUrl: "https://ling.sanjuhealth.com/product_img/5fefe53f-1c0c-4963-8fe0-92423c9435b5.png"
体验版还是正式版都无法现实
只有分享端(分享人用手机分享然后再被分享人的聊天记录中可以看到图片,但是被分享人自己的微信中是看不到图片的,并且分享人如果去PC端也是看不到图片的)

没复现 有稳定复现的方式吗 是只有你有问题还是其他人都可复现
AppID:wx3473d28075d29ae1
1)onShareAppMessage 返回值(分享者端日志):
title: 拼单返现就差你了
path: /pages/sharebill/sharebillDetail/sharebillDetail?shareType=...&order_id=63
imageUrl: https://ling.sanjuhealth.com/product_img/xxxx.png
2)图片可公网访问:
- HTTPS
- Content-Type: image/png
- HTTP 200
3)小程序后台域名已配置:
- request/downloadFile 均包含 https://ling.sanjuhealth.com
- 其他相关域名也已配置
4)被分享者可正常打开 path,对应详情接口返回 productImage 也是同一 URL。
问题是:仅聊天卡片封面灰色占位,不显示 imageUrl。
请协助确认:
- 体验版是否存在对 onShareAppMessage imageUrl 的平台侧策略限制?
- 是否有客户端版本已知问题?
- 该场景是否需要除 request/downloadFile 之外的额外配置?