shareAiConsult.html 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="utf-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
  6. <meta name="format-detection" content="telephone=no">
  7. <title>与U宝对话</title>
  8. <style>
  9. :root {
  10. --bg: #f4f6fb;
  11. --text: #151515;
  12. --muted: #aaaaaa;
  13. --orange: #f47d1f;
  14. --safe-bottom: env(safe-area-inset-bottom, 0px);
  15. }
  16. * {
  17. margin: 0;
  18. padding: 0;
  19. box-sizing: border-box;
  20. }
  21. html {
  22. -webkit-tap-highlight-color: transparent;
  23. }
  24. body {
  25. font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei", sans-serif;
  26. background: var(--bg);
  27. color: var(--text);
  28. min-height: 100vh;
  29. padding-bottom: calc(88px + var(--safe-bottom));
  30. }
  31. .nav {
  32. position: sticky;
  33. top: 0;
  34. z-index: 10;
  35. display: flex;
  36. align-items: center;
  37. padding: 10px 12px;
  38. background: #fff;
  39. }
  40. .nav__close {
  41. width: 36px;
  42. height: 36px;
  43. border: none;
  44. background: transparent;
  45. font-size: 22px;
  46. line-height: 36px;
  47. color: #151515;
  48. flex-shrink: 0;
  49. }
  50. .nav__center {
  51. flex: 1;
  52. min-width: 0;
  53. text-align: center;
  54. padding: 0 8px;
  55. }
  56. .nav__title {
  57. font-size: 15px;
  58. font-weight: 600;
  59. color: #151515;
  60. white-space: nowrap;
  61. overflow: hidden;
  62. text-overflow: ellipsis;
  63. }
  64. .nav__url {
  65. margin-top: 2px;
  66. font-size: 11px;
  67. color: var(--muted);
  68. }
  69. .nav__side {
  70. width: 36px;
  71. flex-shrink: 0;
  72. }
  73. .hero {
  74. padding: 16px 15px 0;
  75. }
  76. .hero__title {
  77. font-size: 20px;
  78. font-weight: 700;
  79. line-height: 1.4;
  80. word-break: break-word;
  81. }
  82. .hero__meta {
  83. margin-top: 8px;
  84. font-size: 12px;
  85. color: var(--muted);
  86. line-height: 1.4;
  87. }
  88. .hero__divider {
  89. margin-top: 14px;
  90. height: 1px;
  91. background: rgba(170, 170, 170, 0.25);
  92. }
  93. .conversation {
  94. padding: 14px 15px 0;
  95. }
  96. .row {
  97. display: flex;
  98. margin-bottom: 12px;
  99. }
  100. .row--user {
  101. justify-content: flex-end;
  102. }
  103. .row--ai {
  104. justify-content: flex-start;
  105. }
  106. .user-bubble {
  107. max-width: 78%;
  108. padding: 10px 12px;
  109. background: var(--orange);
  110. border-radius: 6px;
  111. color: #fff;
  112. font-size: 15px;
  113. font-weight: 500;
  114. line-height: 1.5;
  115. white-space: pre-wrap;
  116. word-break: break-word;
  117. }
  118. .ai-card {
  119. width: 100%;
  120. padding: 14px 13px;
  121. background: #fff;
  122. border-radius: 8px;
  123. box-shadow: 0 1px 6px rgba(0, 0, 0, 0.04);
  124. }
  125. .ai-card__text {
  126. font-size: 14px;
  127. line-height: 1.65;
  128. white-space: pre-wrap;
  129. word-break: break-word;
  130. text-align: left;
  131. }
  132. .fallback-card {
  133. margin: 14px 15px 0;
  134. padding: 14px 13px;
  135. background: #fff;
  136. border-radius: 8px;
  137. }
  138. .empty {
  139. padding: 48px 20px;
  140. text-align: center;
  141. color: var(--muted);
  142. font-size: 14px;
  143. }
  144. .footer {
  145. position: fixed;
  146. left: 0;
  147. right: 0;
  148. bottom: 0;
  149. z-index: 20;
  150. padding: 10px 15px calc(10px + var(--safe-bottom));
  151. background: linear-gradient(180deg, rgba(244, 246, 251, 0) 0%, #f4f6fb 36%);
  152. }
  153. .continue-btn {
  154. width: 100%;
  155. height: 44px;
  156. border: none;
  157. border-radius: 999px;
  158. background: linear-gradient(90deg, #ffb347 0%, #f47d1f 55%, #ff7849 100%);
  159. box-shadow: 0 4px 12px rgba(244, 125, 31, 0.28);
  160. color: #fff;
  161. font-size: 16px;
  162. font-weight: 600;
  163. }
  164. </style>
  165. </head>
  166. <body>
  167. <header class="nav">
  168. <!-- <button type="button" class="nav__close" id="btnClose" aria-label="关闭">×</button>
  169. <div class="nav__center">
  170. <div class="nav__title" id="navTitle">与AI助手-U宝</div>
  171. <div class="nav__url">www.ailien.shop</div>
  172. </div> -->
  173. <div class="nav__side"></div>
  174. </header>
  175. <main id="main">
  176. <div class="empty">对话内容加载中…</div>
  177. </main>
  178. <footer class="footer">
  179. <button type="button" class="continue-btn" id="btnContinue">和U宝继续聊 →</button>
  180. </footer>
  181. <script>
  182. (function () {
  183. var SHARE_URL = "www.ailien.shop";
  184. var APP_DEEPLINK = "shopro://pages/aiSearchResult/index?pageType=home";
  185. function escHtml(s) {
  186. return String(s || "")
  187. .replace(/&/g, "&amp;")
  188. .replace(/</g, "&lt;")
  189. .replace(/>/g, "&gt;")
  190. .replace(/"/g, "&quot;");
  191. }
  192. function getQueryParam(name) {
  193. var m = new RegExp("[?&]" + name + "=([^&]*)").exec(window.location.search);
  194. if (!m) return "";
  195. try {
  196. return decodeURIComponent(m[1].replace(/\+/g, " "));
  197. } catch (e) {
  198. return "";
  199. }
  200. }
  201. function parsePayload() {
  202. var raw = getQueryParam("payload");
  203. if (!raw) return null;
  204. try {
  205. return JSON.parse(raw);
  206. } catch (e) {
  207. return null;
  208. }
  209. }
  210. function getFirstUserQuestion(messages) {
  211. var list = Array.isArray(messages) ? messages : [];
  212. for (var i = 0; i < list.length; i++) {
  213. var m = list[i];
  214. if (!m || m.role !== "user") continue;
  215. if (m.isImage) return "[图片]";
  216. var t = String(m.content || "").trim();
  217. if (t) return t;
  218. }
  219. return "";
  220. }
  221. function formatDiscussionTitle(question) {
  222. var q = String(question || "").trim();
  223. if (!q) return "与AI助手对话";
  224. if (q.slice(-3) === "的讨论") return q;
  225. return q + "的讨论";
  226. }
  227. function formatNavTitle(question) {
  228. var titled = formatDiscussionTitle(question);
  229. var maxLen = 14;
  230. var short = titled.length > maxLen ? titled.slice(0, maxLen) + "..." : titled;
  231. return short + "-U宝";
  232. }
  233. function formatDate(ts) {
  234. var d = ts ? new Date(Number(ts)) : new Date();
  235. if (isNaN(d.getTime())) d = new Date();
  236. return d.getFullYear() + "年" + (d.getMonth() + 1) + "月" + d.getDate() + "日";
  237. }
  238. function renderMessages(messages) {
  239. var html = "";
  240. var list = Array.isArray(messages) ? messages : [];
  241. for (var i = 0; i < list.length; i++) {
  242. var msg = list[i];
  243. if (!msg) continue;
  244. if (msg.role === "user") {
  245. var userText = msg.isImage ? "[图片]" : String(msg.content || "").trim();
  246. if (!userText) continue;
  247. html +=
  248. '<div class="row row--user"><div class="user-bubble">' +
  249. escHtml(userText) +
  250. "</div></div>";
  251. continue;
  252. }
  253. if (msg.role === "ai") {
  254. var aiText = String(msg.content || "").trim();
  255. if (!aiText) continue;
  256. html +=
  257. '<div class="row row--ai"><div class="ai-card"><div class="ai-card__text">' +
  258. escHtml(aiText) +
  259. "</div></div></div>";
  260. }
  261. }
  262. return html;
  263. }
  264. function render(data) {
  265. var main = document.getElementById("main");
  266. var navTitle = document.getElementById("navTitle");
  267. if (!data) {
  268. main.innerHTML = '<div class="empty">对话内容不存在或链接已失效</div>';
  269. return;
  270. }
  271. var messages = data.messages || [];
  272. var firstQuestion =
  273. String(data.firstQuestion || "").trim() || getFirstUserQuestion(messages);
  274. var pageTitle = formatDiscussionTitle(firstQuestion);
  275. var dateText = formatDate(data.shareTime);
  276. navTitle.textContent = formatNavTitle(firstQuestion);
  277. document.title = pageTitle;
  278. var bodyHtml =
  279. '<section class="hero">' +
  280. '<h1 class="hero__title">' +
  281. escHtml(pageTitle) +
  282. "</h1>" +
  283. '<p class="hero__meta">' +
  284. escHtml(dateText) +
  285. " · 内容由AI生成,不能完全保障真实</p>" +
  286. '<div class="hero__divider"></div>' +
  287. "</section>";
  288. var convHtml = renderMessages(messages);
  289. if (convHtml) {
  290. bodyHtml += '<section class="conversation">' + convHtml + "</section>";
  291. } else if (data.content) {
  292. bodyHtml +=
  293. '<section class="fallback-card"><div class="ai-card__text">' +
  294. escHtml(data.content) +
  295. "</div></section>";
  296. } else {
  297. bodyHtml += '<div class="empty">暂无对话内容</div>';
  298. }
  299. main.innerHTML = bodyHtml;
  300. }
  301. function openApp() {
  302. var start = Date.now();
  303. window.location.href = APP_DEEPLINK;
  304. setTimeout(function () {
  305. if (Date.now() - start < 2800) {
  306. window.location.href = "https://www.ailien.shop/";
  307. }
  308. }, 2500);
  309. }
  310. document.getElementById("btnClose").addEventListener("click", function () {
  311. if (window.history.length > 1) {
  312. window.history.back();
  313. } else if (typeof WeixinJSBridge !== "undefined") {
  314. WeixinJSBridge.call("closeWindow");
  315. } else {
  316. window.close();
  317. }
  318. });
  319. document.getElementById("btnContinue").addEventListener("click", openApp);
  320. render(parsePayload());
  321. })();
  322. </script>
  323. </body>
  324. </html>