sunshibo 2 weeks ago
parent
commit
be2a027b5a
1 changed files with 291 additions and 5 deletions
  1. 291 5
      HBuilderProjects/shareIndex.html

+ 291 - 5
HBuilderProjects/shareIndex.html

@@ -469,6 +469,28 @@
 			pointer-events: auto;
 		}
 
+		.fab-wx-launch-host {
+			display: none;
+			position: absolute;
+			left: 24px;
+			right: 24px;
+			bottom: calc(12px + var(--safe-bottom));
+			z-index: 3;
+			max-width: 320px;
+			margin: 0 auto;
+			pointer-events: none;
+		}
+
+		.fab-wx-launch-host--active {
+			display: block;
+			pointer-events: auto;
+		}
+
+		.fab-wx-launch-host wx-open-launch-app {
+			display: block;
+			width: 100%;
+		}
+
 		.fab {
 			display: flex;
 			align-items: center;
@@ -969,6 +991,7 @@
 	</div>
 	<div id="shareBelowContentEl">
 		<div class="fab-wrap">
+			<div class="fab-wx-launch-host" id="openAppWxLaunchHost" aria-hidden="true"></div>
 			<button type="button" class="fab" id="openApp">
 				<img src="images/uBtn.png" alt="APP内打开" decoding="async">
 			</button>
@@ -995,6 +1018,10 @@
 		 * 请用本地 HTTP 打开(如 VS Code Live Server、npx serve)或让后端为 H5 域名配置 Access-Control-Allow-Origin。
 		 */
 		var API_BASE = 'https://test.ailien.shop/alienStore';
+		/** 公众号内 JS-SDK 签名:GET …/wx/getWxConfig?url=当前页地址(不含 #) */
+		var WX_CONFIG_API = 'https://test.ailien.shop/wx/getWxConfig';
+		/** 微信开放平台「移动应用」AppID;若 getWxConfig 未返回 launchAppId 等字段则使用此值 */
+		var WX_LAUNCH_APP_ID = '';
 
 		/**
 		 * 关店(businessStatus=99)更多推荐:POST …/ai/multimodal-services/api/v1/search/global/store-recommend
@@ -1080,6 +1107,216 @@
 			return /MicroMessenger/i.test(navigator.userAgent || '');
 		}
 
+		var wxJssdkLoadPromise = null;
+		var wxConfigReady = false;
+		var wxLaunchAppIdCache = '';
+		var wxLaunchMounted = false;
+		var wxPreparePromise = null;
+
+		function getWxSignPageUrl() {
+			return String(location.href || '').split('#')[0];
+		}
+
+		function loadWxJssdk() {
+			if (typeof wx !== 'undefined' && typeof wx.config === 'function') {
+				return Promise.resolve();
+			}
+			if (wxJssdkLoadPromise) return wxJssdkLoadPromise;
+			wxJssdkLoadPromise = new Promise(function (resolve, reject) {
+				function attach(src, isFallback) {
+					var s = document.createElement('script');
+					s.src = src;
+					s.async = true;
+					s.onload = function () {
+						if (typeof wx !== 'undefined') resolve();
+						else reject(new Error('jweixin 未加载'));
+					};
+					s.onerror = function () {
+						if (!isFallback) {
+							attach('https://res2.wx.qq.com/open/js/jweixin-1.6.0.js', true);
+							return;
+						}
+						reject(new Error('jweixin 加载失败'));
+					};
+					document.head.appendChild(s);
+				}
+				attach('https://res.wx.qq.com/open/js/jweixin-1.6.0.js', false);
+			});
+			return wxJssdkLoadPromise;
+		}
+
+		function fetchWxConfig() {
+			var pageUrl = getWxSignPageUrl();
+			var apiUrl = WX_CONFIG_API + '?url=' + encodeURIComponent(pageUrl);
+			return fetch(apiUrl, {
+				method: 'GET',
+				mode: 'cors',
+				credentials: 'omit',
+				headers: { Accept: 'application/json' }
+			}).then(function (res) {
+				if (!res.ok) throw new Error('HTTP ' + res.status);
+				return res.json();
+			});
+		}
+
+		function normalizeWxConfigPayload(res) {
+			var d = res;
+			if (!res || typeof res !== 'object') return null;
+			if (res.data != null && typeof res.data === 'object') d = res.data;
+			else if (res.result != null && typeof res.result === 'object') d = res.result;
+			var appId = d.appId || d.appid || d.publicId || '';
+			var timestamp = d.timestamp != null ? d.timestamp : d.timeStamp;
+			var nonceStr = d.nonceStr || d.noncestr || '';
+			var signature = d.signature || '';
+			var launchAppId =
+				d.launchAppId ||
+				d.mobileAppId ||
+				d.openAppId ||
+				d.jumpAppId ||
+				d.appAppId ||
+				d.wxAppId ||
+				WX_LAUNCH_APP_ID ||
+				'';
+			if (!appId || !signature || timestamp == null || !nonceStr) return null;
+			return {
+				appId: String(appId),
+				timestamp: Number(timestamp) || 0,
+				nonceStr: String(nonceStr),
+				signature: String(signature),
+				launchAppId: String(launchAppId || '')
+			};
+		}
+
+		function applyWxConfig(cfg) {
+			return new Promise(function (resolve, reject) {
+				if (!cfg || !cfg.appId) {
+					reject(new Error('微信签名参数不完整'));
+					return;
+				}
+				wx.config({
+					debug: true,
+					appId: cfg.appId,
+					timestamp: cfg.timestamp,
+					nonceStr: cfg.nonceStr,
+					signature: cfg.signature,
+					jsApiList: [],
+					openTagList: ['wx-open-launch-app']
+				});
+				wx.ready(function () {
+					wxConfigReady = true;
+					if (cfg.launchAppId) wxLaunchAppIdCache = cfg.launchAppId;
+					resolve(cfg);
+				});
+				wx.error(function (err) {
+					reject(err || new Error('wx.config 失败'));
+				});
+			});
+		}
+
+		function buildAppExtInfo() {
+			var path = getAppUniPathForBusinessSection().replace(/^\//, '');
+			var qs = buildAppOpenQueryString();
+			return path + (qs || '');
+		}
+
+		function escapeHtmlAttr(s) {
+			return String(s)
+				.replace(/&/g, '&amp;')
+				.replace(/"/g, '&quot;')
+				.replace(/</g, '&lt;');
+		}
+
+		function mountWxOpenLaunchApp(launchAppId) {
+			if (!launchAppId) return false;
+			var host = document.getElementById('openAppWxLaunchHost');
+			var btn = document.getElementById('openApp');
+			if (!host || !btn) return false;
+			if (wxLaunchMounted && host.querySelector('wx-open-launch-app')) return true;
+
+			var extinfo = escapeHtmlAttr(buildAppExtInfo());
+			var appid = escapeHtmlAttr(launchAppId);
+			host.innerHTML =
+				'<wx-open-launch-app id="wxOpenLaunchApp" appid="' +
+				appid +
+				'" extinfo="' +
+				extinfo +
+				'">' +
+				'<script type="text/wxtag-template">' +
+				'<style>.wx-fab-inner{display:flex;align-items:center;justify-content:center;width:100%;height:48px;border:0;padding:0;margin:0;background:transparent;}</style>' +
+				'<button type="button" class="wx-fab-inner" aria-label="APP内打开">' +
+				'<img src="images/uBtn.png" alt="" style="height:48px;width:auto;max-width:100%;display:block;">' +
+				'</button>' +
+				'<\/script></wx-open-launch-app>';
+
+			var tag = host.querySelector('wx-open-launch-app');
+			if (tag) {
+				tag.addEventListener('launch', function () {
+					console.log('[wx-open-launch-app] launch');
+				});
+				tag.addEventListener('error', function (e) {
+					console.warn('[wx-open-launch-app]', e && e.detail);
+					tryLaunchAppBySchemeOnly();
+				});
+			}
+
+			host.classList.add('fab-wx-launch-host--active');
+			host.setAttribute('aria-hidden', 'false');
+			btn.setAttribute('aria-hidden', 'true');
+			btn.style.visibility = 'hidden';
+			btn.style.pointerEvents = 'none';
+			wxLaunchMounted = true;
+			return true;
+		}
+
+		function clickWxOpenLaunchInner() {
+			var host = document.getElementById('openAppWxLaunchHost');
+			if (!host) return false;
+			var inner = host.querySelector('.wx-fab-inner');
+			if (inner && typeof inner.click === 'function') {
+				inner.click();
+				return true;
+			}
+			return false;
+		}
+
+		/** 公众号内:先 GET getWxConfig,再 wx.config + 开放标签唤起 App */
+		function prepareWeChatAppLaunch(forceRefresh) {
+			if (!forceRefresh && wxPreparePromise) return wxPreparePromise;
+			wxPreparePromise = loadWxJssdk()
+				.then(function () {
+					return fetchWxConfig();
+				})
+				.then(function (res) {
+					var cfg = normalizeWxConfigPayload(res);
+					if (!cfg) throw new Error('getWxConfig 返回无效');
+					if (wxConfigReady && cfg.launchAppId && wxLaunchAppIdCache === cfg.launchAppId) {
+						return cfg;
+					}
+					wxConfigReady = false;
+					return applyWxConfig(cfg);
+				})
+				.then(function (cfg) {
+					var launchId = (cfg && cfg.launchAppId) || wxLaunchAppIdCache;
+					if (!launchId) throw new Error('缺少移动应用 AppId');
+					mountWxOpenLaunchApp(launchId);
+					return cfg;
+				});
+			return wxPreparePromise;
+		}
+
+		function openAppViaWeChat() {
+			prepareWeChatAppLaunch(false)
+				.then(function () {
+					if (!clickWxOpenLaunchInner()) {
+						tryLaunchAppBySchemeOnly();
+					}
+				})
+				.catch(function (e) {
+					console.warn('[微信唤起App]', e);
+					tryLaunchAppBySchemeOnly();
+				});
+		}
+
 		/** 部分 WebView 对 location.href 拦截,用 a 标签点击略稳 */
 		function launchAppDeepLink(deepLink) {
 			
@@ -1096,11 +1333,51 @@
 			} catch (e2) {}
 		}
 
+		/** 仅 scheme 唤起(无微信提示),供开放标签失败时回退 */
+		function tryLaunchAppBySchemeOnly() {
+			var deepLink = buildAppDeepLink();
+			var t0 = Date.now();
+			var done = false;
+			function finish() {
+				if (done) return;
+				done = true;
+				document.removeEventListener('visibilitychange', onVis);
+				window.removeEventListener('pagehide', onHide);
+			}
+			function onVis() {
+				if (document.visibilityState === 'hidden') finish();
+			}
+			function onHide() {
+				finish();
+			}
+			document.addEventListener('visibilitychange', onVis);
+			window.addEventListener('pagehide', onHide);
+			try {
+				launchAppDeepLink(deepLink);
+			} catch (e3) {
+				finish();
+				showDownloadTip();
+				return;
+			}
+			window.setTimeout(function () {
+				if (done) return;
+				if (document.visibilityState === 'visible' && Date.now() - t0 < 3500) {
+					showDownloadTip();
+				}
+				finish();
+			}, 2600);
+		}
+
 		/**
 		 * 有 plus:检测 App 是否安装后 openURL;深链形如 shopro://{APP_UNI_STORE_PATH}?…
-		 * 系统浏览器:唤起 scheme;微信内常拦截 scheme,需「在浏览器打开」。
+		 * 系统浏览器:唤起 scheme;微信内走 getWxConfig + wx-open-launch-app
 		 */
 		function tryOpenHBuilderApp() {
+			if (isWeChatInAppBrowser()) {
+				openAppViaWeChat();
+				return;
+			}
+
 			var deepLink = buildAppDeepLink();
 
 			if (typeof plus !== 'undefined' && plus.runtime) {
@@ -1145,10 +1422,6 @@
 			document.addEventListener('visibilitychange', onVis);
 			window.addEventListener('pagehide', onHide);
 
-			if (isWeChatInAppBrowser()) {
-				window.alert('若点击后无法打开 App:请先点右上角「···」,选择「在浏览器中打开」,再点「APP内打开」。');
-			}
-
 			try {
 				launchAppDeepLink(deepLink);
 			} catch (e3) {
@@ -2312,12 +2585,25 @@
 			run();
 			bindMarketingMore();
 			bindStaffSection();
+			if (isWeChatInAppBrowser()) {
+				prepareWeChatAppLaunch(false).catch(function (e) {
+					console.warn('[微信预加载 getWxConfig]', e);
+				});
+			}
 			var openBtn = document.getElementById('openApp');
 			if (openBtn) {
 				openBtn.addEventListener('click', function () {
 					tryOpenHBuilderApp();
 				});
 			}
+			var wxHost = document.getElementById('openAppWxLaunchHost');
+			if (wxHost) {
+				wxHost.addEventListener('click', function () {
+					if (!wxConfigReady) {
+						openAppViaWeChat();
+					}
+				});
+			}
 		}
 
 		if (document.readyState === 'complete') {