|
@@ -22,10 +22,6 @@
|
|
|
padding: 0;
|
|
padding: 0;
|
|
|
box-sizing: border-box;
|
|
box-sizing: border-box;
|
|
|
}
|
|
}
|
|
|
- #staffGroupsMount{
|
|
|
|
|
- height: 202px;
|
|
|
|
|
- overflow: hidden;
|
|
|
|
|
- }
|
|
|
|
|
|
|
|
|
|
html {
|
|
html {
|
|
|
font-size: 16px;
|
|
font-size: 16px;
|
|
@@ -469,10 +465,77 @@
|
|
|
pointer-events: none;
|
|
pointer-events: none;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- .fab-wrap .fab {
|
|
|
|
|
|
|
+ #fabDock,
|
|
|
|
|
+ .fab-dock__slot,
|
|
|
|
|
+ #fabDock .fab,
|
|
|
|
|
+ .fab-wx-hit {
|
|
|
pointer-events: auto;
|
|
pointer-events: auto;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ #fabDock {
|
|
|
|
|
+ display: block;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .fab-dock__slot {
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ max-width: 320px;
|
|
|
|
|
+ height: 48px;
|
|
|
|
|
+ margin: 0 auto;
|
|
|
|
|
+ -webkit-tap-highlight-color: transparent;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ #openApp {
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ z-index: 1;
|
|
|
|
|
+ touch-action: manipulation;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ #openApp img {
|
|
|
|
|
+ pointer-events: none;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .fab-wx-hit {
|
|
|
|
|
+ display: none;
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ left: 0;
|
|
|
|
|
+ right: 0;
|
|
|
|
|
+ top: 0;
|
|
|
|
|
+ height: 48px;
|
|
|
|
|
+ z-index: 3;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* 底层 #openApp 始终显示;上层透明开放标签接收点击(标签未渲染时仍能看见并点底层按钮) */
|
|
|
|
|
+ #fabDock.wx-open-enabled .fab-wx-hit {
|
|
|
|
|
+ display: block;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ #launch-btn {
|
|
|
|
|
+ display: block;
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ height: 48px;
|
|
|
|
|
+ min-height: 48px;
|
|
|
|
|
+ line-height: 48px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ #openAppToast {
|
|
|
|
|
+ display: none;
|
|
|
|
|
+ position: fixed;
|
|
|
|
|
+ left: 16px;
|
|
|
|
|
+ right: 16px;
|
|
|
|
|
+ bottom: calc(72px + var(--safe-bottom));
|
|
|
|
|
+ z-index: 10001;
|
|
|
|
|
+ padding: 10px 14px;
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ line-height: 1.45;
|
|
|
|
|
+ color: #fff;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+ background: rgba(0, 0, 0, 0.78);
|
|
|
|
|
+ border-radius: 8px;
|
|
|
|
|
+ pointer-events: none;
|
|
|
|
|
+ word-break: break-all;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
.fab {
|
|
.fab {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
align-items: center;
|
|
align-items: center;
|
|
@@ -971,21 +1034,36 @@
|
|
|
<div id="staffGroupsMount"></div>
|
|
<div id="staffGroupsMount"></div>
|
|
|
</section>
|
|
</section>
|
|
|
</div>
|
|
</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">
|
|
|
|
|
- </button>
|
|
|
|
|
- <div class="home-indicator" aria-hidden="true"></div>
|
|
|
|
|
|
|
+ <pre id="wxDebugBar" style="display:none;position:fixed;left:8px;right:8px;top:8px;z-index:9999;max-height:40vh;overflow:auto;margin:0;padding:8px;font-size:11px;line-height:1.35;background:rgba(0,0,0,.82);color:#0f0;white-space:pre-wrap;word-break:break-all;border-radius:6px;pointer-events:none;"></pre>
|
|
|
|
|
+ <div id="openAppToast" role="status" aria-live="polite"></div>
|
|
|
|
|
+ <div id="fabDock" class="fab-wrap">
|
|
|
|
|
+ <div class="fab-dock__slot">
|
|
|
|
|
+ <button type="button" class="fab" id="openApp">
|
|
|
|
|
+ <img src="images/uBtn.png" alt="APP内打开" decoding="async">
|
|
|
|
|
+ </button>
|
|
|
|
|
+ <div id="fabWxHit" class="fab-wx-hit" aria-hidden="true">
|
|
|
|
|
+ <wx-open-launch-app id="launch-btn" appid="wx5598889b28511717">
|
|
|
|
|
+ <script type="text/wxtag-template">
|
|
|
|
|
+ <style>
|
|
|
|
|
+ .wx-open-app-btn {
|
|
|
|
|
+ display: block;
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ height: 48px;
|
|
|
|
|
+ margin: 0;
|
|
|
|
|
+ padding: 0;
|
|
|
|
|
+ border: none;
|
|
|
|
|
+ opacity: 0;
|
|
|
|
|
+ background: transparent;
|
|
|
|
|
+ }
|
|
|
|
|
+ </style>
|
|
|
|
|
+ <button class="wx-open-app-btn" aria-label="APP内打开">打开</button>
|
|
|
|
|
+ </script>
|
|
|
|
|
+ </wx-open-launch-app>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
- </wx-open-launch-app>
|
|
|
|
|
- <script src="http://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
|
|
|
|
|
|
|
+ <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>
|
|
<script>
|
|
|
(function () {
|
|
(function () {
|
|
|
'use strict';
|
|
'use strict';
|
|
@@ -1006,33 +1084,77 @@
|
|
|
*/
|
|
*/
|
|
|
var API_BASE = 'https://test.ailien.shop/alienStore';
|
|
var API_BASE = 'https://test.ailien.shop/alienStore';
|
|
|
|
|
|
|
|
|
|
+ /** wx-open-launch-app 的 appid(开放平台移动应用;若与公众号不同,只改这里和标签 appid) */
|
|
|
|
|
+ var WECHAT_OPEN_APP_ID = 'wx5598889b28511717';
|
|
|
|
|
+ /** 可选:强制 wx.config 使用的公众号 appId;默认用 getWxConfig 返回的 data.appId */
|
|
|
|
|
+ var WECHAT_MP_APP_ID = '';
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 进入页 GET {API_BASE}/wx/getWxConfig?url={当前页完整 URL,含 ? 参数,不含 #}
|
|
|
|
|
+ * 微信校验签名与 location.href.split('#')[0] 必须一致,不能只签 shareIndex.html 无参数
|
|
|
|
|
+ * 可用 ?wxSignPath= 或 ?wxSignUrl= 覆盖。
|
|
|
|
|
+ */
|
|
|
|
|
+ var WECHAT_GET_WX_CONFIG_PATH = '/wx/getWxConfig';
|
|
|
|
|
+ var H5_PAGE_BASE_FALLBACK = 'https://test.ailien.shop/h5/HBuilderProjects/';
|
|
|
|
|
+ /** 本地 file:// 预览时传给 getWxConfig 的默认 url(无 query) */
|
|
|
|
|
+ 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 = '';
|
|
|
|
|
+ var wxDebugLastStatus = '';
|
|
|
|
|
+
|
|
|
|
|
+ function isWxDebugOn() {
|
|
|
|
|
+ return String(q('wxDebug') || '') === '1';
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function stringifyWxDebugPayload(obj) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ return JSON.stringify(obj, null, 2);
|
|
|
|
|
+ } catch (eJson) {
|
|
|
|
|
+ return String(obj && obj.errMsg ? obj.errMsg : obj);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /** ?wxDebug=1 时弹窗 + 页内条展示完整 errMsg(微信 debug 弹窗有时信息不全) */
|
|
|
|
|
+ function reportWxDebug(stage, payload, extraLines) {
|
|
|
|
|
+ var lines = ['【' + stage + '】'];
|
|
|
|
|
+ if (extraLines && extraLines.length) {
|
|
|
|
|
+ for (var i = 0; i < extraLines.length; i++) lines.push(extraLines[i]);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (payload != null) {
|
|
|
|
|
+ lines.push(stringifyWxDebugPayload(payload));
|
|
|
|
|
+ }
|
|
|
|
|
+ var text = lines.join('\n');
|
|
|
|
|
+ wxDebugLastStatus = text;
|
|
|
|
|
+ console.log('[wxDebug]', text);
|
|
|
|
|
+ if (!isWxDebugOn()) return;
|
|
|
|
|
+ var bar = document.getElementById('wxDebugBar');
|
|
|
|
|
+ if (bar) {
|
|
|
|
|
+ bar.style.display = 'block';
|
|
|
|
|
+ bar.textContent = text;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (isWeChatInAppBrowser()) {
|
|
|
|
|
+ window.alert(text.slice(0, 900));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* 关店(businessStatus=99)更多推荐:POST …/ai/multimodal-services/api/v1/search/global/store-recommend
|
|
* 关店(businessStatus=99)更多推荐:POST …/ai/multimodal-services/api/v1/search/global/store-recommend
|
|
|
- * 与 shareCheckInUndefined.html 一致;请求体 userCity/userLat/userLng 优先取 URL:userCity、lat、lon(兼认 userLat/userLng、weidu/jingdu 等)。
|
|
|
|
|
|
|
+ * 与 shareCheckInUndefined.html 一致;请求体 page、pageSize、storeId、userCity、userLat、userLng 可由 URL 覆盖。
|
|
|
*/
|
|
*/
|
|
|
var API_LIFE_AI_BASE = 'http://183.252.196.135:9100';
|
|
var API_LIFE_AI_BASE = 'http://183.252.196.135:9100';
|
|
|
var STORE_GLOBAL_RECOMMEND_PATH =
|
|
var STORE_GLOBAL_RECOMMEND_PATH =
|
|
|
'/ai/multimodal-services/api/v1/search/global/store-recommend';
|
|
'/ai/multimodal-services/api/v1/search/global/store-recommend';
|
|
|
- /** 关店推荐用户位置:优先 URL 的 lat、lon、userCity;缺省再用大连默认值 */
|
|
|
|
|
- function getClosedRecommendUserLocation() {
|
|
|
|
|
- var latRaw = (q('lat') || q('userLat') || q('latitude') || q('weidu') || '').trim();
|
|
|
|
|
- var lonRaw = (q('lon') || q('userLng') || q('longitude') || q('lng') || q('jingdu') || '').trim();
|
|
|
|
|
- var cityRaw = (q('userCity') || q('city') || '').trim();
|
|
|
|
|
- return {
|
|
|
|
|
- userLat:
|
|
|
|
|
- latRaw !== '' && !isNaN(Number(latRaw)) ? Number(latRaw) : 38.925747,
|
|
|
|
|
- userLng:
|
|
|
|
|
- lonRaw !== '' && !isNaN(Number(lonRaw)) ? Number(lonRaw) : 121.662531,
|
|
|
|
|
- userCity: cityRaw !== '' ? cityRaw : '大连市'
|
|
|
|
|
- };
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ var DEFAULT_REC_USER_LAT = 38.925747;
|
|
|
|
|
+ var DEFAULT_REC_USER_LNG = 121.662531;
|
|
|
|
|
+ var DEFAULT_REC_USER_CITY = '大连市';
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 与 group_user 一致:
|
|
* 与 group_user 一致:
|
|
|
* - manifest:app-plus.distribute.android.schemes、ios.urltypes → shopro(即 shopro://)
|
|
* - manifest:app-plus.distribute.android.schemes、ios.urltypes → shopro(即 shopro://)
|
|
|
* - Android 包名:manifest 未写 packagename 时与 App.vue 升级配置 androidPackageName 一致(云打包以 HBuilderX 为准)
|
|
* - Android 包名:manifest 未写 packagename 时与 App.vue 升级配置 androidPackageName 一致(云打包以 HBuilderX 为准)
|
|
|
*/
|
|
*/
|
|
|
- var APP_ANDROID_PACKAGE = 'com.alien.Udianzaizhe';
|
|
|
|
|
|
|
+ var APP_ANDROID_PACKAGE = 'com.alien.Udianzaina';
|
|
|
var APP_IOS_URL_SCHEME = 'shopro://';
|
|
var APP_IOS_URL_SCHEME = 'shopro://';
|
|
|
/** businessSection 未传或其它值时的默认落地页(与 1 相同) */
|
|
/** businessSection 未传或其它值时的默认落地页(与 1 相同) */
|
|
|
var APP_UNI_STORE_PATH = 'pages/ieisureEntertainment/eightTypeList/delicacyDetails';
|
|
var APP_UNI_STORE_PATH = 'pages/ieisureEntertainment/eightTypeList/delicacyDetails';
|
|
@@ -1046,6 +1168,47 @@
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ function showFabToast(msg, ms) {
|
|
|
|
|
+ var tip = String(msg || '').trim();
|
|
|
|
|
+ if (!tip) return;
|
|
|
|
|
+ var el = document.getElementById('openAppToast');
|
|
|
|
|
+ if (el) {
|
|
|
|
|
+ el.textContent = tip;
|
|
|
|
|
+ el.style.display = 'block';
|
|
|
|
|
+ if (showFabToast._timer) clearTimeout(showFabToast._timer);
|
|
|
|
|
+ showFabToast._timer = setTimeout(function () {
|
|
|
|
|
+ el.style.display = 'none';
|
|
|
|
|
+ }, ms || 3200);
|
|
|
|
|
+ }
|
|
|
|
|
+ console.log('[openApp]', tip);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function showAppOpenFailTip(msg) {
|
|
|
|
|
+ var tip =
|
|
|
|
|
+ msg ||
|
|
|
|
|
+ '未能打开 App,请确认已安装最新版「U店在哪」;微信内请使用页面底部按钮打开。';
|
|
|
|
|
+ showFabToast(tip, 4000);
|
|
|
|
|
+ if (typeof uni !== 'undefined' && typeof uni.showToast === 'function') {
|
|
|
|
|
+ uni.showToast({ title: tip, icon: 'none', duration: 2800 });
|
|
|
|
|
+ } else if (isWeChatInAppBrowser() && isWxDebugOn()) {
|
|
|
|
|
+ window.alert(tip);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /** 与公众平台「JS接口安全域名」一致;分享页实际打开的 host 必须在此列表中 */
|
|
|
|
|
+ var WECHAT_JS_SAFE_HOSTS = ['uat.ailien.shop', 'test.ailien.shop'];
|
|
|
|
|
+
|
|
|
|
|
+ function checkWxJsSafeDomainHint() {
|
|
|
|
|
+ var host = String(location.hostname || '').toLowerCase();
|
|
|
|
|
+ if (!host || host === 'localhost' || host === '127.0.0.1') return '';
|
|
|
|
|
+ if (WECHAT_JS_SAFE_HOSTS.indexOf(host) >= 0) return '';
|
|
|
|
|
+ return (
|
|
|
|
|
+ '当前页面域名为「' +
|
|
|
|
|
+ host +
|
|
|
|
|
+ '」,未在 WECHAT_JS_SAFE_HOSTS 中;请在公众号后台「JS接口安全域名」添加该域名(不含 https:// 与路径)。'
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/** 详情页 onLoad 使用 storeId;分享链常为 id,补全 storeId 避免 App 内参数为空 */
|
|
/** 详情页 onLoad 使用 storeId;分享链常为 id,补全 storeId 避免 App 内参数为空 */
|
|
|
function buildAppOpenQueryString() {
|
|
function buildAppOpenQueryString() {
|
|
|
var raw = (location.search && location.search.charAt(0) === '?') ? location.search.slice(1) : (location.search || '');
|
|
var raw = (location.search && location.search.charAt(0) === '?') ? location.search.slice(1) : (location.search || '');
|
|
@@ -1090,27 +1253,537 @@
|
|
|
return /MicroMessenger/i.test(navigator.userAgent || '');
|
|
return /MicroMessenger/i.test(navigator.userAgent || '');
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /** 部分 WebView 对 location.href 拦截,用 a 标签点击略稳 */
|
|
|
|
|
- function launchAppDeepLink(deepLink) {
|
|
|
|
|
-
|
|
|
|
|
|
|
+ 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);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /** 微信 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();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 微信校验签名用的是地址栏当前 url(不含#)。
|
|
|
|
|
+ * 若后端只签了无参地址,须先把地址栏 sync 成与签名 url 一致再 wx.config。
|
|
|
|
|
+ */
|
|
|
|
|
+ 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);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /** 唤起 App 的 extinfo:保留完整分享链 */
|
|
|
|
|
+ function getPageUrlForWxSign() {
|
|
|
|
|
+ return getWxSignUrlFull();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function buildWxGetConfigRequestUrl(signPageUrl) {
|
|
|
|
|
+ var base = API_BASE.replace(/\/$/, '');
|
|
|
|
|
+ return (
|
|
|
|
|
+ base +
|
|
|
|
|
+ WECHAT_GET_WX_CONFIG_PATH +
|
|
|
|
|
+ '?url=' +
|
|
|
|
|
+ encodeURIComponent(signPageUrl || getWxConfigSignUrl())
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 微信开放标签 extinfo:优先传当前 H5 分享链(与 App 解析分享 URL 一致);
|
|
|
|
|
+ * 超长时再传 path+query JSON,最后才用 shopro://。
|
|
|
|
|
+ */
|
|
|
|
|
+ function buildWeChatLaunchExtinfo() {
|
|
|
|
|
+ var pageUrl = getPageUrlForWxSign();
|
|
|
|
|
+ if (pageUrl && pageUrl.length <= 1024) return pageUrl;
|
|
|
|
|
+ var path = getAppUniPathForBusinessSection().replace(/^\//, '');
|
|
|
|
|
+ var qs = buildAppOpenQueryString().replace(/^\?/, '');
|
|
|
try {
|
|
try {
|
|
|
- var a = document.createElement('a');
|
|
|
|
|
- a.href = deepLink;
|
|
|
|
|
- a.setAttribute('target', '_self');
|
|
|
|
|
- document.body.appendChild(a);
|
|
|
|
|
- a.click();
|
|
|
|
|
- document.body.removeChild(a);
|
|
|
|
|
- } catch (e1) {}
|
|
|
|
|
|
|
+ var pack = JSON.stringify({
|
|
|
|
|
+ path: path,
|
|
|
|
|
+ query: qs,
|
|
|
|
|
+ h5: pageUrl
|
|
|
|
|
+ });
|
|
|
|
|
+ if (pack.length <= 1024) return pack;
|
|
|
|
|
+ } catch (eJson) {}
|
|
|
|
|
+ var deep = buildAppDeepLink();
|
|
|
|
|
+ if (deep && deep.length <= 1024) return deep;
|
|
|
|
|
+ return pageUrl ? pageUrl.slice(0, 1024) : deep.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 resolveWxConfigAppIdFromSignData(d) {
|
|
|
|
|
+ if (!d || typeof d !== 'object') return '';
|
|
|
|
|
+ var mp =
|
|
|
|
|
+ d.mpAppId ||
|
|
|
|
|
+ d.mpAppid ||
|
|
|
|
|
+ d.officialAppId ||
|
|
|
|
|
+ d.officialAccountAppId ||
|
|
|
|
|
+ d.gzhAppId ||
|
|
|
|
|
+ d.serviceAppId;
|
|
|
|
|
+ if (mp != null && String(mp).trim() !== '') return String(mp).trim();
|
|
|
|
|
+ var fromQuery = String(q('wxMpAppId') || WECHAT_MP_APP_ID || '').trim();
|
|
|
|
|
+ if (fromQuery) return fromQuery;
|
|
|
|
|
+ var raw = d.appId || d.appid || d.wxAppId;
|
|
|
|
|
+ if (raw == null || String(raw).trim() === '') return '';
|
|
|
|
|
+ return String(raw).trim();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ 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;
|
|
|
|
|
+ var appId = resolveWxConfigAppIdFromSignData(d);
|
|
|
|
|
+ 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;
|
|
|
|
|
+ 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),
|
|
|
|
|
+ signUrl: signedUrl
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function getWxErrMsg(err) {
|
|
|
|
|
+ if (!err) return '';
|
|
|
|
|
+ if (err.errMsg) return String(err.errMsg);
|
|
|
|
|
+ if (typeof err === 'string') return err;
|
|
|
|
|
+ try {
|
|
|
|
|
+ return JSON.stringify(err);
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ return String(err);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function formatWxConfigErrorTip(err, signPageUrl) {
|
|
|
|
|
+ var errMsg = getWxErrMsg(err);
|
|
|
|
|
+ var tip = '微信 JSSDK 配置失败';
|
|
|
|
|
+ if (/require\s*subscribe/i.test(errMsg)) {
|
|
|
|
|
+ return (
|
|
|
|
|
+ tip +
|
|
|
|
|
+ ':' +
|
|
|
|
|
+ errMsg +
|
|
|
|
|
+ '。测试号须用当前微信号在 mp.weixin.qq.com/debug.cgi 关注后再打开本页。'
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+ if (/invalid signature/i.test(errMsg)) {
|
|
|
|
|
+ var signUrl = signPageUrl || getWxSignUrlFull();
|
|
|
|
|
+ var pageNow = String(location.href || '').split('#')[0];
|
|
|
|
|
+ return (
|
|
|
|
|
+ tip +
|
|
|
|
|
+ ':签名无效。请让后端用与地址栏完全相同的 url 参与签名(不含#)。当前地址栏:' +
|
|
|
|
|
+ pageNow +
|
|
|
|
|
+ ';本次请求签名 url:' +
|
|
|
|
|
+ signUrl
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+ if (/invalid appid/i.test(errMsg)) {
|
|
|
|
|
+ return tip + ':appId 无效,请使用已认证服务号的公众号 AppID。';
|
|
|
|
|
+ }
|
|
|
|
|
+ if (/invalid url/i.test(errMsg)) {
|
|
|
|
|
+ return tip + ':域名未在公众号 JS 接口安全域名中,或未在开放平台绑定域名与移动应用。';
|
|
|
|
|
+ }
|
|
|
|
|
+ if (errMsg) return tip + ':' + errMsg;
|
|
|
|
|
+ return tip + ',开放标签不可用。';
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function fetchWeChatJssdkSign(signPageUrl) {
|
|
|
|
|
+ signPageUrl = signPageUrl || getWxConfigSignUrl();
|
|
|
|
|
+ var urls = [];
|
|
|
|
|
+ var custom = getWeChatJssdkSignRequestUrl();
|
|
|
|
|
+ if (custom) {
|
|
|
|
|
+ var sep0 = custom.indexOf('?') >= 0 ? '&' : '?';
|
|
|
|
|
+ urls.push(custom + sep0 + 'url=' + encodeURIComponent(signPageUrl));
|
|
|
|
|
+ } else {
|
|
|
|
|
+ urls.push(buildWxGetConfigRequestUrl(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, signPageUrl);
|
|
|
|
|
+ if (!pack) throw new Error('empty sign payload');
|
|
|
|
|
+ return pack;
|
|
|
|
|
+ })
|
|
|
|
|
+ .catch(function () {
|
|
|
|
|
+ return tryUrl(idx + 1);
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ 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;
|
|
|
|
|
+ reportWxDebug('wx.ready / config:ok', { errMsg: 'config:ok' }, [
|
|
|
|
|
+ 'appId(config)=' + sign.appId,
|
|
|
|
|
+ 'openTag appid=' + WECHAT_OPEN_APP_ID,
|
|
|
|
|
+ 'signUrl=' + urlForWx,
|
|
|
|
|
+ 'page=' + location.href.split('#')[0],
|
|
|
|
|
+ '开放标签层=' + (isWeChatInAppBrowser() ? '已启用' : '非微信')
|
|
|
|
|
+ ]);
|
|
|
|
|
+ if (wxSignUrlFullBeforeStrip) {
|
|
|
|
|
+ var restore = wxSignUrlFullBeforeStrip + (location.hash || '');
|
|
|
|
|
+ history.replaceState(history.state, document.title, restore);
|
|
|
|
|
+ wxSignUrlFullBeforeStrip = '';
|
|
|
|
|
+ }
|
|
|
|
|
+ scheduleWxOpenTagRefresh();
|
|
|
|
|
+ resolve(true);
|
|
|
|
|
+ });
|
|
|
|
|
+ wx.error(function (err) {
|
|
|
|
|
+ var errMsg = getWxErrMsg(err);
|
|
|
|
|
+ console.warn('[wx.config]', err, 'signUrl=', urlForWx);
|
|
|
|
|
+ reportWxDebug('wx.error / config:fail', err, [
|
|
|
|
|
+ '完整 errMsg=' + errMsg,
|
|
|
|
|
+ 'appId(config)=' + sign.appId,
|
|
|
|
|
+ 'signUrl=' + urlForWx,
|
|
|
|
|
+ 'page=' + location.href.split('#')[0]
|
|
|
|
|
+ ]);
|
|
|
|
|
+ var baseUrl = getWxSignUrlBase();
|
|
|
|
|
+ var fullUrl = getWxSignUrlFull();
|
|
|
|
|
+ if (
|
|
|
|
|
+ !/invalid signature/i.test(errMsg) ||
|
|
|
|
|
+ wxConfigSignRetriedBaseUrl ||
|
|
|
|
|
+ baseUrl === fullUrl ||
|
|
|
|
|
+ urlForWx === baseUrl
|
|
|
|
|
+ ) {
|
|
|
|
|
+ weChatJssdkConfigured = false;
|
|
|
|
|
+ updateFabOpenLayer();
|
|
|
|
|
+ showAppOpenFailTip(formatWxConfigErrorTip(err, urlForWx));
|
|
|
|
|
+ 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;
|
|
|
|
|
+ updateFabOpenLayer();
|
|
|
|
|
+ showAppOpenFailTip(formatWxConfigErrorTip(err, urlForWx));
|
|
|
|
|
+ resolve(false);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ applyWxConfig(sign2, baseUrl).then(resolve);
|
|
|
|
|
+ })
|
|
|
|
|
+ .catch(function () {
|
|
|
|
|
+ weChatJssdkConfigured = false;
|
|
|
|
|
+ updateFabOpenLayer();
|
|
|
|
|
+ showAppOpenFailTip(formatWxConfigErrorTip(err, urlForWx));
|
|
|
|
|
+ resolve(false);
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function tryTriggerWxOpenLaunchApp() {
|
|
|
|
|
+ var tag = document.getElementById('launch-btn');
|
|
|
|
|
+ if (!tag) return false;
|
|
|
|
|
+ try {
|
|
|
|
|
+ if (tag.shadowRoot) {
|
|
|
|
|
+ var inner = tag.shadowRoot.querySelector('button');
|
|
|
|
|
+ if (inner && typeof inner.click === 'function') {
|
|
|
|
|
+ inner.click();
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (typeof tag.click === 'function') {
|
|
|
|
|
+ tag.click();
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (eWx) {
|
|
|
|
|
+ console.warn('[wx-open-launch-app] trigger', eWx);
|
|
|
|
|
+ }
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function refreshWxLaunchTagAttrs() {
|
|
|
|
|
+ var tag = document.getElementById('launch-btn');
|
|
|
|
|
+ if (!tag) return;
|
|
|
|
|
+ try {
|
|
|
|
|
+ if (WECHAT_OPEN_APP_ID) tag.setAttribute('appid', WECHAT_OPEN_APP_ID);
|
|
|
|
|
+ tag.setAttribute('extinfo', buildWeChatLaunchExtinfo());
|
|
|
|
|
+ } catch (eAttr) {
|
|
|
|
|
+ console.warn('[wx-open-launch-app] set attrs', eAttr);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /** wx.config 成功后即切换为开放标签按钮(不再依赖 offsetHeight 检测) */
|
|
|
|
|
+ function updateFabOpenLayer() {
|
|
|
|
|
+ var dock = document.getElementById('fabDock');
|
|
|
|
|
+ var hit = document.getElementById('fabWxHit');
|
|
|
|
|
+ var useWx = isWeChatInAppBrowser() && weChatJssdkConfigured;
|
|
|
|
|
+ if (dock) {
|
|
|
|
|
+ if (useWx) dock.classList.add('wx-open-enabled');
|
|
|
|
|
+ else dock.classList.remove('wx-open-enabled');
|
|
|
|
|
+ }
|
|
|
|
|
+ if (hit) hit.setAttribute('aria-hidden', useWx ? 'false' : 'true');
|
|
|
|
|
+ if (useWx) refreshWxLaunchTagAttrs();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function scheduleWxOpenTagRefresh() {
|
|
|
|
|
+ refreshWxLaunchTagAttrs();
|
|
|
|
|
+ updateFabOpenLayer();
|
|
|
|
|
+ var delays = [100, 400, 1000, 2000];
|
|
|
|
|
+ for (var i = 0; i < delays.length; i++) {
|
|
|
|
|
+ (function (ms) {
|
|
|
|
|
+ window.setTimeout(function () {
|
|
|
|
|
+ if (!weChatJssdkConfigured) return;
|
|
|
|
|
+ refreshWxLaunchTagAttrs();
|
|
|
|
|
+ updateFabOpenLayer();
|
|
|
|
|
+ }, ms);
|
|
|
|
|
+ })(delays[i]);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ var openAppFabClickLock = 0;
|
|
|
|
|
+ function handleOpenAppFabClick() {
|
|
|
|
|
+ var now = Date.now();
|
|
|
|
|
+ if (now - openAppFabClickLock < 500) return;
|
|
|
|
|
+ openAppFabClickLock = now;
|
|
|
|
|
+ tryOpenHBuilderApp();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function bindOpenAppFabClick() {
|
|
|
|
|
+ var btn = document.getElementById('openApp');
|
|
|
|
|
+ if (btn) {
|
|
|
|
|
+ btn.addEventListener('click', function (e) {
|
|
|
|
|
+ if (e && e.cancelable) e.preventDefault();
|
|
|
|
|
+ handleOpenAppFabClick();
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function bindWeChatLaunchTagEvents() {
|
|
|
|
|
+ var tag = document.getElementById('launch-btn');
|
|
|
|
|
+ if (!tag) return;
|
|
|
|
|
+ refreshWxLaunchTagAttrs();
|
|
|
|
|
+ tag.addEventListener('launch', function (e) {
|
|
|
|
|
+ console.log('[wx-open-launch-app] launch ok', e && e.detail);
|
|
|
|
|
+ reportWxDebug('wx-open-launch-app launch', e && e.detail ? e.detail : { errMsg: 'launch:ok' }, [
|
|
|
|
|
+ 'extinfo=' + (tag.getAttribute('extinfo') || '').slice(0, 200)
|
|
|
|
|
+ ]);
|
|
|
|
|
+ });
|
|
|
|
|
+ tag.addEventListener('error', function (e) {
|
|
|
|
|
+ var detail = e && e.detail;
|
|
|
|
|
+ console.warn('[wx-open-launch-app]', detail);
|
|
|
|
|
+ reportWxDebug('wx-open-launch-app error', detail, [
|
|
|
|
|
+ 'openTag appid=' + WECHAT_OPEN_APP_ID,
|
|
|
|
|
+ '完整 errMsg=' +
|
|
|
|
|
+ (detail && detail.errMsg
|
|
|
|
|
+ ? String(detail.errMsg)
|
|
|
|
|
+ : detail && detail.errmsg
|
|
|
|
|
+ ? String(detail.errmsg)
|
|
|
|
|
+ : '(无)')
|
|
|
|
|
+ ]);
|
|
|
|
|
+ var errMsg =
|
|
|
|
|
+ detail && detail.errMsg
|
|
|
|
|
+ ? String(detail.errMsg)
|
|
|
|
|
+ : detail && detail.errmsg
|
|
|
|
|
+ ? String(detail.errmsg)
|
|
|
|
|
+ : '';
|
|
|
|
|
+ if (/launch:fail/i.test(errMsg)) {
|
|
|
|
|
+ showAppOpenFailTip(
|
|
|
|
|
+ '微信未能唤起 App(' +
|
|
|
|
|
+ errMsg +
|
|
|
|
|
+ ')。请确认:①已安装 U店在哪;②开放标签 appid 为移动应用 ID;③开放平台已绑定域名与 App。'
|
|
|
|
|
+ );
|
|
|
|
|
+ } else {
|
|
|
|
|
+ showAppOpenFailTip(
|
|
|
|
|
+ errMsg
|
|
|
|
|
+ ? '开放标签错误:' + errMsg
|
|
|
|
|
+ : '开放标签唤起失败,请用 wxDebug=1 查看详情。'
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 进入页即 GET /wx/getWxConfig(任意浏览器都会请求,便于联调);
|
|
|
|
|
+ * 仅微信内且签名成功后再 wx.config + 展示开放标签按钮。
|
|
|
|
|
+ */
|
|
|
|
|
+ function initWeChatOpenLaunchApp() {
|
|
|
|
|
+ var inWx = isWeChatInAppBrowser();
|
|
|
|
|
+ wxConfigSignRetriedBaseUrl = false;
|
|
|
|
|
+ var signPageUrl = getWxConfigSignUrl();
|
|
|
|
|
+ var reqUrl = buildWxGetConfigRequestUrl(signPageUrl);
|
|
|
|
|
+
|
|
|
|
|
+ if (inWx) {
|
|
|
|
|
+ var domainHint = checkWxJsSafeDomainHint();
|
|
|
|
|
+ if (domainHint) {
|
|
|
|
|
+ console.warn('[wx]', domainHint);
|
|
|
|
|
+ showAppOpenFailTip(domainHint);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ console.log('[wx] GET', reqUrl);
|
|
|
|
|
+
|
|
|
|
|
+ return fetchWeChatJssdkSign(signPageUrl)
|
|
|
|
|
+ .then(function (sign) {
|
|
|
|
|
+ if (!inWx) {
|
|
|
|
|
+ if (!sign) {
|
|
|
|
|
+ console.warn('[wx] getWxConfig empty (non-wechat preview)');
|
|
|
|
|
+ }
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (typeof wx === 'undefined') {
|
|
|
|
|
+ console.warn('[wx] jweixin not loaded');
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ bindWeChatLaunchTagEvents();
|
|
|
|
|
+ if (!sign) {
|
|
|
|
|
+ console.warn('[wx] GET ' + WECHAT_GET_WX_CONFIG_PATH + ' — empty or invalid sign payload');
|
|
|
|
|
+ showAppOpenFailTip(
|
|
|
|
|
+ '微信 JSSDK:getWxConfig 未返回可用的 appId/timestamp/nonceStr/signature。'
|
|
|
|
|
+ );
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ return applyWxConfig(sign, signPageUrl);
|
|
|
|
|
+ })
|
|
|
|
|
+ .catch(function (e) {
|
|
|
|
|
+ console.warn('[wx] init failed', e);
|
|
|
|
|
+ return false;
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function isCustomAppSchemeUrl(url) {
|
|
|
|
|
+ var s = String(url || '').trim();
|
|
|
|
|
+ if (!s) return false;
|
|
|
|
|
+ if (/^https?:/i.test(s)) return false;
|
|
|
|
|
+ return /^[a-z][a-z0-9+.-]*:/i.test(s);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function launchAppDeepLink(deepLink) {
|
|
|
|
|
+ deepLink = String(deepLink || '').trim();
|
|
|
|
|
+ if (!deepLink) return;
|
|
|
|
|
+ if (isCustomAppSchemeUrl(deepLink)) {
|
|
|
|
|
+ var anchor = document.getElementById('appDeepLinkAnchor');
|
|
|
|
|
+ if (!anchor) {
|
|
|
|
|
+ anchor = document.createElement('a');
|
|
|
|
|
+ anchor.id = 'appDeepLinkAnchor';
|
|
|
|
|
+ anchor.setAttribute('aria-hidden', 'true');
|
|
|
|
|
+ anchor.style.cssText =
|
|
|
|
|
+ 'position:fixed;left:-9999px;top:0;width:1px;height:1px;opacity:0;pointer-events:none;';
|
|
|
|
|
+ document.body.appendChild(anchor);
|
|
|
|
|
+ }
|
|
|
|
|
+ anchor.setAttribute('href', deepLink);
|
|
|
|
|
+ anchor.click();
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
try {
|
|
try {
|
|
|
window.location.href = deepLink;
|
|
window.location.href = deepLink;
|
|
|
- } catch (e2) {}
|
|
|
|
|
|
|
+ } catch (eHref) {}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 有 plus:检测 App 是否安装后 openURL;深链形如 shopro://{APP_UNI_STORE_PATH}?…
|
|
* 有 plus:检测 App 是否安装后 openURL;深链形如 shopro://{APP_UNI_STORE_PATH}?…
|
|
|
- * 系统浏览器:唤起 scheme;微信内常拦截 scheme,需「在浏览器打开」。
|
|
|
|
|
|
|
+ * 系统浏览器:scheme 唤起;微信内优先 wx-open-launch-app,失败再 scheme(不再弹「在浏览器中打开」)。
|
|
|
*/
|
|
*/
|
|
|
function tryOpenHBuilderApp() {
|
|
function tryOpenHBuilderApp() {
|
|
|
|
|
+ var inWx = isWeChatInAppBrowser();
|
|
|
|
|
+ showFabToast('正在尝试打开 U店在哪…');
|
|
|
|
|
+
|
|
|
|
|
+ if (inWx && !weChatJssdkConfigured) {
|
|
|
|
|
+ showAppOpenFailTip(
|
|
|
|
|
+ '微信 JSSDK 未就绪:请先关注测试号并确保 wx.config 成功。链接可加 wxDebug=1 查看 config 报错。'
|
|
|
|
|
+ );
|
|
|
|
|
+ launchAppDeepLink(buildAppDeepLink());
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (inWx && weChatJssdkConfigured) {
|
|
|
|
|
+ refreshWxLaunchTagAttrs();
|
|
|
|
|
+ updateFabOpenLayer();
|
|
|
|
|
+ if (tryTriggerWxOpenLaunchApp()) {
|
|
|
|
|
+ showFabToast('正在打开 U店在哪…');
|
|
|
|
|
+ } else {
|
|
|
|
|
+ showAppOpenFailTip(
|
|
|
|
|
+ '请点击底部 APP内打开 图片按钮;若仍无效,请用 wxDebug=1 查看开放标签是否 launch:fail。'
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
var deepLink = buildAppDeepLink();
|
|
var deepLink = buildAppDeepLink();
|
|
|
|
|
|
|
|
if (typeof plus !== 'undefined' && plus.runtime) {
|
|
if (typeof plus !== 'undefined' && plus.runtime) {
|
|
@@ -1125,20 +1798,19 @@
|
|
|
} catch (e) {
|
|
} catch (e) {
|
|
|
console.warn(e);
|
|
console.warn(e);
|
|
|
}
|
|
}
|
|
|
- if (installed === false) {
|
|
|
|
|
- showDownloadTip();
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
try {
|
|
try {
|
|
|
plus.runtime.openURL(deepLink);
|
|
plus.runtime.openURL(deepLink);
|
|
|
} catch (e2) {
|
|
} catch (e2) {
|
|
|
console.warn(e2);
|
|
console.warn(e2);
|
|
|
- showDownloadTip();
|
|
|
|
|
|
|
+ if (installed === false) {
|
|
|
|
|
+ showDownloadTip();
|
|
|
|
|
+ } else if (!inWx) {
|
|
|
|
|
+ showAppOpenFailTip();
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- var t0 = Date.now();
|
|
|
|
|
var done = false;
|
|
var done = false;
|
|
|
function finish() {
|
|
function finish() {
|
|
|
if (done) return;
|
|
if (done) return;
|
|
@@ -1155,25 +1827,21 @@
|
|
|
document.addEventListener('visibilitychange', onVis);
|
|
document.addEventListener('visibilitychange', onVis);
|
|
|
window.addEventListener('pagehide', onHide);
|
|
window.addEventListener('pagehide', onHide);
|
|
|
|
|
|
|
|
- if (isWeChatInAppBrowser()) {
|
|
|
|
|
- window.alert('若点击后无法打开 App:请先点右上角「···」,选择「在浏览器中打开」,再点「APP内打开」。');
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
try {
|
|
try {
|
|
|
launchAppDeepLink(deepLink);
|
|
launchAppDeepLink(deepLink);
|
|
|
} catch (e3) {
|
|
} catch (e3) {
|
|
|
finish();
|
|
finish();
|
|
|
- showDownloadTip();
|
|
|
|
|
|
|
+ if (!inWx) showAppOpenFailTip();
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 微信内 scheme 常被拦截且页面仍 visible,原 2.6s 后 showDownloadTip 会误报「请到应用商店下载」。
|
|
|
|
|
+ * 与 shareDynamic 一致:超时仅收尾,不弹下载提示。
|
|
|
|
|
+ */
|
|
|
window.setTimeout(function () {
|
|
window.setTimeout(function () {
|
|
|
- if (done) return;
|
|
|
|
|
- if (document.visibilityState === 'visible' && Date.now() - t0 < 3500) {
|
|
|
|
|
- showDownloadTip();
|
|
|
|
|
- }
|
|
|
|
|
finish();
|
|
finish();
|
|
|
- }, 2600);
|
|
|
|
|
|
|
+ }, 3200);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function qs() {
|
|
function qs() {
|
|
@@ -1938,20 +2606,28 @@
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function fetchShareClosedStoreRecommend(storeIdStr) {
|
|
function fetchShareClosedStoreRecommend(storeIdStr) {
|
|
|
- var loc = getClosedRecommendUserLocation();
|
|
|
|
|
|
|
+ var latRaw = (q('userLat') || q('latitude') || q('lat') || q('weidu')).trim();
|
|
|
|
|
+ var lngRaw = (q('userLng') || q('longitude') || q('lon') || q('jingdu')).trim();
|
|
|
|
|
+ var userLat =
|
|
|
|
|
+ latRaw !== '' && !isNaN(Number(latRaw)) ? Number(latRaw) : DEFAULT_REC_USER_LAT;
|
|
|
|
|
+ var userLng =
|
|
|
|
|
+ lngRaw !== '' && !isNaN(Number(lngRaw)) ? Number(lngRaw) : DEFAULT_REC_USER_LNG;
|
|
|
|
|
|
|
|
var page = parseInt(q('page') || '1', 10);
|
|
var page = parseInt(q('page') || '1', 10);
|
|
|
var pageSize = parseInt(q('pageSize') || '10', 10);
|
|
var pageSize = parseInt(q('pageSize') || '10', 10);
|
|
|
if (isNaN(page) || page < 1) page = 1;
|
|
if (isNaN(page) || page < 1) page = 1;
|
|
|
if (isNaN(pageSize) || pageSize < 1) pageSize = 10;
|
|
if (isNaN(pageSize) || pageSize < 1) pageSize = 10;
|
|
|
|
|
|
|
|
|
|
+ var userCityRaw = (q('userCity') || q('city') || '').trim();
|
|
|
|
|
+ var userCity = userCityRaw !== '' ? userCityRaw : DEFAULT_REC_USER_CITY;
|
|
|
|
|
+
|
|
|
var body = {
|
|
var body = {
|
|
|
page: page,
|
|
page: page,
|
|
|
pageSize: pageSize,
|
|
pageSize: pageSize,
|
|
|
storeId: String(storeIdStr || ''),
|
|
storeId: String(storeIdStr || ''),
|
|
|
- userCity: loc.userCity,
|
|
|
|
|
- userLat: loc.userLat,
|
|
|
|
|
- userLng: loc.userLng
|
|
|
|
|
|
|
+ userCity: userCity,
|
|
|
|
|
+ userLat: userLat,
|
|
|
|
|
+ userLng: userLng
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
return fetch(API_LIFE_AI_BASE + STORE_GLOBAL_RECOMMEND_PATH, {
|
|
return fetch(API_LIFE_AI_BASE + STORE_GLOBAL_RECOMMEND_PATH, {
|
|
@@ -2322,12 +2998,19 @@
|
|
|
run();
|
|
run();
|
|
|
bindMarketingMore();
|
|
bindMarketingMore();
|
|
|
bindStaffSection();
|
|
bindStaffSection();
|
|
|
- var openBtn = document.getElementById('openApp');
|
|
|
|
|
- if (openBtn) {
|
|
|
|
|
- openBtn.addEventListener('click', function () {
|
|
|
|
|
- tryOpenHBuilderApp();
|
|
|
|
|
- });
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ var launchTag = document.getElementById('launch-btn');
|
|
|
|
|
+ if (launchTag && WECHAT_OPEN_APP_ID) {
|
|
|
|
|
+ launchTag.setAttribute('appid', WECHAT_OPEN_APP_ID);
|
|
|
|
|
+ }
|
|
|
|
|
+ bindOpenAppFabClick();
|
|
|
|
|
+ updateFabOpenLayer();
|
|
|
|
|
+ if (isWxDebugOn()) {
|
|
|
|
|
+ reportWxDebug('page load', { errMsg: 'wxDebug=1 已开启' }, [
|
|
|
|
|
+ 'UA=' + (navigator.userAgent || '').slice(0, 120),
|
|
|
|
|
+ 'page=' + location.href.split('#')[0]
|
|
|
|
|
+ ]);
|
|
|
|
|
+ }
|
|
|
|
|
+ initWeChatOpenLaunchApp();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (document.readyState === 'complete') {
|
|
if (document.readyState === 'complete') {
|