index.html 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584
  1. <!doctype html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <link id="app-favicon" rel="icon" type="image/svg+xml" href="/vite.svg" />
  6. <meta
  7. name="viewport"
  8. content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no"
  9. />
  10. <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
  11. <meta http-equiv="Pragma" content="no-cache" />
  12. <meta http-equiv="Expires" content="0" />
  13. <title></title>
  14. <script>
  15. let globalThis = window;
  16. globalThis.modelServer = "%VITE_API%" || "";
  17. globalThis.isKnowledgeApi = "%VITE_USE_KNOWLEDGE_API%" === "true";
  18. // 新版聊天协议后端基础地址(POST /api/chat,SSE)
  19. globalThis.VITE_CHAT_API = "%VITE_CHAT_API%" || "";
  20. globalThis.isQingpuEnv = "MODE" === "qingpu";
  21. globalThis.__ZHAOSHANG_LLM_MODELS = ["zhaoshang", "zhaoshang_staging", "zhaoshang_safe"];
  22. globalThis.__isZhaoshangLLM = function (model) {
  23. return globalThis.__ZHAOSHANG_LLM_MODELS.indexOf(model || globalThis.llmModel || "") > -1;
  24. };
  25. var cardURL = globalThis.modelServer;
  26. function getQueryObj(querykey) {
  27. if (!querykey || typeof querykey !== "string") {
  28. console.warn("Invalid querykey provided to getQueryObj");
  29. return "";
  30. }
  31. var hash = location.hash;
  32. if (!hash || hash.indexOf("?") === -1) {
  33. console.warn("No query parameters found in URL hash");
  34. return "";
  35. }
  36. var queryString = hash.split("?")[1];
  37. if (!queryString) {
  38. console.warn('No query string found after "?" in URL hash');
  39. return "";
  40. }
  41. var arr = queryString.split("#")[0].split("&");
  42. var resObj = {};
  43. arr.forEach(function (item) {
  44. if (item) {
  45. var splitItem = item.split("=");
  46. var key = splitItem[0];
  47. var value = splitItem[1] || "";
  48. if (key) {
  49. resObj[key] = decodeURIComponent(value);
  50. }
  51. }
  52. });
  53. return resObj.hasOwnProperty(querykey) ? resObj[querykey] : null;
  54. }
  55. function changeLoadingText(text) {
  56. document.querySelector("#loading-cover").classList.add("blur-effect");
  57. setTimeout(function () {
  58. var loadingCover = document.querySelector("#result-text");
  59. loadingCover.innerText = text;
  60. var circle = document.querySelector("#circle");
  61. if (circle) {
  62. circle.style.display = "none";
  63. }
  64. }, 500);
  65. }
  66. function showReloadView(text) {
  67. document.getElementById("error-info").style.display = "flex";
  68. document.querySelector("#error-tips").innerText = text;
  69. setTimeout(function () {
  70. document.getElementById("error-info").style.opacity = 1;
  71. document.getElementById("error-reload").addEventListener("click", function () {
  72. location.reload();
  73. });
  74. }, 100);
  75. }
  76. function addVersionInfo() {
  77. var version = "%VITE_VERSION%";
  78. window.humanCardVersion = version;
  79. var mode = "%MODE%";
  80. window.humanCardEnv = mode;
  81. console.log("%c@@@version===>", "color:green;font-size:15px", version, mode);
  82. document.title += " Ver. " + version;
  83. if (["development", "test"].includes(mode)) {
  84. document.title += " " + mode.toUpperCase();
  85. }
  86. }
  87. function randomString() {
  88. var chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
  89. var length = 32;
  90. var result = "";
  91. for (var i = length; i > 0; --i) result += chars[Math.floor(Math.random() * chars.length)];
  92. return result;
  93. }
  94. function initToken() {
  95. var openId = localStorage.getItem("openId") || randomString();
  96. localStorage.setItem("openId", openId);
  97. var requestOptions = {
  98. method: "POST",
  99. headers: {
  100. "Content-Type": "application/json",
  101. },
  102. body: JSON.stringify({
  103. openid: openId,
  104. mobile: "17",
  105. nickname: globalThis.orgTitle,
  106. app_key: "%VITE_APP_KEY%",
  107. }),
  108. };
  109. var url = globalThis.modelServer + "/open/openid_to_token";
  110. fetch(url, requestOptions)
  111. .then((response) => response.json())
  112. .then((result) => {
  113. console.log(result.ret.token);
  114. globalThis.token = result.ret.token;
  115. // globalThis.token =
  116. // "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyaWQiOjMxNjA1MSwiZXhwIjoxNzM2NDE1MDAyLCJpYXQiOjE3MzU1NTEwMDIsImlzcyI6ImFpLWh1bWFuLW1pbmkiLCJzY29wZSI6IiJ9.jYvLswI5NQaeL8W6Q0TQm7sdz4EtqyLRNVjuuKFtdO0";
  117. // globalThis.mini_id = 10156;
  118. initCardInfo();
  119. })
  120. .catch((error) => console.log("error", error));
  121. }
  122. function initCardInfo() {
  123. var token = globalThis.token;
  124. var mini = globalThis.mini_id;
  125. var card_id = globalThis.card_id;
  126. // if (!token || (!card_id && !mini)) {
  127. // return;
  128. // } else {
  129. // sessionStorage.setItem("token", token);
  130. // }
  131. var url = cardURL + "/mini/info";
  132. let params = [];
  133. if (mini) {
  134. params.push("mini=" + mini);
  135. }
  136. if (card_id) {
  137. params.push("card=" + card_id);
  138. }
  139. url += "?" + params.join("&");
  140. fetch(url, {
  141. method: "get",
  142. headers: {
  143. "Content-Type": "application/x-www-form-urlencoded",
  144. Authorization: token,
  145. },
  146. })
  147. .then(function (response) {
  148. return response.json();
  149. })
  150. .then(function (result) {
  151. if (result.err_code === -1) {
  152. globalThis.showReloadView(result.err_msg);
  153. return;
  154. }
  155. if (result.err_code !== 0) {
  156. globalThis.changeLoadingText(result.err_msg);
  157. return;
  158. }
  159. if (result.ret.hasOwnProperty("card_id") && result.ret.card_id === 0) {
  160. globalThis.changeLoadingText("助手不存在,请在管理后台中创建");
  161. return;
  162. }
  163. globalThis.cardInfo = result.ret;
  164. globalThis.cardInfo.tts_args = {
  165. speed: 30,
  166. volume: 100,
  167. voice_name: "S_x8pOUDlm1",
  168. };
  169. globalThis.hasSequence = true;
  170. // globalThis.token = data.ret;
  171. document.dispatchEvent(new CustomEvent("dataLoaded"));
  172. document.dispatchEvent(new CustomEvent("sequenceLoaded"));
  173. addVersionInfo();
  174. })
  175. .catch(function (err) {
  176. console.warn(err);
  177. addVersionInfo();
  178. globalThis.changeLoadingText(err);
  179. });
  180. }
  181. function initMessageChannel() {
  182. window.addEventListener("message", () => {
  183. if (event.data && event.data.type === "initMessage") {
  184. // 这里收到父页面发来的大文本
  185. console.log("收到父页面消息:", event.data.text);
  186. localStorage.setItem("initMessage", event.data.text);
  187. // ...你的业务处理...
  188. }
  189. });
  190. window.parent.postMessage(
  191. { type: "ready" },
  192. "%VITE_PARENT_ORIGIN%" // 指定父页面的 origin,务必不要写成 '*',防止安全隐患
  193. );
  194. }
  195. function updateFavicon() {
  196. var favicon =
  197. document.querySelector("#app-favicon") || document.querySelector('link[rel="icon"]');
  198. if (!favicon) {
  199. favicon = document.createElement("link");
  200. favicon.id = "app-favicon";
  201. favicon.rel = "icon";
  202. document.head.appendChild(favicon);
  203. }
  204. var isZhaoshang = globalThis.__isZhaoshangLLM(globalThis.llmModel);
  205. favicon.type = isZhaoshang ? "image/x-icon" : "image/svg+xml";
  206. favicon.href = isZhaoshang ? "/qs_favicon.ico" : "/vite.svg";
  207. }
  208. (function initSystem() {
  209. let isLanMode = globalThis.isQingpuEnv;
  210. globalThis.isXF = false;
  211. var requestedPolicyMode = getQueryObj("mode");
  212. var isZhaoshangMode =
  213. requestedPolicyMode === "zhaoshang" ||
  214. requestedPolicyMode === "zhaoshang_staging" ||
  215. requestedPolicyMode === "zhaoshang_safe" ||
  216. /^zhaoshang_staging_\d+$/.test(requestedPolicyMode || "");
  217. globalThis.policyMode = isZhaoshangMode ? requestedPolicyMode : "zhaoshang";
  218. {
  219. globalThis.card_id = 317;
  220. globalThis.orgTitle = "营商助手";
  221. globalThis.llmModel = globalThis.policyMode;
  222. globalThis.greeting =
  223. "您好!我是青浦区营商助手”智”小青。我新上岗不久,还在努力学习提升中,请多理解包涵。请问有什么可以帮助您?";
  224. const credit_code = getQueryObj("credit_code");
  225. if (credit_code) {
  226. globalThis.transmission = { credit_code };
  227. }
  228. // 预下载完整政策列表到内存中
  229. (function () {
  230. var downloadURL = "%VITE_DOWNLOAD_URL%" + "merchant-agent/policies_public.v1.json";
  231. var localFallbackURL = "/merchant-agent/policies_public.v1.json";
  232. function applyPolicies(data, source) {
  233. // 支持 { data: [...] } 和纯数组两种格式
  234. var list = Array.isArray(data) ? data : data.data;
  235. globalThis.policiesPublic = list || [];
  236. console.log(
  237. "%c[zhaoshang] 政策列表加载完成(" +
  238. source +
  239. "):" +
  240. globalThis.policiesPublic.length +
  241. " 条",
  242. "color:green"
  243. );
  244. }
  245. function loadPolicies(url, isFallback) {
  246. return fetch(url)
  247. .then(function (response) {
  248. if (!response.ok) throw new Error("HTTP " + response.status);
  249. return response.json();
  250. })
  251. .then(function (data) {
  252. applyPolicies(data, isFallback ? "local" : "remote");
  253. });
  254. }
  255. loadPolicies(downloadURL, false).catch(function (err) {
  256. console.warn("[zhaoshang] 政策列表下载失败,尝试本地兜底:", err);
  257. loadPolicies(localFallbackURL, true).catch(function (fallbackErr) {
  258. console.warn("[zhaoshang] 本地政策列表也加载失败:", fallbackErr);
  259. globalThis.policiesPublic = [];
  260. });
  261. });
  262. })();
  263. }
  264. document.title = globalThis.orgTitle;
  265. updateFavicon();
  266. initMessageChannel();
  267. if (globalThis.isKnowledgeApi) {
  268. document.title = globalThis.orgTitle;
  269. globalThis.cardInfo = {
  270. id: 100161,
  271. human_gender: 1,
  272. card_id: 297,
  273. card_name: "政策解读",
  274. name: "政策解读",
  275. background: "",
  276. url: "https://timg.metamaker.cn/mms_threejs_res/1c792f578384242a92f1fb2e1ad8f70d",
  277. human_avatar:
  278. "//img.metamaker.cn/resources/52116488a01573be4d6754d7de9090464a54ccc3.png",
  279. title: "1",
  280. duty: "1",
  281. org: "1",
  282. mobile: "1",
  283. wx_id: "1",
  284. logo: "",
  285. tts_args: {
  286. speed: 30,
  287. volume: 100,
  288. voice_name: "S_x8pOUDlm1",
  289. },
  290. share_conf: {
  291. qrcode: "https://img.metamaker.cn/human_card/b380126e1c138a4c1373f1b6bc8e1a4b.jpg",
  292. share_img: "https://img.metamaker.cn/human_card/3ce6f8c86bd3525d113dde450aeadbd7.jpg",
  293. share_title: "政策解读",
  294. card_img: "https://img.metamaker.cn/human_card/91c4f4e2ccf393168f01761e54802351.jpg",
  295. },
  296. func_sets: {
  297. opt_area: [
  298. {
  299. name: "拨打电话",
  300. type: 1,
  301. alias: "",
  302. check: false,
  303. },
  304. {
  305. name: "复制微信",
  306. type: 2,
  307. alias: "",
  308. check: false,
  309. },
  310. {
  311. name: "查看文档",
  312. type: 3,
  313. alias: "",
  314. check: false,
  315. },
  316. ],
  317. question_guide: {
  318. guess_ask: {
  319. check: false,
  320. value: "",
  321. },
  322. common_problem: {
  323. check: false,
  324. value: [],
  325. },
  326. },
  327. hide_name_brand: 0,
  328. human_interaction: {
  329. welcome: {
  330. check: true,
  331. value: globalThis.greeting,
  332. },
  333. },
  334. },
  335. dialog_sets: {
  336. strict: false,
  337. only_kl: false,
  338. unable_answer: {
  339. text: "很抱歉,我不太理解您的意思,请问您想了解的是什么问题呢?",
  340. check: false,
  341. amount: 1,
  342. },
  343. belief_threshold: 0.88,
  344. },
  345. };
  346. globalThis.hasSequence = true;
  347. document.dispatchEvent(new CustomEvent("dataLoaded"));
  348. document.dispatchEvent(new CustomEvent("sequenceLoaded"));
  349. addVersionInfo();
  350. } else {
  351. initToken();
  352. }
  353. })();
  354. (function () {
  355. var src = "//cdn.jsdelivr.net/npm/eruda";
  356. if (!/eruda=true/.test(window.location) && localStorage.getItem("active-eruda") != "true")
  357. return;
  358. document.write("<scr" + 'ipt src="' + src + '"></scr' + "ipt>");
  359. document.write("<scr" + "ipt>eruda.init();</scr" + "ipt>");
  360. })();
  361. //
  362. document.addEventListener("WeixinJSBridgeReady", () => {
  363. console.log("WeixinJSBridgeReady");
  364. // let video = document.getElementById("human-stream");
  365. // video && video.play();
  366. });
  367. </script>
  368. </head>
  369. <body>
  370. <div id="app"></div>
  371. <div id="loading-cover">
  372. <div id="cover">
  373. <!-- <div id="circle" data-text="&nbsp;"></div> -->
  374. <div id="result-text">正在初始化,请稍候...</div>
  375. <!-- 加载中,请稍候... -->
  376. </div>
  377. </div>
  378. <div id="error-info" style="display: none; opacity: 0">
  379. <div id="error-tips"></div>
  380. <div id="error-reload">重试</div>
  381. </div>
  382. <script>
  383. (function initLoadingAnimation() {
  384. var circle = document.querySelector("#circle");
  385. if (!circle) {
  386. return;
  387. }
  388. var text = circle.dataset.text;
  389. var characters = text.split("");
  390. var deltaAngle = 360 / characters.length;
  391. var distanceFromCenter = 110;
  392. characters.forEach(function (character, index) {
  393. var span = document.createElement("span");
  394. span.innerText = character;
  395. var rotateY = "rotateY(" + index * deltaAngle + "deg)";
  396. var translateZ = "translateZ(" + distanceFromCenter + "px)";
  397. span.style.transform = rotateY + " " + translateZ;
  398. circle.appendChild(span);
  399. });
  400. })();
  401. </script>
  402. <script>
  403. var _hmt = _hmt || [];
  404. (function () {
  405. var hm = document.createElement("script");
  406. hm.src = "https://hm.baidu.com/hm.js?d6307cb011c91628ce6be043f40d510a";
  407. var s = document.getElementsByTagName("script")[0];
  408. s.parentNode.insertBefore(hm, s);
  409. })();
  410. </script>
  411. <script type="module" src="/src/main.ts"></script>
  412. <style>
  413. body {
  414. margin: 0px;
  415. background: #fff;
  416. }
  417. #loading-cover {
  418. width: 100%;
  419. height: 100%;
  420. position: fixed;
  421. top: 0;
  422. left: 0;
  423. z-index: 100;
  424. font-size: 14px;
  425. backdrop-filter: blur(40px);
  426. -webkit-backdrop-filter: blur(40px);
  427. background: rgba(0, 0, 0, 0.1);
  428. display: flex;
  429. justify-content: center;
  430. align-items: center;
  431. color: #232323;
  432. font-family:
  433. -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial,
  434. "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol",
  435. "Noto Color Emoji";
  436. }
  437. #cover {
  438. display: flex;
  439. flex-direction: column;
  440. }
  441. #circle {
  442. position: relative;
  443. transform-style: preserve-3d;
  444. animation: rotate-animation 8s linear infinite;
  445. }
  446. #circle span {
  447. position: absolute;
  448. top: 0;
  449. left: 0;
  450. }
  451. .blur-effect {
  452. animation: add-blur-effect 1s ease-in-out forwards !important;
  453. }
  454. #error-info {
  455. display: flex;
  456. flex-direction: column;
  457. justify-content: center;
  458. align-items: center;
  459. width: 100vw;
  460. height: 100vh;
  461. position: fixed;
  462. top: 0;
  463. left: 0;
  464. z-index: 101;
  465. transition: all 0.5s ease-in-out;
  466. }
  467. #error-tips {
  468. display: inline-flex;
  469. padding: 18px 32px;
  470. justify-content: center;
  471. align-items: center;
  472. border-radius: 20px;
  473. background: rgba(11, 12, 14, 0.8);
  474. font-family: Inter;
  475. font-size: 16px;
  476. font-style: normal;
  477. font-weight: 400;
  478. line-height: normal;
  479. color: #ffffff;
  480. transform: translateY(-100%);
  481. }
  482. #error-reload {
  483. display: flex;
  484. width: 162.5px;
  485. padding: 10px 16px;
  486. justify-content: center;
  487. align-items: center;
  488. border-radius: 14px;
  489. background: linear-gradient(95deg, rgba(74, 114, 255, 0.96) -6.43%, #925eff 98.24%);
  490. color: #fff;
  491. /* title/h5_medium */
  492. font-family: PingFang HK;
  493. font-size: 16px;
  494. font-style: normal;
  495. font-weight: 500;
  496. line-height: 24px;
  497. transform: translateY(500%);
  498. }
  499. @keyframes add-blur-effect {
  500. 0% {
  501. filter: blur(0px);
  502. -webkit-filter: blur(0px);
  503. }
  504. 50% {
  505. filter: blur(20px);
  506. -webkit-filter: blur(20px);
  507. }
  508. 100% {
  509. filter: blur(0px);
  510. -webkit-filter: blur(0px);
  511. }
  512. }
  513. @keyframes rotate-animation {
  514. 0% {
  515. transform: rotateY(360deg) rotateX(-12deg) rotateZ(12deg);
  516. }
  517. 50% {
  518. transform: rotateY(180deg) rotateX(12deg) rotateZ(-12deg);
  519. }
  520. 100% {
  521. transform: rotateY(0) rotateX(-12deg) rotateZ(12deg);
  522. }
  523. }
  524. </style>
  525. </body>
  526. </html>