index.html 20 KB

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