|
|
@@ -185,6 +185,62 @@
|
|
|
pointer-events: none;
|
|
|
}
|
|
|
|
|
|
+ .fab-dock__slot {
|
|
|
+ width: 100%;
|
|
|
+ max-width: 320px;
|
|
|
+ height: 48px;
|
|
|
+ margin: 0 auto;
|
|
|
+ pointer-events: auto;
|
|
|
+ }
|
|
|
+
|
|
|
+ #openApp img {
|
|
|
+ display: block;
|
|
|
+ width: 100%;
|
|
|
+ height: 48px;
|
|
|
+ object-fit: contain;
|
|
|
+ pointer-events: none;
|
|
|
+ }
|
|
|
+
|
|
|
+ #launch-btn {
|
|
|
+ display: none;
|
|
|
+ width: 100%;
|
|
|
+ height: 48px;
|
|
|
+ min-height: 48px;
|
|
|
+ border-radius: 24px;
|
|
|
+ overflow: hidden;
|
|
|
+ opacity: 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ body.is-wechat.wx-jssdk-ready #launch-btn {
|
|
|
+ display: block;
|
|
|
+ }
|
|
|
+
|
|
|
+ body.is-wechat.wx-jssdk-ready #openApp {
|
|
|
+ display: none !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ #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;
|
|
|
+ }
|
|
|
+
|
|
|
+ #openApp {
|
|
|
+ touch-action: manipulation;
|
|
|
+ }
|
|
|
+
|
|
|
.fab-wrap .fab {
|
|
|
pointer-events: auto;
|
|
|
}
|
|
|
@@ -236,13 +292,51 @@
|
|
|
<p id="recEmpty" style="padding:12px;color:#999;font-size:14px;display:none;">暂无推荐</p>
|
|
|
</div>
|
|
|
|
|
|
+ <div id="openAppToast" role="status" aria-live="polite"></div>
|
|
|
<div class="fab-wrap">
|
|
|
- <button type="button" class="fab" id="openApp">
|
|
|
- <img src="images/uBtn.png" alt="APP内打开" decoding="async">
|
|
|
- </button>
|
|
|
+ <div class="fab-dock__slot">
|
|
|
+ <!-- 非微信 / 微信 JSSDK 未就绪:scheme 唤起 -->
|
|
|
+ <button type="button" class="fab" id="openApp">
|
|
|
+ <img src="images/uBtn.png" alt="APP内打开" decoding="async">
|
|
|
+ </button>
|
|
|
+ <!-- 微信内 wx.config 成功后:仅此按钮可唤起 App(须用户直接点击) -->
|
|
|
+ <wx-open-launch-app id="launch-btn" appid="wxf5f1efe3a9f5012e" extinfo="">
|
|
|
+ <script type="text/wxtag-template">
|
|
|
+ <style>
|
|
|
+ .wx-open-app-btn {
|
|
|
+ display: block;
|
|
|
+ width: 100%;
|
|
|
+ height: 48px;
|
|
|
+ margin: 0;
|
|
|
+ padding: 0;
|
|
|
+ border: none;
|
|
|
+ border-radius: 24px;
|
|
|
+ background: #F47D1F;
|
|
|
+ box-shadow: 0 4px 16px rgba(245, 130, 32, 0.45);
|
|
|
+ cursor: pointer;
|
|
|
+ overflow: hidden;
|
|
|
+ -webkit-tap-highlight-color: transparent;
|
|
|
+ }
|
|
|
+ .wx-open-app-btn img {
|
|
|
+ display: block;
|
|
|
+ width: 100%;
|
|
|
+ height: 48px;
|
|
|
+ object-fit: contain;
|
|
|
+ opacity: 1;
|
|
|
+ pointer-events: none;
|
|
|
+ -webkit-user-drag: none;
|
|
|
+ }
|
|
|
+ </style>
|
|
|
+ <button class="wx-open-app-btn" aria-label="APP内打开">
|
|
|
+ <img src="https://test.ailien.shop/h5/HBuilderProjects/images/uBtn.png" alt="APP内打开" width="320" height="48" />
|
|
|
+ </button>
|
|
|
+ </script>
|
|
|
+ </wx-open-launch-app>
|
|
|
+ </div>
|
|
|
<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';
|
|
|
@@ -253,6 +347,7 @@
|
|
|
*
|
|
|
* businessStatus=99(关店,与 shareIndex.html 一致):点「APP内打开」深链为 shopro://pages/index/login?…,不再进打卡页。
|
|
|
*/
|
|
|
+ var API_BASE = 'https://test.ailien.shop/alienStore';
|
|
|
var API_LIFE_AI_BASE = 'http://183.252.196.135:9100';
|
|
|
var STORE_GLOBAL_RECOMMEND_PATH =
|
|
|
'/ai/multimodal-services/api/v1/search/global/store-recommend';
|
|
|
@@ -261,15 +356,27 @@
|
|
|
var DEFAULT_USER_CITY = '大连市';
|
|
|
var DEFAULT_STORE_ID = '378';
|
|
|
|
|
|
+ /**
|
|
|
+ * 微信 JSSDK — 与 shareIndex.html 一致
|
|
|
+ * POST {API_BASE}/wx/getWxConfig,body 传 url(当前页完整地址,不含 #)
|
|
|
+ */
|
|
|
+ var WECHAT_MP_APP_ID = 'wx412792c77f47babd';
|
|
|
+ var WECHAT_OPEN_APP_ID = 'wxf5f1efe3a9f5012e';
|
|
|
+ var WECHAT_GET_WX_CONFIG_PATH = '/wx/getWxConfig';
|
|
|
+ var H5_PAGE_BASE_FALLBACK = 'https://test.ailien.shop/h5/HBuilderProjects/';
|
|
|
+ var WX_GET_CONFIG_SIGN_URL = H5_PAGE_BASE_FALLBACK + 'shareCheckInUndefined.html';
|
|
|
+ var WECHAT_JS_SAFE_HOSTS = ['uat.ailien.shop', 'test.ailien.shop'];
|
|
|
+ var weChatJssdkConfigured = false;
|
|
|
+ var wxConfigSignRetriedBaseUrl = false;
|
|
|
+ var wxInitLastError = '';
|
|
|
+ var wxJssdkInitPromise = null;
|
|
|
+
|
|
|
var APP_ANDROID_PACKAGE = 'com.alien.Udianzaizhe';
|
|
|
var APP_IOS_URL_SCHEME = 'shopro://';
|
|
|
var APP_UNI_STORE_PATH = 'pages/checkIn/index';
|
|
|
|
|
|
- function qs() {
|
|
|
- return new URLSearchParams(location.search || '');
|
|
|
- }
|
|
|
function q(name) {
|
|
|
- var v = qs().get(name);
|
|
|
+ var v = mergeSearchAndHashParams().get(name);
|
|
|
return v == null ? '' : String(v);
|
|
|
}
|
|
|
|
|
|
@@ -284,24 +391,46 @@
|
|
|
|
|
|
function mergeSearchAndHashParams() {
|
|
|
var params = new URLSearchParams();
|
|
|
- function ingest(querySlice) {
|
|
|
+ function ingestAppend(querySlice) {
|
|
|
if (!querySlice) return;
|
|
|
var p = new URLSearchParams(querySlice);
|
|
|
p.forEach(function (val, key) {
|
|
|
params.append(key, val);
|
|
|
});
|
|
|
}
|
|
|
+ function ingestSet(querySlice) {
|
|
|
+ if (!querySlice) return;
|
|
|
+ var p = new URLSearchParams(querySlice);
|
|
|
+ p.forEach(function (val, key) {
|
|
|
+ params.set(key, val);
|
|
|
+ });
|
|
|
+ }
|
|
|
if (location.search && location.search.length > 1) {
|
|
|
- ingest(location.search.slice(1));
|
|
|
+ ingestAppend(location.search.slice(1));
|
|
|
}
|
|
|
var hash = location.hash || '';
|
|
|
var qi = hash.indexOf('?');
|
|
|
if (qi >= 0) {
|
|
|
- ingest(hash.slice(qi + 1));
|
|
|
+ ingestSet(hash.slice(qi + 1));
|
|
|
}
|
|
|
return params;
|
|
|
}
|
|
|
|
|
|
+ function getRawMergedQueryForAppOpen() {
|
|
|
+ var rawSearch =
|
|
|
+ location.search && location.search.length > 1 ? location.search.slice(1) : '';
|
|
|
+ var hash0 = location.hash || '';
|
|
|
+ var hqi0 = hash0.indexOf('?');
|
|
|
+ var rawHashQ = hqi0 >= 0 ? hash0.slice(hqi0 + 1) : '';
|
|
|
+
|
|
|
+ if (!rawSearch && !rawHashQ) return '';
|
|
|
+
|
|
|
+ if (rawHashQ && rawSearch) {
|
|
|
+ return rawHashQ;
|
|
|
+ }
|
|
|
+ return rawHashQ || rawSearch;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 关店 businessStatus=99:URL(含 hash)带 businessStatus=99 时,唤起 App 进 pages/index/login(与 shareIndex.html isClosedMerchantForAppOpen)。
|
|
|
*/
|
|
|
@@ -310,22 +439,28 @@
|
|
|
var bs = String(mergeSearchAndHashParams().get('businessStatus') || '').trim();
|
|
|
if (bs === '99' || Number(bs) === 99) return true;
|
|
|
} catch (e0) {}
|
|
|
- var bs2 = String(q('businessStatus') || '').trim();
|
|
|
- return bs2 === '99' || Number(bs2) === 99;
|
|
|
+ return false;
|
|
|
}
|
|
|
|
|
|
function getAppUniPathForCheckInShare() {
|
|
|
if (isClosedMerchantForAppOpen()) {
|
|
|
return 'pages/index/login';
|
|
|
}
|
|
|
- return String(APP_UNI_STORE_PATH || 'pages/checkIn/index').replace(/^\//, '');
|
|
|
+ var p = mergeSearchAndHashParams();
|
|
|
+ if (!p.has('isShareCheckInSquare')) {
|
|
|
+ return String(APP_UNI_STORE_PATH || 'pages/checkIn/index').replace(/^\//, '');
|
|
|
+ }
|
|
|
+ var v = String(p.get('isShareCheckInSquare') || '').trim().toLowerCase();
|
|
|
+ if (v === '' || v === '0' || v === 'false' || v === 'no') {
|
|
|
+ return String(APP_UNI_STORE_PATH || 'pages/checkIn/index').replace(/^\//, '');
|
|
|
+ }
|
|
|
+ return 'pages/checkIn/details';
|
|
|
}
|
|
|
|
|
|
/** businessStatus=99(商户关闭):插图与文案;否则为「内容已删除」 */
|
|
|
function applyEmptyHeroState() {
|
|
|
var merged = mergeSearchAndHashParams();
|
|
|
var bsRaw = merged.get('businessStatus');
|
|
|
- if (bsRaw == null || bsRaw === '') bsRaw = q('businessStatus');
|
|
|
var bs = bsRaw == null ? '' : String(bsRaw).trim();
|
|
|
var isClosed = Number(bs) === 99 || bs === '99';
|
|
|
var img = document.getElementById('heroEmptyIllustration');
|
|
|
@@ -344,15 +479,61 @@
|
|
|
this.onerror = null;
|
|
|
this.src = 'images/empty.png';
|
|
|
};
|
|
|
+ if (weChatJssdkConfigured) {
|
|
|
+ refreshWxLaunchTagAttrs();
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
function buildAppOpenQueryStringMerged() {
|
|
|
+ var rawQs = getRawMergedQueryForAppOpen();
|
|
|
+ if (rawQs) {
|
|
|
+ return '?' + rawQs;
|
|
|
+ }
|
|
|
var params = mergeSearchAndHashParams();
|
|
|
var sid = params.get('storeId') || params.get('id') || '';
|
|
|
if (sid && !params.has('storeId')) {
|
|
|
params.set('storeId', sid);
|
|
|
}
|
|
|
- return params.toString() ? ('?' + params.toString()) : '';
|
|
|
+ var qsOut = params.toString();
|
|
|
+ return qsOut ? ('?' + qsOut) : '';
|
|
|
+ }
|
|
|
+
|
|
|
+ function buildAppUniPageLaunchUrl() {
|
|
|
+ var path = getAppUniPathForCheckInShare().replace(/^\//, '');
|
|
|
+ var qs = buildAppOpenQueryStringMerged().replace(/^\?/, '');
|
|
|
+ return qs ? path + '?' + qs : path;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * wx-open-launch-app extinfo:pages/checkIn/index、pages/checkIn/details 或关店 pages/index/login
|
|
|
+ */
|
|
|
+ function buildWeChatLaunchExtinfo() {
|
|
|
+ var uniPage = buildAppUniPageLaunchUrl();
|
|
|
+ if (uniPage.length <= 1024) return uniPage;
|
|
|
+
|
|
|
+ var path = getAppUniPathForCheckInShare().replace(/^\//, '');
|
|
|
+ var rawQs = getRawMergedQueryForAppOpen();
|
|
|
+ if (rawQs) {
|
|
|
+ var packedRaw = path + '?' + rawQs;
|
|
|
+ if (packedRaw.length <= 1024) return packedRaw;
|
|
|
+ }
|
|
|
+
|
|
|
+ var params = mergeSearchAndHashParams();
|
|
|
+ var mini = new URLSearchParams();
|
|
|
+ var storeId = params.get('storeId') || params.get('id') || '';
|
|
|
+ if (storeId) mini.set('storeId', storeId);
|
|
|
+ var square = params.get('isShareCheckInSquare');
|
|
|
+ if (square != null && String(square).trim() !== '') {
|
|
|
+ mini.set('isShareCheckInSquare', String(square).trim());
|
|
|
+ }
|
|
|
+ var bs = params.get('businessStatus');
|
|
|
+ if (bs != null && String(bs).trim() !== '') {
|
|
|
+ mini.set('businessStatus', String(bs).trim());
|
|
|
+ }
|
|
|
+ var shortPage = path + '?' + mini.toString();
|
|
|
+ if (shortPage.length <= 1024) return shortPage;
|
|
|
+ var deep = buildAppDeepLink().replace(/^shopro:\/\//i, '');
|
|
|
+ return deep.length <= 1024 ? deep : shortPage.slice(0, 1024);
|
|
|
}
|
|
|
|
|
|
function buildAppDeepLink() {
|
|
|
@@ -365,6 +546,414 @@
|
|
|
return root + '/' + path + s;
|
|
|
}
|
|
|
|
|
|
+ function isWeChatInAppBrowser() {
|
|
|
+ return /MicroMessenger/i.test(navigator.userAgent || '');
|
|
|
+ }
|
|
|
+
|
|
|
+ function readQueryParam(name) {
|
|
|
+ return q(name);
|
|
|
+ }
|
|
|
+
|
|
|
+ function isWxDebugOn() {
|
|
|
+ return readQueryParam('wxDebug') === '1';
|
|
|
+ }
|
|
|
+
|
|
|
+ function isWxForceDebug() {
|
|
|
+ return readQueryParam('wxForce') === '1';
|
|
|
+ }
|
|
|
+
|
|
|
+ function isWxConfigOnClickDebug() {
|
|
|
+ return readQueryParam('wxConfigOnClick') === '1';
|
|
|
+ }
|
|
|
+
|
|
|
+ function isWxPcAutoDebugHost() {
|
|
|
+ var h = (location.hostname || '').toLowerCase();
|
|
|
+ if (h === 'localhost' || h === '127.0.0.1') return true;
|
|
|
+ for (var i = 0; i < WECHAT_JS_SAFE_HOSTS.length; i++) {
|
|
|
+ if (WECHAT_JS_SAFE_HOSTS[i] === h) return true;
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ function isWxPcBrowser() {
|
|
|
+ return !isWeChatInAppBrowser();
|
|
|
+ }
|
|
|
+
|
|
|
+ function shouldInitWeChatJssdkOnLoad() {
|
|
|
+ if (isWeChatInAppBrowser()) return true;
|
|
|
+ return isWxForceDebug() || isWxPcAutoDebugHost();
|
|
|
+ }
|
|
|
+
|
|
|
+ function shouldFetchWxConfig(fromUserClick) {
|
|
|
+ if (isWeChatInAppBrowser()) return true;
|
|
|
+ if (fromUserClick) return true;
|
|
|
+ return isWxForceDebug() || isWxPcAutoDebugHost();
|
|
|
+ }
|
|
|
+
|
|
|
+ function getWxHtmlUrl() {
|
|
|
+ var forced = String(q('wxSignUrl') || '').trim();
|
|
|
+ if (forced) return forced.split('#')[0];
|
|
|
+ return String(location.href || '').split('#')[0];
|
|
|
+ }
|
|
|
+
|
|
|
+ function getWxSignPageUrlForApi() {
|
|
|
+ var htmlUrl = getWxHtmlUrl();
|
|
|
+ if (htmlUrl && /^https?:\/\//i.test(htmlUrl)) return htmlUrl;
|
|
|
+ return WX_GET_CONFIG_SIGN_URL;
|
|
|
+ }
|
|
|
+
|
|
|
+ function getWxHtmlUrlBase() {
|
|
|
+ var htmlUrl = getWxSignPageUrlForApi();
|
|
|
+ try {
|
|
|
+ var u = new URL(htmlUrl);
|
|
|
+ return u.origin + u.pathname;
|
|
|
+ } catch (eU) {
|
|
|
+ return WX_GET_CONFIG_SIGN_URL;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ function getWxConfigSignUrl() {
|
|
|
+ if (String(q('wxSignBaseOnly') || '') === '1') return getWxHtmlUrlBase();
|
|
|
+ return getWxSignPageUrlForApi();
|
|
|
+ }
|
|
|
+
|
|
|
+ function getWxGetConfigApiUrl() {
|
|
|
+ return API_BASE.replace(/\/$/, '') + WECHAT_GET_WX_CONFIG_PATH;
|
|
|
+ }
|
|
|
+
|
|
|
+ function buildWxGetConfigRequestBody(htmlUrl) {
|
|
|
+ return {
|
|
|
+ url: String(htmlUrl || '').split('#')[0].trim()
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ function resolveWxConfigAppIdFromSignData(d) {
|
|
|
+ if (!d || typeof d !== 'object') return '';
|
|
|
+ var mp =
|
|
|
+ d.mpAppId ||
|
|
|
+ d.mpAppid ||
|
|
|
+ d.officialAppId ||
|
|
|
+ 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;
|
|
|
+ return raw != null && String(raw).trim() !== '' ? 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 != null && String(d.nonceStr) !== ''
|
|
|
+ ? d.nonceStr
|
|
|
+ : d.noncestr != null && String(d.noncestr) !== ''
|
|
|
+ ? d.noncestr
|
|
|
+ : d.nonce;
|
|
|
+ var signature = d.signature || d.sign;
|
|
|
+ if (!appId || timestamp == null || nonceStr == null || nonceStr === '' || !signature) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ return {
|
|
|
+ appId: String(appId),
|
|
|
+ timestamp: Number(timestamp),
|
|
|
+ nonceStr: String(nonceStr),
|
|
|
+ signature: String(signature),
|
|
|
+ signUrl: String(signUrlUsed || '')
|
|
|
+ .split('#')[0]
|
|
|
+ .trim()
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ 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 (/invalid signature/i.test(errMsg)) {
|
|
|
+ return (
|
|
|
+ tip +
|
|
|
+ ':签名无效。请核对:①后端用与前端相同的 url 签名;②url=' +
|
|
|
+ (signPageUrl || getWxHtmlUrl()) +
|
|
|
+ ';③nonceStr/timestamp 与接口返回一致;④appId=' +
|
|
|
+ WECHAT_MP_APP_ID
|
|
|
+ );
|
|
|
+ }
|
|
|
+ if (/require\s*subscribe/i.test(errMsg)) {
|
|
|
+ return tip + ':' + errMsg + '(服务号需用户已关注)';
|
|
|
+ }
|
|
|
+ return errMsg ? tip + ':' + errMsg : tip;
|
|
|
+ }
|
|
|
+
|
|
|
+ function setWxInitError(msg) {
|
|
|
+ wxInitLastError = String(msg || '').trim();
|
|
|
+ if (wxInitLastError) console.warn('[wx]', wxInitLastError);
|
|
|
+ }
|
|
|
+
|
|
|
+ function requestWeChatJssdkSignAndConfig(htmlUrlOptional) {
|
|
|
+ var htmlUrl = String(
|
|
|
+ htmlUrlOptional != null ? htmlUrlOptional : getWxConfigSignUrl()
|
|
|
+ )
|
|
|
+ .split('#')[0]
|
|
|
+ .trim();
|
|
|
+ if (!htmlUrl || !/^https?:\/\//i.test(htmlUrl)) {
|
|
|
+ htmlUrl = WX_GET_CONFIG_SIGN_URL;
|
|
|
+ }
|
|
|
+ if (isWxDebugOn()) {
|
|
|
+ try {
|
|
|
+ window.alert('htmlUrl(签名用):\n' + htmlUrl);
|
|
|
+ } catch (eDbg) {}
|
|
|
+ }
|
|
|
+ var requestUrl = getWxGetConfigApiUrl();
|
|
|
+ var requestBody = buildWxGetConfigRequestBody(htmlUrl);
|
|
|
+ console.log('[wx] htmlUrl=', htmlUrl);
|
|
|
+ console.log('[wx] POST getWxConfig →', requestUrl, requestBody);
|
|
|
+ return fetch(requestUrl, {
|
|
|
+ method: 'POST',
|
|
|
+ mode: 'cors',
|
|
|
+ credentials: 'omit',
|
|
|
+ headers: {
|
|
|
+ Accept: 'application/json',
|
|
|
+ 'Content-Type': 'application/json;charset=UTF-8'
|
|
|
+ },
|
|
|
+ body: JSON.stringify(requestBody)
|
|
|
+ })
|
|
|
+ .then(function (r) {
|
|
|
+ if (r.ok) return r.json();
|
|
|
+ return r
|
|
|
+ .text()
|
|
|
+ .catch(function () {
|
|
|
+ return '';
|
|
|
+ })
|
|
|
+ .then(function (text) {
|
|
|
+ var hint = '';
|
|
|
+ try {
|
|
|
+ var j = JSON.parse(text);
|
|
|
+ hint = j.msg || j.message || '';
|
|
|
+ } catch (eP) {
|
|
|
+ if (text) hint = text.slice(0, 120);
|
|
|
+ }
|
|
|
+ throw new Error('getWxConfig HTTP ' + r.status + (hint ? ':' + hint : ''));
|
|
|
+ });
|
|
|
+ })
|
|
|
+ .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 || res.message || 'getWxConfig code ' + c);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ var sign = normalizeWxJssdkSignPayload(res, htmlUrl);
|
|
|
+ if (!sign) {
|
|
|
+ console.warn('[wx] getWxConfig 响应字段不全', res, 'htmlUrl=', htmlUrl);
|
|
|
+ throw new Error(
|
|
|
+ 'getWxConfig 缺少 appId/timestamp/nonceStr/signature(见控制台)'
|
|
|
+ );
|
|
|
+ }
|
|
|
+ if (sign.appId !== WECHAT_MP_APP_ID) {
|
|
|
+ console.warn(
|
|
|
+ '[wx] 后端 appId=' + sign.appId + ',期望服务号 ' + WECHAT_MP_APP_ID
|
|
|
+ );
|
|
|
+ }
|
|
|
+ if (typeof wx === 'undefined') {
|
|
|
+ if (isWxPcBrowser()) {
|
|
|
+ console.log('[wx] PC getWxConfig 成功(无 jweixin)', sign);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ setWxInitError('jweixin.js 未加载');
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return applyWxConfigFromSign(sign, htmlUrl);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ function applyWxConfigFromSign(sign, htmlUrl) {
|
|
|
+ var wxConfigParams = {
|
|
|
+ debug: isWxDebugOn(),
|
|
|
+ appId: String(sign.appId),
|
|
|
+ timestamp: sign.timestamp,
|
|
|
+ nonceStr: String(sign.nonceStr),
|
|
|
+ signature: String(sign.signature),
|
|
|
+ jsApiList: [],
|
|
|
+ openTagList: ['wx-open-launch-app']
|
|
|
+ };
|
|
|
+ return new Promise(function (resolve) {
|
|
|
+ wx.config(wxConfigParams);
|
|
|
+ wx.ready(function () {
|
|
|
+ weChatJssdkConfigured = true;
|
|
|
+ document.body.classList.add('wx-jssdk-ready');
|
|
|
+ refreshWxLaunchTagAttrs();
|
|
|
+ console.log('[wx.config] ready, htmlUrl=', htmlUrl);
|
|
|
+ resolve(true);
|
|
|
+ });
|
|
|
+ wx.error(function (err) {
|
|
|
+ weChatJssdkConfigured = false;
|
|
|
+ document.body.classList.remove('wx-jssdk-ready');
|
|
|
+ wxJssdkInitPromise = null;
|
|
|
+ var errMsg = getWxErrMsg(err);
|
|
|
+ setWxInitError(formatWxConfigErrorTip(err, htmlUrl));
|
|
|
+ if (isWxDebugOn()) window.alert(wxInitLastError);
|
|
|
+ console.warn('[wx.config]', errMsg, 'htmlUrl=', htmlUrl);
|
|
|
+ var baseUrl = getWxHtmlUrlBase();
|
|
|
+ var fullUrl = getWxSignPageUrlForApi();
|
|
|
+ if (
|
|
|
+ !/invalid signature/i.test(errMsg) ||
|
|
|
+ wxConfigSignRetriedBaseUrl ||
|
|
|
+ baseUrl === fullUrl ||
|
|
|
+ htmlUrl === baseUrl
|
|
|
+ ) {
|
|
|
+ resolve(false);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ wxConfigSignRetriedBaseUrl = true;
|
|
|
+ requestWeChatJssdkSignAndConfig(baseUrl).then(resolve);
|
|
|
+ });
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ function refreshWxLaunchTagAttrs() {
|
|
|
+ var tag = document.getElementById('launch-btn');
|
|
|
+ if (!tag) return;
|
|
|
+ try {
|
|
|
+ tag.setAttribute('appid', WECHAT_OPEN_APP_ID);
|
|
|
+ tag.setAttribute('extinfo', buildWeChatLaunchExtinfo());
|
|
|
+ } catch (eA) {}
|
|
|
+ }
|
|
|
+
|
|
|
+ function bindWeChatLaunchTagEvents() {
|
|
|
+ var tag = document.getElementById('launch-btn');
|
|
|
+ if (!tag || tag._wxLaunchBound) return;
|
|
|
+ tag._wxLaunchBound = true;
|
|
|
+ refreshWxLaunchTagAttrs();
|
|
|
+ tag.addEventListener('launch', function () {
|
|
|
+ console.log('[wx-open-launch-app] launch ok');
|
|
|
+ showFabToast('正在打开 U店在哪…');
|
|
|
+ });
|
|
|
+ tag.addEventListener('error', function (e) {
|
|
|
+ var detail = e && e.detail;
|
|
|
+ var errMsg =
|
|
|
+ detail && detail.errMsg
|
|
|
+ ? String(detail.errMsg)
|
|
|
+ : detail && detail.errmsg
|
|
|
+ ? String(detail.errmsg)
|
|
|
+ : '';
|
|
|
+ console.warn('[wx-open-launch-app]', detail);
|
|
|
+ showAppOpenFailTip(
|
|
|
+ errMsg
|
|
|
+ ? '未能唤起 App:' + errMsg
|
|
|
+ : '未能唤起 App,请确认已安装 U店在哪,且开放平台已绑定服务号与移动应用。'
|
|
|
+ );
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ function initWeChatOpenLaunchApp(fromUserClick) {
|
|
|
+ if (!shouldFetchWxConfig(!!fromUserClick)) {
|
|
|
+ console.log('[wx] 未调用 getWxConfig');
|
|
|
+ return Promise.resolve(false);
|
|
|
+ }
|
|
|
+ if (isWxPcBrowser()) {
|
|
|
+ console.warn(
|
|
|
+ '[wx] PC:请求 getWxConfig' + (fromUserClick ? '(按钮)' : '(进页)')
|
|
|
+ );
|
|
|
+ }
|
|
|
+ if (wxJssdkInitPromise && !fromUserClick) return wxJssdkInitPromise;
|
|
|
+ wxConfigSignRetriedBaseUrl = false;
|
|
|
+ wxInitLastError = '';
|
|
|
+ var htmlUrl = getWxConfigSignUrl();
|
|
|
+ bindWeChatLaunchTagEvents();
|
|
|
+ wxJssdkInitPromise = requestWeChatJssdkSignAndConfig(htmlUrl)
|
|
|
+ .then(function (ok) {
|
|
|
+ if (ok === true) return true;
|
|
|
+ if (!wxInitLastError) {
|
|
|
+ setWxInitError('wx.config 失败,可加 ?wxDebug=1 查看 htmlUrl');
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ })
|
|
|
+ .catch(function (e) {
|
|
|
+ var msg = e && e.message ? e.message : 'getWxConfig 请求失败';
|
|
|
+ setWxInitError(msg);
|
|
|
+ console.warn('[wx] requestWeChatJssdkSignAndConfig failed', msg, 'htmlUrl=', htmlUrl);
|
|
|
+ if (isWeChatInAppBrowser() && isWxDebugOn()) window.alert(msg);
|
|
|
+ return false;
|
|
|
+ })
|
|
|
+ .finally(function () {
|
|
|
+ if (!weChatJssdkConfigured) wxJssdkInitPromise = null;
|
|
|
+ });
|
|
|
+ return wxJssdkInitPromise;
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 进页即尝试拉签名;jweixin 晚到时自动重试 */
|
|
|
+ function scheduleWeChatJssdkBootstrap() {
|
|
|
+ if (!shouldInitWeChatJssdkOnLoad()) return;
|
|
|
+ var attempts = 0;
|
|
|
+ function tick() {
|
|
|
+ attempts += 1;
|
|
|
+ if (weChatJssdkConfigured) return;
|
|
|
+ initWeChatOpenLaunchApp();
|
|
|
+ if (!weChatJssdkConfigured && attempts < 8 && typeof wx === 'undefined') {
|
|
|
+ setTimeout(tick, 400);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ tick();
|
|
|
+ }
|
|
|
+
|
|
|
+ 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._t) clearTimeout(showFabToast._t);
|
|
|
+ showFabToast._t = setTimeout(function () {
|
|
|
+ el.style.display = 'none';
|
|
|
+ }, ms || 2800);
|
|
|
+ }
|
|
|
+ console.log('[openApp]', tip);
|
|
|
+ }
|
|
|
+
|
|
|
+ function showAppOpenFailTip(msg) {
|
|
|
+ var tip = msg || '未能打开 App,请确认已安装最新版「U店在哪」。';
|
|
|
+ if (typeof uni !== 'undefined' && typeof uni.showToast === 'function') {
|
|
|
+ uni.showToast({ title: tip, icon: 'none', duration: 2800 });
|
|
|
+ } else if (isWeChatInAppBrowser()) {
|
|
|
+ window.alert(tip);
|
|
|
+ } else {
|
|
|
+ console.warn('[openApp]', tip);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ function tryFetchWxConfigOnPcClick() {
|
|
|
+ if (!isWxPcBrowser()) return Promise.resolve(false);
|
|
|
+ showFabToast('正在请求 getWxConfig…');
|
|
|
+ wxJssdkInitPromise = null;
|
|
|
+ return initWeChatOpenLaunchApp(true).then(function (ok) {
|
|
|
+ if (ok) {
|
|
|
+ showFabToast(
|
|
|
+ weChatJssdkConfigured
|
|
|
+ ? 'getWxConfig 成功,wx.config 已就绪'
|
|
|
+ : 'getWxConfig 成功'
|
|
|
+ );
|
|
|
+ } else {
|
|
|
+ showFabToast(wxInitLastError || 'getWxConfig 失败');
|
|
|
+ }
|
|
|
+ return ok;
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
function launchAppDeepLink(deepLink) {
|
|
|
try {
|
|
|
var a = document.createElement('a');
|
|
|
@@ -380,6 +969,19 @@
|
|
|
}
|
|
|
|
|
|
function tryOpenHBuilderApp() {
|
|
|
+ /* 微信内须直接点击 wx-open-launch-app,scheme 会被拦截 */
|
|
|
+ if (isWeChatInAppBrowser()) return;
|
|
|
+
|
|
|
+ tryFetchWxConfigOnPcClick().then(function () {
|
|
|
+ if (isWxConfigOnClickDebug() && !isWxForceDebug() && !isWxPcAutoDebugHost()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ tryOpenHBuilderAppViaScheme();
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ function tryOpenHBuilderAppViaScheme() {
|
|
|
+ showFabToast('正在打开 U店在哪…');
|
|
|
var deepLink = buildAppDeepLink();
|
|
|
|
|
|
if (typeof plus !== 'undefined' && plus.runtime) {
|
|
|
@@ -398,18 +1000,15 @@
|
|
|
showDownloadTip();
|
|
|
return;
|
|
|
}
|
|
|
- if (installed === true) {
|
|
|
- try {
|
|
|
- plus.runtime.openURL(deepLink);
|
|
|
- } catch (e2) {
|
|
|
- console.warn(e2);
|
|
|
- showDownloadTip();
|
|
|
- }
|
|
|
- return;
|
|
|
+ try {
|
|
|
+ plus.runtime.openURL(deepLink);
|
|
|
+ } catch (e2) {
|
|
|
+ console.warn(e2);
|
|
|
+ showDownloadTip();
|
|
|
}
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
- var t0 = Date.now();
|
|
|
var done = false;
|
|
|
function finish() {
|
|
|
if (done) return;
|
|
|
@@ -426,13 +1025,15 @@
|
|
|
document.addEventListener('visibilitychange', onVis);
|
|
|
window.addEventListener('pagehide', onHide);
|
|
|
|
|
|
- launchAppDeepLink(deepLink);
|
|
|
+ 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);
|
|
|
}
|
|
|
@@ -672,13 +1273,36 @@
|
|
|
}
|
|
|
|
|
|
function boot() {
|
|
|
+ var launchTag = document.getElementById('launch-btn');
|
|
|
+ if (launchTag) launchTag.setAttribute('appid', WECHAT_OPEN_APP_ID);
|
|
|
+ bindWeChatLaunchTagEvents();
|
|
|
+ if (shouldInitWeChatJssdkOnLoad()) {
|
|
|
+ if (isWeChatInAppBrowser()) document.body.classList.add('is-wechat');
|
|
|
+ if (isWxPcBrowser() && (isWxForceDebug() || isWxPcAutoDebugHost())) {
|
|
|
+ document.body.classList.add('wx-pc-debug');
|
|
|
+ }
|
|
|
+ scheduleWeChatJssdkBootstrap();
|
|
|
+ }
|
|
|
+
|
|
|
applyEmptyHeroState();
|
|
|
run();
|
|
|
- var openBtn = document.getElementById('openApp');
|
|
|
- if (openBtn) {
|
|
|
- openBtn.addEventListener('click', function () {
|
|
|
- tryOpenHBuilderApp();
|
|
|
- });
|
|
|
+
|
|
|
+ if (isWeChatInAppBrowser()) {
|
|
|
+ var openBtnWx = document.getElementById('openApp');
|
|
|
+ if (openBtnWx) {
|
|
|
+ openBtnWx.addEventListener('click', function () {
|
|
|
+ if (!weChatJssdkConfigured) {
|
|
|
+ showFabToast(
|
|
|
+ wxInitLastError || '微信 SDK 初始化中,请稍候再点底部按钮'
|
|
|
+ );
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ var openBtn = document.getElementById('openApp');
|
|
|
+ if (openBtn) {
|
|
|
+ openBtn.addEventListener('click', tryOpenHBuilderApp);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|