index.html 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  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 / globalThis.isKnowledgeApi 及其消费方
  17. // (`cardURL`、`if (isKnowledgeApi) { 硬编码 cardInfo }`)——都是"写了没人读"的死代码,
  18. // 2026-09-28 按用户要求删除;`VITE_API` 本身保留(企业登录接口在用)
  19. // 新版聊天协议后端基础地址(POST /api/chat,SSE)
  20. globalThis.VITE_CHAT_API = "%VITE_CHAT_API%" || "";
  21. // DMS 数据管理服务(会话/问答记录/反馈)。token 由 vite 代理注入,前端不持有
  22. globalThis.VITE_DMS_API = "%VITE_DMS_API%" || "";
  23. globalThis.VITE_IP_API = "%VITE_IP_API%" || "";
  24. // [已移除] globalThis.isQingpuEnv = "MODE" === "qingpu";
  25. // 2026-09-24 用户要求「环境只保留开发和生产」:qingpu 模式已删除,这行本身也是死的
  26. // (写的是字面量 "MODE",永远不会等于 "qingpu";且全仓库没有任何地方读 isQingpuEnv)
  27. globalThis.__ZHAOSHANG_LLM_MODELS = ["zhaoshang", "zhaoshang_staging", "zhaoshang_safe"];
  28. globalThis.__isZhaoshangLLM = function (model) {
  29. return globalThis.__ZHAOSHANG_LLM_MODELS.indexOf(model || globalThis.llmModel || "") > -1;
  30. };
  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. // 远端 = **DMS 文件服务**里的那份(用户 2026-09-24:把 policies_public.v2.json 传到 DMS,
  119. // 那个路径就是新的"远端政策接口")。同源路径:dev 走 vite 代理、生产走 nginx。
  120. var remoteURL = "%VITE_POLICY_REMOTE_URL%";
  121. // 兜底 = 随包发布的本地副本(`public/merchant-agent/…`)
  122. var localFallbackURL = "/merchant-agent/policies_public.v2.json";
  123. function applyPolicies(data, source) {
  124. // 支持 { data: [...] } 和纯数组两种格式
  125. var list = Array.isArray(data) ? data : data.data;
  126. globalThis.policiesPublic = list || [];
  127. console.log(
  128. "%c[zhaoshang] 政策列表加载完成(" +
  129. source +
  130. "):" +
  131. globalThis.policiesPublic.length +
  132. " 条",
  133. "color:green"
  134. );
  135. }
  136. function loadPolicies(source) {
  137. return fetch(source.url)
  138. .then(function (response) {
  139. if (!response.ok) throw new Error("HTTP " + response.status);
  140. return response.json();
  141. })
  142. .then(function (data) {
  143. applyPolicies(data, source.label);
  144. });
  145. }
  146. // 顺序固定:**先远端(DMS),取不到再用本地兜底**(用户 2026-09-24 明确要求)。
  147. // 原先那个「开发环境本地优先」的开关(VITE_POLICY_LOCAL_FIRST)随旧远端一起删掉了 ——
  148. // 它当年是为了绕开"厂商远端那份没有市级政策id"的问题,现在远端换成我们自己传的 DMS 那份,
  149. // 两边内容一致,不再需要这个开关。
  150. var sources = [
  151. { url: remoteURL, label: "dms" },
  152. { url: localFallbackURL, label: "local" },
  153. ];
  154. loadPolicies(sources[0]).catch(function (err) {
  155. console.warn(
  156. "[zhaoshang] 政策列表(" + sources[0].label + ")加载失败,尝试另一个来源:",
  157. err
  158. );
  159. loadPolicies(sources[1]).catch(function (fallbackErr) {
  160. console.warn("[zhaoshang] 政策列表两个来源都加载失败:", fallbackErr);
  161. globalThis.policiesPublic = [];
  162. });
  163. });
  164. })();
  165. }
  166. document.title = globalThis.orgTitle;
  167. updateFavicon();
  168. initMessageChannel();
  169. // ⚠️ 这三行+addVersionInfo 是**加载遮罩的收尾信号**,删不得:
  170. // BusinessAssistant.vue 的 initSequence() 靠 `globalThis.hasSequence`(或等 `sequenceLoaded` 事件)
  171. // 来隐藏 #loading-cover —— 少了它页面会永远卡在"正在初始化,请稍候..."。
  172. // (原来它们挂在 `if (globalThis.isKnowledgeApi) { … }` 里、在硬编码 cardInfo 之后;
  173. // 2026-09-28 删那段死枝时**误删过一次**,现已移到这里无条件执行,语义与原来一致。)
  174. globalThis.hasSequence = true;
  175. document.dispatchEvent(new CustomEvent("dataLoaded"));
  176. document.dispatchEvent(new CustomEvent("sequenceLoaded"));
  177. addVersionInfo();
  178. })();
  179. (function () {
  180. var src = "//cdn.jsdelivr.net/npm/eruda";
  181. if (!/eruda=true/.test(window.location) && localStorage.getItem("active-eruda") != "true")
  182. return;
  183. document.write("<scr" + 'ipt src="' + src + '"></scr' + "ipt>");
  184. document.write("<scr" + "ipt>eruda.init();</scr" + "ipt>");
  185. })();
  186. //
  187. document.addEventListener("WeixinJSBridgeReady", () => {
  188. console.log("WeixinJSBridgeReady");
  189. // let video = document.getElementById("human-stream");
  190. // video && video.play();
  191. });
  192. </script>
  193. </head>
  194. <body>
  195. <div id="app"></div>
  196. <div id="loading-cover">
  197. <div id="cover">
  198. <!-- <div id="circle" data-text="&nbsp;"></div> -->
  199. <div id="result-text">正在初始化,请稍候...</div>
  200. <!-- 加载中,请稍候... -->
  201. </div>
  202. </div>
  203. <div id="error-info" style="display: none; opacity: 0">
  204. <div id="error-tips"></div>
  205. <div id="error-reload">重试</div>
  206. </div>
  207. <script>
  208. (function initLoadingAnimation() {
  209. var circle = document.querySelector("#circle");
  210. if (!circle) {
  211. return;
  212. }
  213. var text = circle.dataset.text;
  214. var characters = text.split("");
  215. var deltaAngle = 360 / characters.length;
  216. var distanceFromCenter = 110;
  217. characters.forEach(function (character, index) {
  218. var span = document.createElement("span");
  219. span.innerText = character;
  220. var rotateY = "rotateY(" + index * deltaAngle + "deg)";
  221. var translateZ = "translateZ(" + distanceFromCenter + "px)";
  222. span.style.transform = rotateY + " " + translateZ;
  223. circle.appendChild(span);
  224. });
  225. })();
  226. </script>
  227. <!-- [已删] 百度统计(hm.baidu.com/hm.js)——2026-09-28 用户确认删除:
  228. 数据进的是厂商账号(其站点配置 dm 指向 demo.heijingai.com),本项目从未用过它的数据,
  229. 且它是全站唯一往第三方发请求、且在青浦域名上写 cookie 的东西。理由与排查见
  230. harness/docs/reference/current-api-call-sites.md 的 #14 -->
  231. <script type="module" src="/src/main.ts"></script>
  232. <style>
  233. body {
  234. margin: 0px;
  235. background: #fff;
  236. }
  237. #loading-cover {
  238. width: 100%;
  239. height: 100%;
  240. position: fixed;
  241. top: 0;
  242. left: 0;
  243. z-index: 100;
  244. font-size: 14px;
  245. backdrop-filter: blur(40px);
  246. -webkit-backdrop-filter: blur(40px);
  247. background: rgba(0, 0, 0, 0.1);
  248. display: flex;
  249. justify-content: center;
  250. align-items: center;
  251. color: #232323;
  252. font-family:
  253. -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial,
  254. "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol",
  255. "Noto Color Emoji";
  256. }
  257. #cover {
  258. display: flex;
  259. flex-direction: column;
  260. }
  261. #circle {
  262. position: relative;
  263. transform-style: preserve-3d;
  264. animation: rotate-animation 8s linear infinite;
  265. }
  266. #circle span {
  267. position: absolute;
  268. top: 0;
  269. left: 0;
  270. }
  271. .blur-effect {
  272. animation: add-blur-effect 1s ease-in-out forwards !important;
  273. }
  274. #error-info {
  275. display: flex;
  276. flex-direction: column;
  277. justify-content: center;
  278. align-items: center;
  279. width: 100vw;
  280. height: 100vh;
  281. position: fixed;
  282. top: 0;
  283. left: 0;
  284. z-index: 101;
  285. transition: all 0.5s ease-in-out;
  286. }
  287. #error-tips {
  288. display: inline-flex;
  289. padding: 18px 32px;
  290. justify-content: center;
  291. align-items: center;
  292. border-radius: 20px;
  293. background: rgba(11, 12, 14, 0.8);
  294. font-family: Inter;
  295. font-size: 16px;
  296. font-style: normal;
  297. font-weight: 400;
  298. line-height: normal;
  299. color: #ffffff;
  300. transform: translateY(-100%);
  301. }
  302. #error-reload {
  303. display: flex;
  304. width: 162.5px;
  305. padding: 10px 16px;
  306. justify-content: center;
  307. align-items: center;
  308. border-radius: 14px;
  309. background: linear-gradient(95deg, rgba(74, 114, 255, 0.96) -6.43%, #925eff 98.24%);
  310. color: #fff;
  311. /* title/h5_medium */
  312. font-family: PingFang HK;
  313. font-size: 16px;
  314. font-style: normal;
  315. font-weight: 500;
  316. line-height: 24px;
  317. transform: translateY(500%);
  318. }
  319. @keyframes add-blur-effect {
  320. 0% {
  321. filter: blur(0px);
  322. -webkit-filter: blur(0px);
  323. }
  324. 50% {
  325. filter: blur(20px);
  326. -webkit-filter: blur(20px);
  327. }
  328. 100% {
  329. filter: blur(0px);
  330. -webkit-filter: blur(0px);
  331. }
  332. }
  333. @keyframes rotate-animation {
  334. 0% {
  335. transform: rotateY(360deg) rotateX(-12deg) rotateZ(12deg);
  336. }
  337. 50% {
  338. transform: rotateY(180deg) rotateX(12deg) rotateZ(-12deg);
  339. }
  340. 100% {
  341. transform: rotateY(0) rotateX(-12deg) rotateZ(12deg);
  342. }
  343. }
  344. </style>
  345. </body>
  346. </html>