|
|
@@ -475,13 +475,19 @@
|
|
|
|
|
|
#fabWrapWx.is-active {
|
|
|
display: block;
|
|
|
+ pointer-events: auto;
|
|
|
}
|
|
|
|
|
|
+ /* 开放标签宿主必须有明确宽高,否则微信内 Shadow 内容可能不占位、看起来像「没按钮」 */
|
|
|
#launch-btn {
|
|
|
display: block;
|
|
|
width: 100%;
|
|
|
max-width: 320px;
|
|
|
+ height: 48px;
|
|
|
+ min-height: 48px;
|
|
|
margin: 0 auto;
|
|
|
+ pointer-events: auto;
|
|
|
+ overflow: visible;
|
|
|
}
|
|
|
|
|
|
.fab {
|
|
|
@@ -1006,7 +1012,7 @@
|
|
|
}
|
|
|
</style>
|
|
|
<button class="wx-open-app-btn" aria-label="APP内打开">
|
|
|
- <img src="https://test.ailien.shop/h5/HBuilderProjects/images/uBtn.png" alt="APP内打开" />
|
|
|
+ <img src="images/uBtn.png" alt="APP内打开" />
|
|
|
</button>
|
|
|
</script>
|
|
|
</wx-open-launch-app>
|
|
|
@@ -1054,6 +1060,8 @@
|
|
|
var WX_GET_CONFIG_SIGN_URL = 'https://test.ailien.shop/h5/HBuilderProjects/shareIndex.html';
|
|
|
/** wx.config 已成功(开放标签可展示);与「用户已成功唤起 App」不是同一回事 */
|
|
|
var weChatJssdkConfigured = false;
|
|
|
+ var wxConfigSignRetriedBaseUrl = false;
|
|
|
+ var wxSignUrlFullBeforeStrip = '';
|
|
|
|
|
|
/**
|
|
|
* 关店(businessStatus=99)更多推荐:POST …/ai/multimodal-services/api/v1/search/global/store-recommend
|
|
|
@@ -1163,22 +1171,52 @@
|
|
|
return API_BASE.replace(/\/$/, '') + (custom.charAt(0) === '/' ? custom : '/' + custom);
|
|
|
}
|
|
|
|
|
|
+ /** 微信 JSSDK 参与签名的 url:完整链(含 ?),不含 # */
|
|
|
+ function getWxSignUrlFull() {
|
|
|
+ var forced = String(q('wxSignUrl') || '').trim();
|
|
|
+ if (forced) return forced.split('#')[0];
|
|
|
+ if (location.origin && /^https?:/i.test(location.origin)) {
|
|
|
+ return (
|
|
|
+ location.origin +
|
|
|
+ (location.pathname || '/h5/HBuilderProjects/shareIndex.html') +
|
|
|
+ (location.search || '')
|
|
|
+ );
|
|
|
+ }
|
|
|
+ var href = String(location.href || '').split('#')[0];
|
|
|
+ if (href && href.indexOf('file:') !== 0) return href;
|
|
|
+ return WX_GET_CONFIG_SIGN_URL;
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 无 query 的页面地址(后端若只签该地址,须配合 syncBrowserUrlForWxSign) */
|
|
|
+ function getWxSignUrlBase() {
|
|
|
+ if (location.origin && /^https?:/i.test(location.origin)) {
|
|
|
+ return location.origin + (location.pathname || '/h5/HBuilderProjects/shareIndex.html');
|
|
|
+ }
|
|
|
+ return WX_GET_CONFIG_SIGN_URL;
|
|
|
+ }
|
|
|
+
|
|
|
+ function getWxConfigSignUrl() {
|
|
|
+ if (String(q('wxSignBaseOnly') || '') === '1') return getWxSignUrlBase();
|
|
|
+ return getWxSignUrlFull();
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
- * /wx/getWxConfig 与 wx.config 签名用 url(微信官方要求):
|
|
|
- * 当前页完整地址,包含 ? 后 GET 参数,不包含 # 后内容。
|
|
|
- * 若只签 https://.../shareIndex.html 而实际打开带 ?id=...,会报 invalid signature。
|
|
|
+ * 微信校验签名用的是地址栏当前 url(不含#)。
|
|
|
+ * 若后端只签了无参地址,须先把地址栏 sync 成与签名 url 一致再 wx.config。
|
|
|
*/
|
|
|
- function getWxConfigSignUrl() {
|
|
|
- var u = String(location.href || '').split('#')[0];
|
|
|
- if (!u || u.indexOf('file:') === 0) {
|
|
|
- return WX_GET_CONFIG_SIGN_URL;
|
|
|
+ function syncBrowserUrlForWxSign(signUrl) {
|
|
|
+ var target = String(signUrl || '').split('#')[0];
|
|
|
+ if (!target) return;
|
|
|
+ var hash = location.hash || '';
|
|
|
+ var current = String(location.href || '').split('#')[0];
|
|
|
+ if (current !== target) {
|
|
|
+ history.replaceState(history.state, document.title, target + hash);
|
|
|
}
|
|
|
- return u;
|
|
|
}
|
|
|
|
|
|
- /** 唤起 App 的 extinfo:与签名 url 相同规则 */
|
|
|
+ /** 唤起 App 的 extinfo:保留完整分享链 */
|
|
|
function getPageUrlForWxSign() {
|
|
|
- return getWxConfigSignUrl();
|
|
|
+ return getWxSignUrlFull();
|
|
|
}
|
|
|
|
|
|
function buildWxGetConfigRequestUrl(signPageUrl) {
|
|
|
@@ -1240,7 +1278,7 @@
|
|
|
return String(raw).trim();
|
|
|
}
|
|
|
|
|
|
- function normalizeWxJssdkSignPayload(res) {
|
|
|
+ function normalizeWxJssdkSignPayload(res, signUrlUsed) {
|
|
|
if (!res || typeof res !== 'object') return null;
|
|
|
var d = res.data != null && typeof res.data === 'object' ? res.data : res;
|
|
|
if (!d || typeof d !== 'object') return null;
|
|
|
@@ -1249,11 +1287,18 @@
|
|
|
var nonceStr = d.nonceStr || d.noncestr || d.nonce;
|
|
|
var signature = d.signature || d.sign;
|
|
|
if (!appId || timestamp == null || !nonceStr || !signature) return null;
|
|
|
+ var signedUrl =
|
|
|
+ d.signUrl != null && String(d.signUrl).trim() !== ''
|
|
|
+ ? String(d.signUrl).trim().split('#')[0]
|
|
|
+ : signUrlUsed != null && String(signUrlUsed).trim() !== ''
|
|
|
+ ? String(signUrlUsed).trim().split('#')[0]
|
|
|
+ : '';
|
|
|
return {
|
|
|
appId: String(appId),
|
|
|
timestamp: Number(timestamp),
|
|
|
nonceStr: String(nonceStr),
|
|
|
- signature: String(signature)
|
|
|
+ signature: String(signature),
|
|
|
+ signUrl: signedUrl
|
|
|
};
|
|
|
}
|
|
|
|
|
|
@@ -1263,15 +1308,14 @@
|
|
|
else if (typeof err === 'string') errMsg = err;
|
|
|
var tip = '微信 JSSDK 配置失败';
|
|
|
if (/invalid signature/i.test(errMsg)) {
|
|
|
- var signUrl = signPageUrl || getWxConfigSignUrl();
|
|
|
- var hasQuery = signUrl.indexOf('?') >= 0;
|
|
|
+ var signUrl = signPageUrl || getWxSignUrlFull();
|
|
|
+ var pageNow = String(location.href || '').split('#')[0];
|
|
|
return (
|
|
|
tip +
|
|
|
- ':签名无效(invalid signature)。后端签名用的 url 必须与微信当前页完全一致(含?后参数、不含#):' +
|
|
|
- signUrl +
|
|
|
- (hasQuery
|
|
|
- ? ''
|
|
|
- : '(若分享链带 id/storeId,不能只用无参数的 shareIndex.html 签名)')
|
|
|
+ ':签名无效。请让后端用与地址栏完全相同的 url 参与签名(不含#)。当前地址栏:' +
|
|
|
+ pageNow +
|
|
|
+ ';本次请求签名 url:' +
|
|
|
+ signUrl
|
|
|
);
|
|
|
}
|
|
|
if (/invalid appid/i.test(errMsg)) {
|
|
|
@@ -1284,8 +1328,8 @@
|
|
|
return tip + ',开放标签不可用。';
|
|
|
}
|
|
|
|
|
|
- function fetchWeChatJssdkSign() {
|
|
|
- var signPageUrl = getWxConfigSignUrl();
|
|
|
+ function fetchWeChatJssdkSign(signPageUrl) {
|
|
|
+ signPageUrl = signPageUrl || getWxConfigSignUrl();
|
|
|
var urls = [];
|
|
|
var custom = getWeChatJssdkSignRequestUrl();
|
|
|
if (custom) {
|
|
|
@@ -1313,7 +1357,7 @@
|
|
|
throw new Error(res.msg || 'sign rejected');
|
|
|
}
|
|
|
}
|
|
|
- var pack = normalizeWxJssdkSignPayload(res);
|
|
|
+ var pack = normalizeWxJssdkSignPayload(res, signPageUrl);
|
|
|
if (!pack) throw new Error('empty sign payload');
|
|
|
return pack;
|
|
|
})
|
|
|
@@ -1324,6 +1368,81 @@
|
|
|
return tryUrl(0);
|
|
|
}
|
|
|
|
|
|
+ function applyWxConfig(sign, signPageUrl) {
|
|
|
+ var urlForWx =
|
|
|
+ sign.signUrl && String(sign.signUrl).trim() !== ''
|
|
|
+ ? String(sign.signUrl).trim()
|
|
|
+ : signPageUrl;
|
|
|
+ syncBrowserUrlForWxSign(urlForWx);
|
|
|
+ console.log('[wx.config] appId=', sign.appId, 'signUrl=', urlForWx, 'page=', location.href.split('#')[0]);
|
|
|
+
|
|
|
+ return new Promise(function (resolve) {
|
|
|
+ wx.config({
|
|
|
+ debug: String(q('wxDebug') || '') === '1',
|
|
|
+ appId: sign.appId,
|
|
|
+ timestamp: sign.timestamp,
|
|
|
+ nonceStr: sign.nonceStr,
|
|
|
+ signature: sign.signature,
|
|
|
+ jsApiList: ['checkJsApi'],
|
|
|
+ openTagList: ['wx-open-launch-app']
|
|
|
+ });
|
|
|
+ wx.ready(function () {
|
|
|
+ weChatJssdkConfigured = true;
|
|
|
+ if (wxSignUrlFullBeforeStrip) {
|
|
|
+ var restore = wxSignUrlFullBeforeStrip + (location.hash || '');
|
|
|
+ history.replaceState(history.state, document.title, restore);
|
|
|
+ wxSignUrlFullBeforeStrip = '';
|
|
|
+ }
|
|
|
+ var tag = document.getElementById('launch-btn');
|
|
|
+ if (tag) {
|
|
|
+ try {
|
|
|
+ tag.setAttribute('extinfo', buildWeChatLaunchExtinfo());
|
|
|
+ } catch (eReady) {}
|
|
|
+ }
|
|
|
+ setFabLaunchMode('wx');
|
|
|
+ resolve(true);
|
|
|
+ });
|
|
|
+ wx.error(function (err) {
|
|
|
+ console.warn('[wx.config]', err, 'signUrl=', urlForWx);
|
|
|
+ var errMsg = err && err.errMsg ? String(err.errMsg) : '';
|
|
|
+ var baseUrl = getWxSignUrlBase();
|
|
|
+ var fullUrl = getWxSignUrlFull();
|
|
|
+ if (
|
|
|
+ !/invalid signature/i.test(errMsg) ||
|
|
|
+ wxConfigSignRetriedBaseUrl ||
|
|
|
+ baseUrl === fullUrl ||
|
|
|
+ urlForWx === baseUrl
|
|
|
+ ) {
|
|
|
+ weChatJssdkConfigured = false;
|
|
|
+ showAppOpenFailTip(formatWxConfigErrorTip(err, urlForWx));
|
|
|
+ setFabLaunchMode('scheme');
|
|
|
+ resolve(false);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ wxConfigSignRetriedBaseUrl = true;
|
|
|
+ wxSignUrlFullBeforeStrip = fullUrl;
|
|
|
+ console.warn('[wx.config] retry with base url (no query):', baseUrl);
|
|
|
+ fetchWeChatJssdkSign(baseUrl)
|
|
|
+ .then(function (sign2) {
|
|
|
+ if (!sign2) {
|
|
|
+ weChatJssdkConfigured = false;
|
|
|
+ showAppOpenFailTip(formatWxConfigErrorTip(err, urlForWx));
|
|
|
+ setFabLaunchMode('scheme');
|
|
|
+ resolve(false);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ applyWxConfig(sign2, baseUrl).then(resolve);
|
|
|
+ })
|
|
|
+ .catch(function () {
|
|
|
+ weChatJssdkConfigured = false;
|
|
|
+ showAppOpenFailTip(formatWxConfigErrorTip(err, urlForWx));
|
|
|
+ setFabLaunchMode('scheme');
|
|
|
+ resolve(false);
|
|
|
+ });
|
|
|
+ });
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
function setFabLaunchMode(mode) {
|
|
|
var wxWrap = document.getElementById('fabWrapWx');
|
|
|
var fbWrap = document.getElementById('fabWrapFallback');
|
|
|
@@ -1343,7 +1462,7 @@
|
|
|
}
|
|
|
if (fbWrap) {
|
|
|
fbWrap.style.display = '';
|
|
|
- fbWrap.setAttribute('aria-hidden', 'false');
|
|
|
+ fbWrap.removeAttribute('aria-hidden');
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -1384,6 +1503,7 @@
|
|
|
*/
|
|
|
function initWeChatOpenLaunchApp() {
|
|
|
var inWx = isWeChatInAppBrowser();
|
|
|
+ wxConfigSignRetriedBaseUrl = false;
|
|
|
var signPageUrl = getWxConfigSignUrl();
|
|
|
var reqUrl = buildWxGetConfigRequestUrl(signPageUrl);
|
|
|
|
|
|
@@ -1395,16 +1515,13 @@
|
|
|
console.warn('[wx]', domainHint);
|
|
|
showAppOpenFailTip(domainHint);
|
|
|
}
|
|
|
- var fbWrap0 = document.getElementById('fabWrapFallback');
|
|
|
- if (fbWrap0) {
|
|
|
- fbWrap0.style.display = 'none';
|
|
|
- fbWrap0.setAttribute('aria-hidden', 'true');
|
|
|
- }
|
|
|
+ /* 签名完成前仍显示普通按钮,避免底部长时间空白 */
|
|
|
+ setFabLaunchMode('scheme');
|
|
|
}
|
|
|
|
|
|
console.log('[wx] GET', reqUrl);
|
|
|
|
|
|
- return fetchWeChatJssdkSign()
|
|
|
+ return fetchWeChatJssdkSign(signPageUrl)
|
|
|
.then(function (sign) {
|
|
|
if (!inWx) {
|
|
|
if (!sign) {
|
|
|
@@ -1426,36 +1543,7 @@
|
|
|
setFabLaunchMode('scheme');
|
|
|
return false;
|
|
|
}
|
|
|
- console.log('[wx.config] appId=', sign.appId, 'signUrl=', signPageUrl);
|
|
|
- return new Promise(function (resolve) {
|
|
|
- wx.config({
|
|
|
- debug: String(q('wxDebug') || '') === '1',
|
|
|
- appId: sign.appId,
|
|
|
- timestamp: sign.timestamp,
|
|
|
- nonceStr: sign.nonceStr,
|
|
|
- signature: sign.signature,
|
|
|
- jsApiList: ['checkJsApi'],
|
|
|
- openTagList: ['wx-open-launch-app']
|
|
|
- });
|
|
|
- wx.ready(function () {
|
|
|
- weChatJssdkConfigured = true;
|
|
|
- var tag = document.getElementById('launch-btn');
|
|
|
- if (tag) {
|
|
|
- try {
|
|
|
- tag.setAttribute('extinfo', buildWeChatLaunchExtinfo());
|
|
|
- } catch (eReady) {}
|
|
|
- }
|
|
|
- setFabLaunchMode('wx');
|
|
|
- resolve(true);
|
|
|
- });
|
|
|
- wx.error(function (err) {
|
|
|
- console.warn('[wx.config]', err);
|
|
|
- weChatJssdkConfigured = false;
|
|
|
- showAppOpenFailTip(formatWxConfigErrorTip(err, signPageUrl));
|
|
|
- setFabLaunchMode('scheme');
|
|
|
- resolve(false);
|
|
|
- });
|
|
|
- });
|
|
|
+ return applyWxConfig(sign, signPageUrl);
|
|
|
})
|
|
|
.catch(function (e) {
|
|
|
console.warn('[wx] init failed', e);
|
|
|
@@ -2727,11 +2815,9 @@
|
|
|
tryOpenHBuilderApp();
|
|
|
});
|
|
|
}
|
|
|
+ initWeChatOpenLaunchApp();
|
|
|
}
|
|
|
|
|
|
- /** 进入页立即请求 getWxConfig,不等到 window.onload */
|
|
|
- initWeChatOpenLaunchApp();
|
|
|
-
|
|
|
if (document.readyState === 'complete') {
|
|
|
boot();
|
|
|
} else {
|