Jelajahi Sumber

U宝h5页面修复

lxr 1 Minggu lalu
induk
melakukan
e33ab23365
1 mengubah file dengan 605 tambahan dan 14 penghapusan
  1. 605 14
      HBuilderProjects/shareAiConsult.html

+ 605 - 14
HBuilderProjects/shareAiConsult.html

@@ -124,6 +124,13 @@
       z-index: 20;
       padding: 10px 15px calc(10px + var(--safe-bottom));
       background: linear-gradient(180deg, rgba(244, 246, 251, 0) 0%, #f4f6fb 36%);
+      pointer-events: none;
+    }
+
+    .footer__dock {
+      position: relative;
+      width: 100%;
+      pointer-events: auto;
     }
 
     .continue-btn {
@@ -136,6 +143,47 @@
       color: #fff;
       font-size: 16px;
       font-weight: 600;
+      touch-action: manipulation;
+    }
+
+    #launch-btn {
+      display: none;
+      position: absolute;
+      left: 0;
+      right: 0;
+      top: 0;
+      width: 100%;
+      height: 44px;
+      min-height: 44px;
+      border-radius: 999px;
+      overflow: hidden;
+    }
+
+    body.is-wechat.wx-jssdk-ready #launch-btn {
+      display: block;
+    }
+
+    body.is-wechat.wx-jssdk-ready #btnContinue {
+      visibility: hidden;
+      pointer-events: none;
+    }
+
+    #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;
     }
   </style>
 </head>
@@ -144,13 +192,60 @@
     <div class="empty">对话内容加载中…</div>
   </main>
 
+  <div id="openAppToast" role="status" aria-live="polite"></div>
+
   <footer class="footer">
-    <button type="button" class="continue-btn" id="btnContinue">和U宝继续聊 →</button>
+    <div class="footer__dock">
+      <button type="button" class="continue-btn" id="btnContinue">和U宝继续聊 →</button>
+      <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: 44px;
+              margin: 0;
+              padding: 0;
+              border: none;
+              border-radius: 999px;
+              background: linear-gradient(90deg, #ffb347 0%, #f47d1f 55%, #ff7849 100%);
+              box-shadow: 0 4px 12px rgba(244, 125, 31, 0.28);
+              color: #fff;
+              font-size: 16px;
+              font-weight: 600;
+              line-height: 44px;
+              text-align: center;
+              cursor: pointer;
+              -webkit-tap-highlight-color: transparent;
+            }
+          </style>
+          <button class="wx-open-app-btn">和U宝继续聊 →</button>
+        </script>
+      </wx-open-launch-app>
+    </div>
   </footer>
 
+  <script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
   <script>
     (function () {
-      var APP_DEEPLINK = "shopro://pages/aiSearchResult/index?pageType=home";
+      "use strict";
+
+      var API_BASE = "https://test.ailien.shop/alienStore";
+      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 + "shareAiConsult.html";
+      var WECHAT_JS_SAFE_HOSTS = ["uat.ailien.shop", "test.ailien.shop"];
+      var APP_ANDROID_PACKAGE = "com.alien.Udianzaizhe";
+      var APP_IOS_URL_SCHEME = "shopro://";
+      var APP_UNI_AI_PATH = "pages/aiSearchResult/index";
+
+      var sharePayloadCache = null;
+      var weChatJssdkConfigured = false;
+      var wxConfigSignRetriedBaseUrl = false;
+      var wxInitLastError = "";
+      var wxJssdkInitPromise = null;
 
       function escHtml(s) {
         return String(s || "")
@@ -160,6 +255,15 @@
           .replace(/"/g, "&quot;");
       }
 
+      function q(name) {
+        try {
+          var v = new URLSearchParams(location.search || "").get(name);
+          return v == null ? "" : String(v);
+        } catch (e) {
+          return "";
+        }
+      }
+
       function getQueryParam(name) {
         var m = new RegExp("[?&]" + name + "=([^&]*)").exec(window.location.search);
         if (!m) return "";
@@ -193,10 +297,10 @@
       }
 
       function formatDiscussionTitle(question) {
-        var q = String(question || "").trim();
-        if (!q) return "与AI助手对话";
-        if (q.slice(-3) === "的讨论") return q;
-        return q + "的讨论";
+        var qText = String(question || "").trim();
+        if (!qText) return "与AI助手对话";
+        if (qText.slice(-3) === "的讨论") return qText;
+        return qText + "的讨论";
       }
 
       function formatDate(ts) {
@@ -233,9 +337,11 @@
       }
 
       function render(data) {
+        sharePayloadCache = data || null;
         var main = document.getElementById("main");
         if (!data) {
           main.innerHTML = '<div class="empty">对话内容不存在或链接已失效</div>';
+          refreshWxLaunchTagAttrs();
           return;
         }
 
@@ -271,20 +377,505 @@
         }
 
         main.innerHTML = bodyHtml;
+        refreshWxLaunchTagAttrs();
+      }
+
+      function buildAiConsultOpenParams() {
+        var params = new URLSearchParams();
+        params.set("pageType", "home");
+        params.set("fromShareAiConsult", "1");
+        return params;
+      }
+
+      function buildAppDeepLink() {
+        var path = APP_UNI_AI_PATH;
+        var qs = buildAiConsultOpenParams().toString();
+        var root = APP_IOS_URL_SCHEME.replace(/\/$/, "");
+        return root + "/" + path + (qs ? "?" + qs : "");
+      }
+
+      function buildWeChatLaunchExtinfo() {
+        var qs = buildAiConsultOpenParams().toString();
+        var uniPage = qs ? APP_UNI_AI_PATH + "?" + qs : APP_UNI_AI_PATH;
+        return uniPage.length <= 1024 ? uniPage : APP_UNI_AI_PATH + "?pageType=home&fromShareAiConsult=1";
+      }
+
+      function isWeChatInAppBrowser() {
+        return /MicroMessenger/i.test(navigator.userAgent || "");
+      }
+
+      function isIOSWeChatBrowser() {
+        var ua = navigator.userAgent || "";
+        return /MicroMessenger/i.test(ua) && /iPhone|iPad|iPod/i.test(ua);
+      }
+
+      function isWxDebugOn() {
+        return q("wxDebug") === "1";
+      }
+
+      function isWxForceDebug() {
+        return q("wxForce") === "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);
+
+        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) {
+              throw new Error("getWxConfig 缺少 appId/timestamp/nonceStr/signature(见控制台)");
+            }
+            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");
+        });
+        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)) {
+          return Promise.resolve(false);
+        }
+        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;
+      }
+
+      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);
+          }
+        }
+        if (isIOSWeChatBrowser()) {
+          setTimeout(tick, 350);
+        } else {
+          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 showDownloadTip() {
+        var msg = "请到应用商店下载「U店在哪」";
+        if (typeof uni !== "undefined" && typeof uni.showToast === "function") {
+          uni.showToast({ title: msg, icon: "none", duration: 2500 });
+        } else {
+          window.alert(msg);
+        }
+      }
+
+      function showAppOpenFailTip(msg) {
+        var tip = msg || "未能打开 App,请确认已安装最新版「U店在哪」。";
+        if (typeof uni !== "undefined" && typeof uni.showToast === "function") {
+          uni.showToast({ title: msg, icon: "none", duration: 2800 });
+        } else if (isWeChatInAppBrowser()) {
+          window.alert(tip);
+        } else {
+          showFabToast(tip);
+        }
+      }
+
+      function launchAppDeepLink(deepLink) {
+        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) {}
+        try {
+          window.location.href = deepLink;
+        } catch (e2) {}
+      }
+
+      function tryOpenHBuilderAppViaScheme() {
+        var deepLink = buildAppDeepLink();
+
+        if (typeof plus !== "undefined" && plus.runtime) {
+          var installed = null;
+          try {
+            if (typeof plus.runtime.isApplicationExist === "function") {
+              installed = plus.runtime.isApplicationExist({
+                pname: APP_ANDROID_PACKAGE,
+                action: APP_IOS_URL_SCHEME,
+              });
+            }
+          } catch (e) {
+            console.warn(e);
+          }
+          try {
+            plus.runtime.openURL(deepLink);
+          } catch (e2) {
+            console.warn(e2);
+            if (installed === false) {
+              showDownloadTip();
+            }
+          }
+          return;
+        }
+
+        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 () {
+          finish();
+        }, 3200);
       }
 
-      function openApp() {
-        var start = Date.now();
-        window.location.href = APP_DEEPLINK;
-        setTimeout(function () {
-          if (Date.now() - start < 2800) {
-            window.location.href = "https://www.ailien.shop/";
+      function tryOpenApp() {
+        if (isWeChatInAppBrowser()) {
+          if (!weChatJssdkConfigured) {
+            initWeChatOpenLaunchApp(true).then(function (ok) {
+              if (!ok) {
+                showAppOpenFailTip(wxInitLastError || "微信唤起 App 初始化失败");
+              }
+            });
           }
-        }, 2500);
+          return;
+        }
+        tryOpenHBuilderAppViaScheme();
       }
 
-      document.getElementById("btnContinue").addEventListener("click", openApp);
+      if (isWeChatInAppBrowser()) {
+        document.body.classList.add("is-wechat");
+      }
 
+      bindWeChatLaunchTagEvents();
+      scheduleWeChatJssdkBootstrap();
+      document.getElementById("btnContinue").addEventListener("click", tryOpenApp);
       render(parsePayload());
     })();
   </script>