zhuli 3 недель назад
Родитель
Сommit
8c6903805e
1 измененных файлов с 268 добавлено и 19 удалено
  1. 268 19
      HBuilderProjects/shareIndex.html

+ 268 - 19
HBuilderProjects/shareIndex.html

@@ -469,6 +469,21 @@
 			pointer-events: auto;
 		}
 
+		#fabWrapWx {
+			display: none;
+		}
+
+		#fabWrapWx.is-active {
+			display: block;
+		}
+
+		#launch-btn {
+			display: block;
+			width: 100%;
+			max-width: 320px;
+			margin: 0 auto;
+		}
+
 		.fab {
 			display: flex;
 			align-items: center;
@@ -967,21 +982,43 @@
 			<div id="staffGroupsMount"></div>
 		</section>
 	</div>
-	<wx-open-launch-app
-	id="launch-btn"
-	appid="wxf5f1efe3a9f5012e"
-	extinfo="https://test.ailien.shop/h5/HBuilderProjects/shareIndex.html"
-	>
-		<div id="shareBelowContentEl">
-			<div class="fab-wrap">
-				<button type="button" class="fab" id="openApp">
-					<img src="images/uBtn.png" alt="APP内打开" decoding="async">
+	<!-- 微信内:wx-open-launch-app(需 JSSDK 签名 + 公众号关联 App);非微信见 #fabWrapFallback -->
+	<div id="fabWrapWx" class="fab-wrap" aria-hidden="true">
+		<wx-open-launch-app id="launch-btn" appid="wxf5f1efe3a9f5012e">
+			<script type="text/wxtag-template">
+				<style>
+					.wx-open-app-btn {
+						display: block;
+						width: 100%;
+						max-width: 320px;
+						height: 48px;
+						margin: 0 auto;
+						padding: 0;
+						border: none;
+						background: transparent;
+						cursor: pointer;
+					}
+					.wx-open-app-btn img {
+						display: block;
+						width: 100%;
+						height: 48px;
+						object-fit: contain;
+					}
+				</style>
+				<button class="wx-open-app-btn" aria-label="APP内打开">
+					<img src="https://test.ailien.shop/h5/HBuilderProjects/images/uBtn.png" alt="APP内打开" />
 				</button>
-				<div class="home-indicator" aria-hidden="true"></div>
-			</div>
-		</div>
-	</wx-open-launch-app>
-	<script src="http://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
+			</script>
+		</wx-open-launch-app>
+		<div class="home-indicator" aria-hidden="true"></div>
+	</div>
+	<div id="fabWrapFallback" class="fab-wrap">
+		<button type="button" class="fab" id="openApp">
+			<img src="images/uBtn.png" alt="APP内打开" decoding="async">
+		</button>
+		<div class="home-indicator" aria-hidden="true"></div>
+	</div>
+	<script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
 	<script>
 	(function () {
 		'use strict';
@@ -1002,6 +1039,20 @@
 		 */
 		var API_BASE = 'https://test.ailien.shop/alienStore';
 
+		/** 微信开放标签:移动应用 AppID(与 wx-open-launch-app appid 一致) */
+		var WECHAT_OPEN_APP_ID = 'wxf5f1efe3a9f5012e';
+		/**
+		 * JSSDK 签名接口路径(相对 API_BASE);后端需返回 appId、timestamp、nonceStr、signature。
+		 * 可用 ?wxSignPath=/your/path 或完整 ?wxSignUrl=https://... 覆盖。
+		 */
+		var WECHAT_JSSDK_SIGN_PATHS = [
+			'/wechat/mp/jsapi-signature',
+			'/wechat/jsapi/getSign',
+			'/wx/jsapi/signature'
+		];
+		var H5_PAGE_BASE_FALLBACK = 'https://test.ailien.shop/h5/HBuilderProjects/';
+		var weChatOpenTagReady = false;
+
 		/**
 		 * 关店(businessStatus=99)更多推荐:POST …/ai/multimodal-services/api/v1/search/global/store-recommend
 		 * 与 shareCheckInUndefined.html 一致;请求体 page、pageSize、storeId、userCity、userLat、userLng 可由 URL 覆盖。
@@ -1076,6 +1127,203 @@
 			return /MicroMessenger/i.test(navigator.userAgent || '');
 		}
 
+		function getWeChatJssdkSignRequestUrl() {
+			var custom = String(q('wxSignPath') || q('wxSignUrl') || '').trim();
+			if (!custom) return '';
+			if (/^https?:\/\//i.test(custom)) return custom;
+			return API_BASE.replace(/\/$/, '') + (custom.charAt(0) === '/' ? custom : '/' + custom);
+		}
+
+		function getPageUrlForWxSign() {
+			var u = String(location.href || '').split('#')[0];
+			if (!u || u.indexOf('file:') === 0) {
+				return H5_PAGE_BASE_FALLBACK + 'shareIndex.html' + (location.search || '');
+			}
+			return u;
+		}
+
+		function buildWeChatLaunchExtinfo() {
+			var deep = buildAppDeepLink();
+			if (deep && deep.length <= 1024) return deep;
+			var page = getPageUrlForWxSign();
+			return page.length <= 1024 ? page : page.slice(0, 1024);
+		}
+
+		function absH5AssetUrl(rel) {
+			rel = String(rel || '').replace(/^\//, '');
+			var origin = location.origin;
+			if (origin && origin !== 'null' && /^https?:/i.test(origin)) {
+				var dir = location.pathname.replace(/[^/]*$/, '');
+				return origin + dir + rel;
+			}
+			return H5_PAGE_BASE_FALLBACK + rel;
+		}
+
+		function normalizeWxJssdkSignPayload(res) {
+			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;
+			var appId = d.appId || d.appid || WECHAT_OPEN_APP_ID;
+			var timestamp = d.timestamp != null ? d.timestamp : d.timeStamp;
+			var nonceStr = d.nonceStr || d.noncestr || d.nonce;
+			var signature = d.signature || d.sign;
+			if (!appId || timestamp == null || !nonceStr || !signature) return null;
+			return {
+				appId: String(appId),
+				timestamp: Number(timestamp),
+				nonceStr: String(nonceStr),
+				signature: String(signature)
+			};
+		}
+
+		function fetchWeChatJssdkSign() {
+			var signPageUrl = getPageUrlForWxSign();
+			var urls = [];
+			var custom = getWeChatJssdkSignRequestUrl();
+			if (custom) {
+				var sep0 = custom.indexOf('?') >= 0 ? '&' : '?';
+				urls.push(custom + sep0 + 'url=' + encodeURIComponent(signPageUrl));
+			} else {
+				var base = API_BASE.replace(/\/$/, '');
+				for (var pi = 0; pi < WECHAT_JSSDK_SIGN_PATHS.length; pi++) {
+					urls.push(
+						base +
+							WECHAT_JSSDK_SIGN_PATHS[pi] +
+							'?url=' +
+							encodeURIComponent(signPageUrl)
+					);
+				}
+			}
+			function tryUrl(idx) {
+				if (idx >= urls.length) return Promise.resolve(null);
+				return fetch(urls[idx], {
+					method: 'GET',
+					mode: 'cors',
+					credentials: 'omit',
+					headers: { Accept: 'application/json' }
+				})
+					.then(function (r) {
+						if (!r.ok) throw new Error('HTTP ' + r.status);
+						return r.json();
+					})
+					.then(function (res) {
+						if (res && res.code != null) {
+							var c = Number(res.code);
+							if (c !== 200 && c !== 0 && res.success !== true) {
+								throw new Error(res.msg || 'sign rejected');
+							}
+						}
+						var pack = normalizeWxJssdkSignPayload(res);
+						if (!pack) throw new Error('empty sign payload');
+						return pack;
+					})
+					.catch(function () {
+						return tryUrl(idx + 1);
+					});
+			}
+			return tryUrl(0);
+		}
+
+		function setFabLaunchMode(mode) {
+			var wxWrap = document.getElementById('fabWrapWx');
+			var fbWrap = document.getElementById('fabWrapFallback');
+			if (mode === 'wx') {
+				if (wxWrap) {
+					wxWrap.classList.add('is-active');
+					wxWrap.setAttribute('aria-hidden', 'false');
+				}
+				if (fbWrap) {
+					fbWrap.style.display = 'none';
+					fbWrap.setAttribute('aria-hidden', 'true');
+				}
+			} else {
+				if (wxWrap) {
+					wxWrap.classList.remove('is-active');
+					wxWrap.setAttribute('aria-hidden', 'true');
+				}
+				if (fbWrap) {
+					fbWrap.style.display = '';
+					fbWrap.setAttribute('aria-hidden', 'false');
+				}
+			}
+		}
+
+		function bindWeChatLaunchTagEvents() {
+			var tag = document.getElementById('launch-btn');
+			if (!tag) return;
+			try {
+				tag.setAttribute('extinfo', buildWeChatLaunchExtinfo());
+			} catch (eExt) {}
+			tag.addEventListener('launch', function () {
+				weChatOpenTagReady = true;
+			});
+			tag.addEventListener('error', function (e) {
+				console.warn('[wx-open-launch-app]', e && e.detail);
+				weChatOpenTagReady = false;
+				setFabLaunchMode('scheme');
+			});
+		}
+
+		function initWeChatOpenLaunchApp() {
+			if (!isWeChatInAppBrowser()) {
+				setFabLaunchMode('scheme');
+				return Promise.resolve(false);
+			}
+			/** 签名完成前不展示 scheme 按钮,避免误点触发无效唤起 */
+			var fbWrap0 = document.getElementById('fabWrapFallback');
+			if (fbWrap0) {
+				fbWrap0.style.display = 'none';
+				fbWrap0.setAttribute('aria-hidden', 'true');
+			}
+			if (typeof wx === 'undefined') {
+				console.warn('[wx] jweixin not loaded');
+				setFabLaunchMode('scheme');
+				return Promise.resolve(false);
+			}
+			bindWeChatLaunchTagEvents();
+			return fetchWeChatJssdkSign()
+				.then(function (sign) {
+					if (!sign) {
+						console.warn('[wx] JSSDK sign unavailable — check WECHAT_JSSDK_SIGN_PATHS or ?wxSignPath=');
+						setFabLaunchMode('scheme');
+						return false;
+					}
+					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: [],
+							openTagList: ['wx-open-launch-app']
+						});
+						wx.ready(function () {
+							weChatOpenTagReady = 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);
+							weChatOpenTagReady = false;
+							setFabLaunchMode('scheme');
+							resolve(false);
+						});
+					});
+				})
+				.catch(function (e) {
+					console.warn('[wx] init failed', e);
+					setFabLaunchMode('scheme');
+					return false;
+				});
+		}
+
 		/** 部分 WebView 对 location.href 拦截,用 a 标签点击略稳 */
 		function launchAppDeepLink(deepLink) {
 			
@@ -1094,9 +1342,13 @@
 
 		/**
 		 * 有 plus:检测 App 是否安装后 openURL;深链形如 shopro://{APP_UNI_STORE_PATH}?…
-		 * 系统浏览器:唤起 scheme;微信内常拦截 scheme,需「在浏览器打开」
+		 * 系统浏览器:scheme 唤起;微信内优先 wx-open-launch-app,失败再 scheme(不再弹「在浏览器中打开」)
 		 */
 		function tryOpenHBuilderApp() {
+			if (isWeChatInAppBrowser() && weChatOpenTagReady) {
+				return;
+			}
+
 			var deepLink = buildAppDeepLink();
 
 			if (typeof plus !== 'undefined' && plus.runtime) {
@@ -1141,10 +1393,6 @@
 			document.addEventListener('visibilitychange', onVis);
 			window.addEventListener('pagehide', onHide);
 
-			if (isWeChatInAppBrowser()) {
-				window.alert('若点击后无法打开 App:请先点右上角「···」,选择「在浏览器中打开」,再点「APP内打开」。');
-			}
-
 			try {
 				launchAppDeepLink(deepLink);
 			} catch (e3) {
@@ -2322,6 +2570,7 @@
 					tryOpenHBuilderApp();
 				});
 			}
+			initWeChatOpenLaunchApp();
 		}
 
 		if (document.readyState === 'complete') {