index.html 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  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 addVersionInfo() {
  61. var version = "%VITE_VERSION%";
  62. window.humanCardVersion = version;
  63. var mode = "%MODE%";
  64. window.humanCardEnv = mode;
  65. console.log("%c@@@version===>", "color:green;font-size:15px", version, mode);
  66. document.title += " Ver. " + version;
  67. if (mode === "development") {
  68. document.title += " " + mode.toUpperCase();
  69. }
  70. }
  71. function initMessageChannel() {
  72. window.addEventListener("message", () => {
  73. if (event.data && event.data.type === "initMessage") {
  74. // 这里收到父页面发来的大文本
  75. console.log("收到父页面消息:", event.data.text);
  76. localStorage.setItem("initMessage", event.data.text);
  77. // ...你的业务处理...
  78. }
  79. });
  80. window.parent.postMessage(
  81. { type: "ready" },
  82. "%VITE_PARENT_ORIGIN%" // 指定父页面的 origin,务必不要写成 '*',防止安全隐患
  83. );
  84. }
  85. function updateFavicon() {
  86. var favicon =
  87. document.querySelector("#app-favicon") || document.querySelector('link[rel="icon"]');
  88. if (!favicon) {
  89. favicon = document.createElement("link");
  90. favicon.id = "app-favicon";
  91. favicon.rel = "icon";
  92. document.head.appendChild(favicon);
  93. }
  94. var isZhaoshang = globalThis.__isZhaoshangLLM(globalThis.llmModel);
  95. favicon.type = isZhaoshang ? "image/x-icon" : "image/svg+xml";
  96. favicon.href = isZhaoshang ? "/qs_favicon.ico" : "/vite.svg";
  97. }
  98. (function initSystem() {
  99. var requestedPolicyMode = getQueryObj("mode");
  100. var isZhaoshangMode =
  101. requestedPolicyMode === "zhaoshang" ||
  102. requestedPolicyMode === "zhaoshang_staging" ||
  103. requestedPolicyMode === "zhaoshang_safe" ||
  104. /^zhaoshang_staging_\d+$/.test(requestedPolicyMode || "");
  105. globalThis.policyMode = isZhaoshangMode ? requestedPolicyMode : "zhaoshang";
  106. {
  107. globalThis.card_id = 317;
  108. globalThis.orgTitle = "营商助手";
  109. globalThis.llmModel = globalThis.policyMode;
  110. globalThis.greeting =
  111. "您好!我是青浦区营商助手”智”小青。我新上岗不久,还在努力学习提升中,请多理解包涵。请问有什么可以帮助您?";
  112. const credit_code = getQueryObj("credit_code");
  113. if (credit_code) {
  114. globalThis.transmission = { credit_code };
  115. }
  116. // 预下载完整政策列表到内存中
  117. (function () {
  118. var downloadURL = "%VITE_DOWNLOAD_URL%" + "merchant-agent/policies_public.v2.json";
  119. var localFallbackURL = "/merchant-agent/policies_public.v2.json";
  120. function applyPolicies(data, source) {
  121. // 支持 { data: [...] } 和纯数组两种格式
  122. var list = Array.isArray(data) ? data : data.data;
  123. globalThis.policiesPublic = list || [];
  124. console.log(
  125. "%c[zhaoshang] 政策列表加载完成(" +
  126. source +
  127. "):" +
  128. globalThis.policiesPublic.length +
  129. " 条",
  130. "color:green"
  131. );
  132. }
  133. function loadPolicies(source) {
  134. return fetch(source.url)
  135. .then(function (response) {
  136. if (!response.ok) throw new Error("HTTP " + response.status);
  137. return response.json();
  138. })
  139. .then(function (data) {
  140. applyPolicies(data, source.label);
  141. });
  142. }
  143. // 开发环境优先读本地副本:远端那份 327 条全都没有 data.市级政策id,
  144. // 政策详情面板的「政策名称」会因此拼不出地址、点不开;本地这份有 261/262。
  145. // 由 VITE_POLICY_LOCAL_FIRST 控制(仅 .env.development 为 true)
  146. var localFirst = "%VITE_POLICY_LOCAL_FIRST%" === "true";
  147. var sources = localFirst
  148. ? [
  149. { url: localFallbackURL, label: "local" },
  150. { url: downloadURL, label: "remote" },
  151. ]
  152. : [
  153. { url: downloadURL, label: "remote" },
  154. { url: localFallbackURL, label: "local" },
  155. ];
  156. loadPolicies(sources[0]).catch(function (err) {
  157. console.warn(
  158. "[zhaoshang] 政策列表(" + sources[0].label + ")加载失败,尝试另一个来源:",
  159. err
  160. );
  161. loadPolicies(sources[1]).catch(function (fallbackErr) {
  162. console.warn("[zhaoshang] 政策列表两个来源都加载失败:", fallbackErr);
  163. globalThis.policiesPublic = [];
  164. });
  165. });
  166. })();
  167. }
  168. document.title = globalThis.orgTitle;
  169. updateFavicon();
  170. initMessageChannel();
  171. if (globalThis.isKnowledgeApi) {
  172. document.title = globalThis.orgTitle;
  173. globalThis.cardInfo = {
  174. id: 100161,
  175. human_gender: 1,
  176. card_id: 297,
  177. card_name: "政策解读",
  178. name: "政策解读",
  179. background: "",
  180. url: "https://timg.metamaker.cn/mms_threejs_res/1c792f578384242a92f1fb2e1ad8f70d",
  181. human_avatar:
  182. "//img.metamaker.cn/resources/52116488a01573be4d6754d7de9090464a54ccc3.png",
  183. title: "1",
  184. duty: "1",
  185. org: "1",
  186. mobile: "1",
  187. wx_id: "1",
  188. logo: "",
  189. tts_args: {
  190. speed: 30,
  191. volume: 100,
  192. voice_name: "S_x8pOUDlm1",
  193. },
  194. share_conf: {
  195. qrcode: "https://img.metamaker.cn/human_card/b380126e1c138a4c1373f1b6bc8e1a4b.jpg",
  196. share_img: "https://img.metamaker.cn/human_card/3ce6f8c86bd3525d113dde450aeadbd7.jpg",
  197. share_title: "政策解读",
  198. card_img: "https://img.metamaker.cn/human_card/91c4f4e2ccf393168f01761e54802351.jpg",
  199. },
  200. func_sets: {
  201. opt_area: [
  202. {
  203. name: "拨打电话",
  204. type: 1,
  205. alias: "",
  206. check: false,
  207. },
  208. {
  209. name: "复制微信",
  210. type: 2,
  211. alias: "",
  212. check: false,
  213. },
  214. {
  215. name: "查看文档",
  216. type: 3,
  217. alias: "",
  218. check: false,
  219. },
  220. ],
  221. question_guide: {
  222. guess_ask: {
  223. check: false,
  224. value: "",
  225. },
  226. common_problem: {
  227. check: false,
  228. value: [],
  229. },
  230. },
  231. hide_name_brand: 0,
  232. human_interaction: {
  233. welcome: {
  234. check: true,
  235. value: globalThis.greeting,
  236. },
  237. },
  238. },
  239. dialog_sets: {
  240. strict: false,
  241. only_kl: false,
  242. unable_answer: {
  243. text: "很抱歉,我不太理解您的意思,请问您想了解的是什么问题呢?",
  244. check: false,
  245. amount: 1,
  246. },
  247. belief_threshold: 0.88,
  248. },
  249. };
  250. globalThis.hasSequence = true;
  251. document.dispatchEvent(new CustomEvent("dataLoaded"));
  252. document.dispatchEvent(new CustomEvent("sequenceLoaded"));
  253. addVersionInfo();
  254. }
  255. })();
  256. (function () {
  257. var src = "//cdn.jsdelivr.net/npm/eruda";
  258. if (!/eruda=true/.test(window.location) && localStorage.getItem("active-eruda") != "true")
  259. return;
  260. document.write("<scr" + 'ipt src="' + src + '"></scr' + "ipt>");
  261. document.write("<scr" + "ipt>eruda.init();</scr" + "ipt>");
  262. })();
  263. //
  264. document.addEventListener("WeixinJSBridgeReady", () => {
  265. console.log("WeixinJSBridgeReady");
  266. // let video = document.getElementById("human-stream");
  267. // video && video.play();
  268. });
  269. </script>
  270. </head>
  271. <body>
  272. <div id="app"></div>
  273. <div id="loading-cover">
  274. <div id="cover">
  275. <!-- <div id="circle" data-text="&nbsp;"></div> -->
  276. <div id="result-text">正在初始化,请稍候...</div>
  277. <!-- 加载中,请稍候... -->
  278. </div>
  279. </div>
  280. <div id="error-info" style="display: none; opacity: 0">
  281. <div id="error-tips"></div>
  282. <div id="error-reload">重试</div>
  283. </div>
  284. <script>
  285. (function initLoadingAnimation() {
  286. var circle = document.querySelector("#circle");
  287. if (!circle) {
  288. return;
  289. }
  290. var text = circle.dataset.text;
  291. var characters = text.split("");
  292. var deltaAngle = 360 / characters.length;
  293. var distanceFromCenter = 110;
  294. characters.forEach(function (character, index) {
  295. var span = document.createElement("span");
  296. span.innerText = character;
  297. var rotateY = "rotateY(" + index * deltaAngle + "deg)";
  298. var translateZ = "translateZ(" + distanceFromCenter + "px)";
  299. span.style.transform = rotateY + " " + translateZ;
  300. circle.appendChild(span);
  301. });
  302. })();
  303. </script>
  304. <script>
  305. var _hmt = _hmt || [];
  306. (function () {
  307. var hm = document.createElement("script");
  308. hm.src = "https://hm.baidu.com/hm.js?d6307cb011c91628ce6be043f40d510a";
  309. var s = document.getElementsByTagName("script")[0];
  310. s.parentNode.insertBefore(hm, s);
  311. })();
  312. </script>
  313. <script type="module" src="/src/main.ts"></script>
  314. <style>
  315. body {
  316. margin: 0px;
  317. background: #fff;
  318. }
  319. #loading-cover {
  320. width: 100%;
  321. height: 100%;
  322. position: fixed;
  323. top: 0;
  324. left: 0;
  325. z-index: 100;
  326. font-size: 14px;
  327. backdrop-filter: blur(40px);
  328. -webkit-backdrop-filter: blur(40px);
  329. background: rgba(0, 0, 0, 0.1);
  330. display: flex;
  331. justify-content: center;
  332. align-items: center;
  333. color: #232323;
  334. font-family:
  335. -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial,
  336. "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol",
  337. "Noto Color Emoji";
  338. }
  339. #cover {
  340. display: flex;
  341. flex-direction: column;
  342. }
  343. #circle {
  344. position: relative;
  345. transform-style: preserve-3d;
  346. animation: rotate-animation 8s linear infinite;
  347. }
  348. #circle span {
  349. position: absolute;
  350. top: 0;
  351. left: 0;
  352. }
  353. .blur-effect {
  354. animation: add-blur-effect 1s ease-in-out forwards !important;
  355. }
  356. #error-info {
  357. display: flex;
  358. flex-direction: column;
  359. justify-content: center;
  360. align-items: center;
  361. width: 100vw;
  362. height: 100vh;
  363. position: fixed;
  364. top: 0;
  365. left: 0;
  366. z-index: 101;
  367. transition: all 0.5s ease-in-out;
  368. }
  369. #error-tips {
  370. display: inline-flex;
  371. padding: 18px 32px;
  372. justify-content: center;
  373. align-items: center;
  374. border-radius: 20px;
  375. background: rgba(11, 12, 14, 0.8);
  376. font-family: Inter;
  377. font-size: 16px;
  378. font-style: normal;
  379. font-weight: 400;
  380. line-height: normal;
  381. color: #ffffff;
  382. transform: translateY(-100%);
  383. }
  384. #error-reload {
  385. display: flex;
  386. width: 162.5px;
  387. padding: 10px 16px;
  388. justify-content: center;
  389. align-items: center;
  390. border-radius: 14px;
  391. background: linear-gradient(95deg, rgba(74, 114, 255, 0.96) -6.43%, #925eff 98.24%);
  392. color: #fff;
  393. /* title/h5_medium */
  394. font-family: PingFang HK;
  395. font-size: 16px;
  396. font-style: normal;
  397. font-weight: 500;
  398. line-height: 24px;
  399. transform: translateY(500%);
  400. }
  401. @keyframes add-blur-effect {
  402. 0% {
  403. filter: blur(0px);
  404. -webkit-filter: blur(0px);
  405. }
  406. 50% {
  407. filter: blur(20px);
  408. -webkit-filter: blur(20px);
  409. }
  410. 100% {
  411. filter: blur(0px);
  412. -webkit-filter: blur(0px);
  413. }
  414. }
  415. @keyframes rotate-animation {
  416. 0% {
  417. transform: rotateY(360deg) rotateX(-12deg) rotateZ(12deg);
  418. }
  419. 50% {
  420. transform: rotateY(180deg) rotateX(12deg) rotateZ(-12deg);
  421. }
  422. 100% {
  423. transform: rotateY(0) rotateX(-12deg) rotateZ(12deg);
  424. }
  425. }
  426. </style>
  427. </body>
  428. </html>