You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2844 lines
71 KiB

6 months ago
4 weeks ago
4 weeks ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
1 month ago
6 months ago
6 months ago
6 days ago
6 days ago
6 days ago
6 days ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 days ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
6 days ago
4 weeks ago
6 months ago
6 months ago
6 days ago
3 months ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
6 days ago
6 days ago
6 days ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 days ago
6 days ago
6 days ago
6 days ago
6 days ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
6 days ago
6 days ago
6 days ago
6 days ago
6 days ago
4 weeks ago
6 days ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
  1. <script setup>
  2. // 导入
  3. import { ref, computed, onMounted, watch, nextTick, onUnmounted, h } from "vue";
  4. import { setHeight } from "../utils/setHeight";
  5. import {
  6. getUserCountAPI,
  7. showExchangeAPI,
  8. godExchangeAPI,
  9. exchangeAPI,
  10. getGoldCoinAPI,
  11. } from "../api/AIxiaocaishen";
  12. import { ElMessage } from "element-plus";
  13. import AIchat from "./AIchat.vue";
  14. import AIfind from "./AIfind.vue";
  15. import Feedback from "./Feedback.vue";
  16. import Announcement from "./Announcement.vue";
  17. import { useAppBridge } from "../assets/js/useAppBridge.js";
  18. import { useDataStore } from "@/store/dataList.js";
  19. import { useChatStore } from "../store/chat";
  20. import { useEmotionAudioStore } from "../store/emotionAudio";
  21. import { useAudioStore } from "../store/audio";
  22. import _ from "lodash";
  23. import logo from "../assets/img/homePage/logo.png";
  24. import madeInHL from "../assets/img/homePage/madeInHL.png";
  25. import getCountAll from "../assets/img/homePage/get-count-all.png";
  26. import announcementBtn from "../assets/img/homePage/announcement.png";
  27. import thinkActive from "../assets/img/homePage/tail/think-active.png";
  28. import thinkNoActive from "../assets/img/homePage/tail/think-no-active.png";
  29. import languageBtn from "../assets/img/homePage/tail/language.png";
  30. import dbqbButton01 from "../assets/img/AiEmotion/dbqb-button01.png";
  31. import dbqbButton02 from "../assets/img/AiEmotion/dbqb-button02.png";
  32. import emotionButton01 from "../assets/img/AiEmotion/emotion-button01.png";
  33. import emotionButton02 from "../assets/img/AiEmotion/emotion-button02.png";
  34. import voice from "../assets/img/homePage/tail/voice.png";
  35. import voiceNoActive from "../assets/img/homePage/tail/voice-no-active.png";
  36. import sendBtn from "../assets/img/homePage/tail/send.png";
  37. import msgBtn from "../assets/img/homePage/tail/msg.png";
  38. import feedbackBtn from "../assets/img/Feedback/feedbackBtn.png";
  39. import back from "../assets/img/Feedback/back.png";
  40. import AiEmotion from "./AiEmotion.vue";
  41. import HistoryRecord from "./components/HistoryRecord.vue";
  42. // import VConsole from "vconsole";
  43. // const vConsole = new VConsole();
  44. const isMobile = ref(null);
  45. // 获取 AiEmotion 组件的 ref
  46. const aiEmotionRef = ref(null);
  47. // 获取历史记录组件的 ref
  48. const historyRecordRef = ref(null);
  49. // import { useUserStore } from "../store/userPessionCode.js";
  50. const { getQueryVariable, setActiveTabIndex, getUserInfo } = useDataStore();
  51. const dataStore = useDataStore();
  52. const chatStore = useChatStore();
  53. // 变量
  54. // 音频管理
  55. const emotionAudioStore = useEmotionAudioStore();
  56. const audioStore = useAudioStore();
  57. // 根据当前页面类型获取对应的音频store
  58. const getCurrentAudioStore = () => {
  59. return activeTab.value === "AiEmotion" ? emotionAudioStore : audioStore;
  60. };
  61. const isVoice = computed(() => {
  62. const currentStore = getCurrentAudioStore();
  63. return currentStore.isVoiceEnabled;
  64. });
  65. const toggleVoice = () => {
  66. const currentStore = getCurrentAudioStore();
  67. if (!currentStore.isVoiceEnabled) {
  68. // 如果语音功能关闭,先开启语音功能
  69. currentStore.toggleVoice();
  70. } else {
  71. // 如果语音功能开启,则切换播放/暂停状态
  72. if (currentStore.currentAudioUrl || currentStore.ttsUrl) {
  73. // 有音频时切换播放/暂停
  74. currentStore.togglePlayPause();
  75. } else {
  76. // 没有音频时关闭语音功能
  77. currentStore.toggleVoice();
  78. }
  79. }
  80. };
  81. // 将默认值改为从 sessionStorage 中获取,如果没有则使用默认值 'aifindCow'为第一个默认tab
  82. const activeTab = ref(sessionStorage.getItem("activeTabAI") || "AIchat");
  83. const activeIndex = ref(
  84. parseInt(sessionStorage.getItem("activeIndexAI") || "0")
  85. );
  86. const tabs = computed(() => [
  87. {
  88. name: "AIchat",
  89. label: "夺宝奇兵大模型",
  90. },
  91. // {
  92. // name: "AIfind",
  93. // label: "发现",
  94. // },
  95. {
  96. name: "AiEmotion",
  97. label: "AI情绪大模型",
  98. },
  99. ]);
  100. // 修改 setActiveTab 方法,添加一个可选参数 forceAIchat
  101. const setActiveTab = (tab, index, forceAIchat = false) => {
  102. isScrolling.value = false; //回复滚动到底部方法
  103. isAnnouncementVisible.value = false;
  104. // 重置输入框禁用状态,防止页面切换时状态残留
  105. console.log("tab", tab, "index", index);
  106. if (tab == "AIchat") {
  107. isInputDisabled.value = chatStore.chatInput;
  108. console.log("切换到AIchat页面,输入框状态为", isInputDisabled.value);
  109. } else if (tab == "AiEmotion") {
  110. isInputDisabled.value = chatStore.emotionInput;
  111. console.log("切换到AiEmotion页面,输入框状态为", isInputDisabled.value);
  112. } else {
  113. isInputDisabled.value = false;
  114. }
  115. if (forceAIchat && activeTab.value !== "AIchat") {
  116. activeTab.value = "AIchat";
  117. activeIndex.value = 0;
  118. sessionStorage.setItem("activeTabAI", "AIchat");
  119. sessionStorage.setItem("activeIndexAI", "0");
  120. } else {
  121. activeTab.value = tab;
  122. activeIndex.value = index;
  123. sessionStorage.setItem("activeTabAI", tab);
  124. sessionStorage.setItem("activeIndexAI", index.toString());
  125. }
  126. setActiveTabIndex(index);
  127. console.log(tab, index, "tab, index");
  128. setHeight(document.getElementById("testId")); // 给父组件发送窗口高度
  129. };
  130. // 修改 activeComponent 的计算逻辑
  131. const activeComponent = computed(() => {
  132. if (activeTab.value === "AIchat") {
  133. return AIchat;
  134. } else if (activeTab.value === "AIfind") {
  135. return AIfind;
  136. } else if (activeTab.value === "AiEmotion") {
  137. return AiEmotion; // 新增逻辑
  138. }
  139. });
  140. const activeTwoTab = computed(() => {
  141. if (isAnnouncementVisible.value) {
  142. return Announcement;
  143. } else {
  144. return Feedback;
  145. }
  146. });
  147. // 新增一个方法,调用时先判断是否处于 AIchat,若不在则跳转到 AIchat
  148. const ensureAIchat = () => {
  149. setActiveTab("AIchat", 0, true);
  150. };
  151. // 获取次数
  152. const UserCount = computed(() => chatStore.UserCount);
  153. const getCount = () => {
  154. console.log("点击了获取次数的按钮");
  155. };
  156. // 深度思考
  157. const isThinking = ref(true);
  158. const toggleThink = () => {
  159. isThinking.value = !isThinking.value;
  160. };
  161. // 发送消息
  162. const message = ref("");
  163. // 传输对象
  164. const messages = ref([]);
  165. // 信息加载状态
  166. const isLoading = computed(() => {
  167. chatStore.isLoading;
  168. });
  169. // 输入框禁用状态
  170. const isInputDisabled = ref(false);
  171. // 添加用户消息
  172. const updateMessage = (title) => {
  173. message.value = title;
  174. console.log("updateMessage 的值:", title);
  175. };
  176. watch(
  177. () => chatStore.announcementMsg,
  178. (newVal) => {
  179. console.log("监听到公告改变", chatStore.announcementMsg);
  180. if (chatStore.announcementMsg && !isInputDisabled.value) {
  181. message.value = chatStore.announcementMsg;
  182. chatStore.announcementMsg = null;
  183. }
  184. }
  185. );
  186. watch(
  187. () => dataStore.isFeedback,
  188. async (newVal) => {
  189. if (!dataStore.isFeedback) {
  190. // 重置公告页面显示状态
  191. isAnnouncementVisible.value = false;
  192. await nextTick();
  193. // 监听页面高度
  194. throttledHeightListener();
  195. }
  196. }
  197. );
  198. watch(
  199. () => chatStore.chatInput,
  200. async (newVal) => {
  201. if (activeTab.value == "AIchat") {
  202. isInputDisabled.value = chatStore.chatInput;
  203. }
  204. }
  205. );
  206. watch(
  207. () => chatStore.emotionInput,
  208. async (newVal) => {
  209. if (activeTab.value == "AiEmotion") {
  210. isInputDisabled.value = chatStore.emotionInput;
  211. }
  212. }
  213. );
  214. const sendMessage = async () => {
  215. if (
  216. localStorage.getItem("localToken") == null ||
  217. localStorage.getItem("localToken") == ""
  218. ) {
  219. ElMessage.error("请先登录");
  220. return;
  221. }
  222. // 检查输入内容是否为空
  223. if (!message.value || !message.value.trim()) {
  224. ElMessage.warning("输入内容不能为空");
  225. return;
  226. }
  227. isScrolling.value = false;
  228. // 注意:历史记录会在消息发送后自动更新,无需手动添加
  229. // 取消历史记录选中状态
  230. if (historyRecordRef) {
  231. historyRecordRef.value.selectedRecordId = null;
  232. }
  233. // 判断当前是否为 AiEmotion 组件
  234. if (activeTab.value === "AiEmotion") {
  235. // 禁用输入框
  236. isInputDisabled.value = true;
  237. chatStore.emotionInput = true;
  238. // 调用 AiEmotion 组件的 handleSendMessage 方法
  239. aiEmotionRef.value?.handleSendMessage(message.value, () => {
  240. // 打字机效果完成后的回调,重新启用输入框
  241. isInputDisabled.value = false;
  242. chatStore.emotionInput = false;
  243. });
  244. message.value = ""; // 清空输入框
  245. return;
  246. }
  247. // 调用 ensureAIchat 确保跳转到 AIchat 页面
  248. ensureAIchat();
  249. if (isInputDisabled.value) return;
  250. isInputDisabled.value = true;
  251. chatStore.chatInput = true;
  252. const messageContent = message.value;
  253. // 重置消息输入框
  254. message.value = "";
  255. setTimeout(() => {
  256. console.log("延时后添加消息", messageContent);
  257. // 发送消息时,设置 isLoading 为 true
  258. messages.value = [
  259. ...messages.value,
  260. {
  261. sender: "user",
  262. content: messageContent,
  263. audioArray: [],
  264. audioStatus: false,
  265. },
  266. ];
  267. console.log(messages.value, "messages.value");
  268. }, 200);
  269. };
  270. // 重新启用输入框的方法
  271. const enableInput = () => {
  272. console.log("解除禁用");
  273. isInputDisabled.value = false;
  274. };
  275. // 处理历史记录选择
  276. const handleHistorySelect = (stockData) => {
  277. console.log("接收到历史记录数据:", stockData);
  278. // 如果当前不在AiEmotion页面,切换到AiEmotion页面
  279. // if (activeTab.value !== 'AiEmotion') {
  280. // setActiveTab('AiEmotion', 1);
  281. // }
  282. // 等待组件渲染完成后调用addStock方法
  283. nextTick(() => {
  284. if (aiEmotionRef.value && aiEmotionRef.value.addStock) {
  285. aiEmotionRef.value.addStock(stockData);
  286. } else {
  287. console.error("AiEmotion组件或addStock方法不可用");
  288. }
  289. });
  290. };
  291. // 公告
  292. // 新增一个变量来控制是否显示公告页面
  293. const isAnnouncementVisible = ref(false);
  294. // Token规则提示框相关
  295. const tokenRuleDialogVisible = ref(false);
  296. const hasShownTokenRule = ref({
  297. AIchat: sessionStorage.getItem("hasShownTokenRule_AIchat") === "true",
  298. AiEmotion: sessionStorage.getItem("hasShownTokenRule_AiEmotion") === "true",
  299. });
  300. // 关闭Token规则提示框
  301. const closeTokenRuleDialog = () => {
  302. tokenRuleDialogVisible.value = false;
  303. };
  304. // 添加全局点击事件监听器,使任何点击动作都能关闭提示框
  305. // 定义处理函数,以便正确移除事件监听器
  306. const handleGlobalClick = () => {
  307. if (tokenRuleDialogVisible.value) {
  308. tokenRuleDialogVisible.value = false;
  309. }
  310. };
  311. onMounted(() => {
  312. document.addEventListener("click", handleGlobalClick);
  313. });
  314. // 在组件卸载时移除事件监听器
  315. onUnmounted(() => {
  316. document.removeEventListener("click", handleGlobalClick);
  317. });
  318. // 检查是否需要显示Token规则提示框(从其他页面跳转过来时)
  319. const checkTokenRuleOnPageLoad = () => {
  320. const activeTab = sessionStorage.getItem("activeTabAI");
  321. const fromExternalPage = sessionStorage.getItem("fromExternalPage");
  322. if (
  323. fromExternalPage === "true" &&
  324. activeTab &&
  325. !hasShownTokenRule.value[activeTab]
  326. ) {
  327. tokenRuleDialogVisible.value = true;
  328. hasShownTokenRule.value[activeTab] = true;
  329. sessionStorage.setItem(`hasShownTokenRule_${activeTab}`, "true");
  330. // 清除标记,避免重复显示
  331. sessionStorage.removeItem("fromExternalPage");
  332. }
  333. };
  334. const showAnnouncement = async () => {
  335. console.log("打开公告");
  336. dataStore.isFeedback = true; // 显示用户反馈页面
  337. isScrolling.value = false; //回复滚动到底部方法
  338. isAnnouncementVisible.value = true; // 显示公告页面
  339. if (isMobile.value) {
  340. if (historyRecordRef) {
  341. historyRecordRef.value.isCollapsed = true;
  342. }
  343. }
  344. };
  345. // 跳转用户反馈
  346. const showFeedback = () => {
  347. console.log("打开用户反馈");
  348. dataStore.isFeedback = true; // 显示用户反馈页面
  349. isAnnouncementVisible.value = false; // 显示反馈页面
  350. if (isMobile.value) {
  351. if (historyRecordRef) {
  352. historyRecordRef.value.isCollapsed = true;
  353. }
  354. }
  355. };
  356. // 保证发送消息时,滚动屏在底部
  357. const tabContentAIchat = ref(null);
  358. const tabContentAiEmotion = ref(null);
  359. const isScrolling = ref(false); //判断用户是否在滚动
  360. // AiEmotion页面高度监听器相关变量
  361. const aiEmotionHeightObserver = ref(null);
  362. const isAiEmotionAutoScrollEnabled = ref(false);
  363. const isAiEmotionUserScrolling = ref(false); // 用户是否正在手动滚动
  364. const aiEmotionScrollTimer = ref(null); // 滚动检测定时器
  365. const isChartInteracting = ref(false); // 图表是否正在交互
  366. const chartInteractionTimer = ref(null); // 图表交互检测定时器
  367. // 获取当前活动页面的滚动容器
  368. const getCurrentScrollContainer = () => {
  369. if (activeTab.value === "AIchat") {
  370. return tabContentAIchat.value;
  371. } else if (activeTab.value === "AiEmotion") {
  372. return tabContentAiEmotion.value;
  373. }
  374. return null;
  375. };
  376. const smoothScrollToBottom = async () => {
  377. // console.log("调用滚动到底部的方法");
  378. // await nextTick();
  379. const container = getCurrentScrollContainer();
  380. // console.log(container, 'container')
  381. // console.log(isScrolling.value, 'isScrolling.value')
  382. if (!container) return;
  383. await nextTick(); // 确保在DOM更新后执行
  384. if (!isScrolling.value) {
  385. container.scrollTop = container.scrollHeight - container.offsetHeight;
  386. // container.scrollTop = container.scrollHeight;
  387. // container.scrollTop = container.offsetHeight;
  388. // container.scrollTop = container.scrollHeight + container.offsetHeight;
  389. // console.log(container.scrollHeight, container.offsetHeight, container.scrollHeight - container.offsetHeight, container.scrollTop, "总长度", "可视长度", "位置")
  390. }
  391. };
  392. const throttledSmoothScrollToBottom = _.throttle(smoothScrollToBottom, 300, {
  393. trailing: false,
  394. });
  395. // AiEmotion页面自动滚动到底部的防抖函数
  396. const debouncedAiEmotionScrollToBottom = _.debounce(() => {
  397. if (
  398. activeTab.value === "AiEmotion" &&
  399. isAiEmotionAutoScrollEnabled.value &&
  400. !isAiEmotionUserScrolling.value &&
  401. !isChartInteracting.value
  402. ) {
  403. const container = tabContentAiEmotion.value;
  404. if (container) {
  405. container.scrollTop = container.scrollHeight - container.offsetHeight;
  406. }
  407. }
  408. }, 150);
  409. // 启动AiEmotion页面高度监听器
  410. const startAiEmotionHeightObserver = () => {
  411. // 先停止之前的监听器
  412. stopAiEmotionHeightObserver();
  413. isAiEmotionAutoScrollEnabled.value = true;
  414. // 创建ResizeObserver监听页面内容变化
  415. aiEmotionHeightObserver.value = new ResizeObserver((entries) => {
  416. if (
  417. isAiEmotionAutoScrollEnabled.value &&
  418. activeTab.value === "AiEmotion" &&
  419. !isChartInteracting.value
  420. ) {
  421. debouncedAiEmotionScrollToBottom();
  422. }
  423. });
  424. // 监听document.body的尺寸变化
  425. if (document.body) {
  426. aiEmotionHeightObserver.value.observe(document.body);
  427. }
  428. // 创建MutationObserver监听DOM结构变化
  429. const mutationObserver = new MutationObserver((mutations) => {
  430. let shouldScroll = false;
  431. mutations.forEach((mutation) => {
  432. if (mutation.type === "childList" && mutation.addedNodes.length > 0) {
  433. // 检查新增的节点是否包含实际内容
  434. const hasContent = Array.from(mutation.addedNodes).some((node) => {
  435. if (node.nodeType === Node.ELEMENT_NODE) {
  436. return node.offsetHeight > 0 || node.scrollHeight > 0;
  437. }
  438. return (
  439. node.nodeType === Node.TEXT_NODE &&
  440. node.textContent.trim().length > 0
  441. );
  442. });
  443. if (hasContent) {
  444. shouldScroll = true;
  445. }
  446. }
  447. });
  448. if (
  449. shouldScroll &&
  450. isAiEmotionAutoScrollEnabled.value &&
  451. activeTab.value === "AiEmotion" &&
  452. !isChartInteracting.value
  453. ) {
  454. debouncedAiEmotionScrollToBottom();
  455. }
  456. });
  457. // 监听AiEmotion页面的主要内容区域的DOM变化
  458. const aiEmotionContainer = tabContentAiEmotion.value;
  459. if (aiEmotionContainer) {
  460. mutationObserver.observe(aiEmotionContainer, {
  461. childList: true,
  462. subtree: true,
  463. attributes: false,
  464. characterData: true,
  465. });
  466. }
  467. // 保存mutationObserver引用以便清理
  468. aiEmotionHeightObserver.value.mutationObserver = mutationObserver;
  469. // 为AiEmotion页面的滚动容器添加滚动事件监听器
  470. if (aiEmotionContainer) {
  471. aiEmotionContainer.addEventListener("scroll", handleAiEmotionUserScroll, {
  472. passive: true,
  473. });
  474. // 保存滚动事件监听器引用以便清理
  475. aiEmotionHeightObserver.value.scrollListener = handleAiEmotionUserScroll;
  476. }
  477. console.log("AiEmotion页面高度监听器已启动");
  478. };
  479. // AiEmotion页面用户滚动检测
  480. const handleAiEmotionUserScroll = () => {
  481. // 标记用户正在滚动
  482. isAiEmotionUserScrolling.value = true;
  483. // 清除之前的定时器
  484. if (aiEmotionScrollTimer.value) {
  485. clearTimeout(aiEmotionScrollTimer.value);
  486. }
  487. // 设置定时器,2秒后恢复自动滚动
  488. // aiEmotionScrollTimer.value = setTimeout(() => {
  489. // isAiEmotionUserScrolling.value = false;
  490. // console.log("AiEmotion页面用户滚动检测:恢复自动滚动");
  491. // }, 2000);
  492. };
  493. // 图表交互状态管理
  494. const handleChartInteractionStart = () => {
  495. console.log("图表交互开始,临时禁用自动滚动");
  496. isChartInteracting.value = true;
  497. // 清除之前的定时器
  498. if (chartInteractionTimer.value) {
  499. clearTimeout(chartInteractionTimer.value);
  500. }
  501. };
  502. const handleChartInteractionEnd = () => {
  503. // 清除之前的定时器
  504. if (chartInteractionTimer.value) {
  505. clearTimeout(chartInteractionTimer.value);
  506. }
  507. // 设置定时器,1秒后恢复自动滚动
  508. chartInteractionTimer.value = setTimeout(() => {
  509. isChartInteracting.value = false;
  510. console.log("图表交互结束,恢复自动滚动");
  511. }, 1000);
  512. };
  513. // 暴露图表交互管理函数给全局使用
  514. window.handleChartInteractionStart = handleChartInteractionStart;
  515. window.handleChartInteractionEnd = handleChartInteractionEnd;
  516. // 处理AiEmotion页面的滚动请求
  517. const handleAiEmotionScrollToBottom = () => {
  518. if (activeTab.value === "AiEmotion") {
  519. const container = tabContentAiEmotion.value;
  520. if (container) {
  521. // 使用nextTick确保DOM已更新
  522. nextTick(() => {
  523. container.scrollTop = container.scrollHeight - container.offsetHeight;
  524. console.log("AiEmotion页面:执行容器滚动到底部");
  525. });
  526. }
  527. }
  528. };
  529. // 停止AiEmotion页面高度监听器
  530. const stopAiEmotionHeightObserver = () => {
  531. isAiEmotionAutoScrollEnabled.value = false;
  532. isAiEmotionUserScrolling.value = false;
  533. // 清理滚动检测定时器
  534. if (aiEmotionScrollTimer.value) {
  535. clearTimeout(aiEmotionScrollTimer.value);
  536. aiEmotionScrollTimer.value = null;
  537. }
  538. if (aiEmotionHeightObserver.value) {
  539. // 清理ResizeObserver
  540. aiEmotionHeightObserver.value.disconnect();
  541. // 清理MutationObserver
  542. if (aiEmotionHeightObserver.value.mutationObserver) {
  543. aiEmotionHeightObserver.value.mutationObserver.disconnect();
  544. aiEmotionHeightObserver.value.mutationObserver = null;
  545. }
  546. // 清理滚动事件监听器
  547. if (
  548. aiEmotionHeightObserver.value.scrollListener &&
  549. tabContentAiEmotion.value
  550. ) {
  551. tabContentAiEmotion.value.removeEventListener(
  552. "scroll",
  553. aiEmotionHeightObserver.value.scrollListener
  554. );
  555. aiEmotionHeightObserver.value.scrollListener = null;
  556. }
  557. aiEmotionHeightObserver.value = null;
  558. }
  559. console.log("AiEmotion页面高度监听器已停止");
  560. };
  561. watch(
  562. () => chatStore.messages.length,
  563. () => {
  564. // console.log('messages变化了')
  565. // 只有在AIchat页面时才执行自动滚动
  566. if (activeTab.value === "AIchat") {
  567. throttledSmoothScrollToBottom();
  568. }
  569. // setTimeout(throttledSmoothScrollToBottom, 100);
  570. },
  571. { deep: false, immediate: true }
  572. );
  573. watch(
  574. () => chatStore.dbqbClickRecord,
  575. async (newValue, oldValue) => {
  576. const container = getCurrentScrollContainer();
  577. if (!container) return;
  578. await nextTick(); // 确保在DOM更新后执行
  579. container.scrollTop = 0;
  580. }
  581. );
  582. watch(
  583. () => chatStore.dbqbScrollToTop,
  584. async (newValue, oldValue) => {
  585. const container = getCurrentScrollContainer();
  586. if (!container) return;
  587. await nextTick(); // 确保在DOM更新后执行
  588. container.scrollTop = 0;
  589. }
  590. );
  591. watch(
  592. activeTab,
  593. async () => {
  594. console.log("activeTab变化了", activeTab.value);
  595. if (activeTab.value == "AIchat" || activeTab.value == "AiEmotion") {
  596. if (historyRecordRef.value && historyRecordRef.value.getHistoryList) {
  597. const result = historyRecordRef.value.getHistoryList({
  598. model: activeTab.value == "AIchat" ? 1 : 2,
  599. token: localStorage.getItem("localToken"),
  600. });
  601. }
  602. }
  603. if (activeTab.value === "AIchat") {
  604. isScrolling.value = false; //回复滚动到底部方法
  605. // 停止AiEmotion页面的高度监听器
  606. stopAiEmotionHeightObserver();
  607. setTimeout(() => {
  608. // throttledSmoothScrollToBottom();
  609. }, 100);
  610. } else if (activeTab.value === "AiEmotion") {
  611. // 启动AiEmotion页面的高度监听器
  612. await nextTick(); // 确保DOM更新后启动监听器
  613. startAiEmotionHeightObserver();
  614. } else {
  615. // 其他页面时停止AiEmotion页面的高度监听器
  616. stopAiEmotionHeightObserver();
  617. }
  618. // AiEmotion页面不执行自动滚动,避免刷新后滚动到底部
  619. // setTimeout(throttledSmoothScrollToBottom, 100);
  620. },
  621. { deep: true, immediate: true }
  622. );
  623. // 获取token的核心函数
  624. // const fnGetToken = () => {
  625. // // console.log('进入fnGetToken')
  626. // window.JWready = (ress) => {
  627. // // console.log('进入JWready')
  628. // try {
  629. // ress = JSON.parse(ress);
  630. // // console.log(ress, 'ress')
  631. // } catch (error) {
  632. // console.log(error, "fnGetToken error");
  633. // } //platform为5是app端
  634. // // platform.value = ress.data.platform
  635. // // 处理平台判断
  636. // console.log(ress.data.platform, "ress.data.platform");
  637. // if (!ress.data.platform) {
  638. // // 非App环境通过URL参数获取
  639. // localStorage.setItem(
  640. // "localToken",
  641. // decodeURIComponent(String(getQueryVariable("token")))
  642. // );
  643. // // localStorage.setItem('localToken', "+SsksARQgUHIbIG3rRnnbZi0+fEeMx8pywnIlrmTxo5EOPR/wjWDV7w7+ZUseiBtf9kFa/atmNx6QfSpv5w")
  644. // } else {
  645. // // App环境通过桥接获取
  646. // useAppBridge().packageFun(
  647. // "JWgetStorage",
  648. // (response) => {
  649. // const res = JSON.parse(response); // 解析返回的结果
  650. // localStorage.setItem("localToken", res.data);
  651. // // localStorage.setItem('localToken', "+SsksARQgUHIbIG3rRnnbZi0+fEeMx8pywnIlrmTxo5EOPR/wjWDV7w7+ZUseiBtf9kFa/atmNx6QfSpv5w")
  652. // },
  653. // 5,
  654. // {
  655. // key: "token",
  656. // }
  657. // );
  658. // }
  659. // };
  660. // // console.log('出来了')
  661. // // 触发App桥接
  662. // useAppBridge().packageFun("JWwebReady", () => {}, 5, {});
  663. // };
  664. // 在setTimeout中延迟执行
  665. // setTimeout(() => {
  666. // fnGetToken();
  667. // }, 800);
  668. const heightListener = () => {
  669. const tabContainer = getCurrentScrollContainer();
  670. if (!tabContainer) return;
  671. let befortop = 0;
  672. const scrollHandler = () => {
  673. const aftertop = tabContainer.scrollTop;
  674. // 新增底部判断逻辑
  675. const isBottom =
  676. aftertop + tabContainer.offsetHeight + 70 >= tabContainer.scrollHeight;
  677. if (activeTab.value === "AIchat") {
  678. if (aftertop - befortop > 0) {
  679. // console.log("向下滚动");
  680. isScrolling.value = true;
  681. } else {
  682. // console.log("向上滚动");
  683. isScrolling.value = true;
  684. }
  685. // 添加底部状态检测
  686. if (isBottom) {
  687. // console.log("滚动到底部");
  688. isScrolling.value = false;
  689. }
  690. }
  691. befortop = aftertop;
  692. };
  693. // console.log(isScrolling.value, 'isScrolling.value')
  694. tabContainer.addEventListener("scroll", scrollHandler);
  695. };
  696. const throttledHeightListener = _.throttle(heightListener, 500, {
  697. trailing: false,
  698. });
  699. // const goToRecharge = () => {
  700. // console.log("点击充值");
  701. // // http://39.101.133.168:8919/payment/recharge/index?
  702. // // url=http%3A%2F%2Flocalhost%3A8080%2FLiveActivity%2Fpck
  703. // // &platform=1
  704. // // &token=+S4h5QEE1hTIb4CxphrnbZi0+fEeMx8pywnIlrmTmo4QO6IolWnVWu5r+J4rKXMwK41UPfKqyIp+RvWmtM8
  705. // const userAgent = navigator.userAgent.toLowerCase();
  706. // const mobileKeywords = ["mobile", "android", "iphone", "ipad", "ipod"];
  707. // const isMobile = mobileKeywords.some((keyword) =>
  708. // userAgent.includes(keyword)
  709. // );
  710. // console.log(isMobile ? "手机" : "电脑");
  711. // const url = encodeURI("http://39.101.133.168:8857/aixiaocaishen/homePage");
  712. // console.log(url, "url");
  713. // const platform = isMobile ? 2 : 1;
  714. // const token = encodeURIComponent(localStorage.getItem("localToken"));
  715. // console.log(token, "token");
  716. // const rechargeUrl =
  717. // "http://39.101.133.168:8919/payment/recharge/index?" +
  718. // "url=" +
  719. // url +
  720. // "&platform=" +
  721. // platform +
  722. // "&token=" +
  723. // token;
  724. // console.log(rechargeUrl, "rechargeUrl");
  725. // window.location.href = rechargeUrl;
  726. // // window.open(rechargeUrl)
  727. // };
  728. const adjustFooterPosition = (height) => {
  729. const html = document.querySelector("html");
  730. const body = document.querySelector("body");
  731. const isAndroid = /Android/i.test(navigator.userAgent);
  732. if (isAndroid) {
  733. console.log("是安卓设备");
  734. console.log("window.visualViewport", window.visualViewport.height);
  735. const homePage = document.querySelector(".homepage");
  736. homePage.style.height = `${height}px`;
  737. // homePage.style.height = `460px`;
  738. html.scrollTop = 0;
  739. } else {
  740. console.log("非安卓设备");
  741. console.log("调整底部位置", height);
  742. const homePage = document.querySelector(".homepage");
  743. homePage.style.height = `${height}px`;
  744. html.scrollTop = 0;
  745. }
  746. setTimeout(() => {
  747. // 隐藏滚动条
  748. html.style.overflow = "hidden";
  749. body.style.overflow = "hidden";
  750. }, 200);
  751. };
  752. // 是否正在输入法组合
  753. const inputing = ref(false);
  754. const onFocus = function () {
  755. const visualViewport = window.visualViewport;
  756. // 获取可视区域高度
  757. setTimeout(() => {
  758. console.log("输入框聚焦");
  759. console.log(visualViewport.height, "visualViewport.height");
  760. const keyboardHeight = window.innerHeight - visualViewport.height;
  761. console.log(window.innerHeight, "window.innerHeight");
  762. console.log(keyboardHeight, "keyboardHeight");
  763. adjustFooterPosition(visualViewport.height);
  764. }, 200);
  765. };
  766. const onBlur = function () {
  767. inputing.value = false;
  768. const visualViewport = window.visualViewport;
  769. setTimeout(() => {
  770. console.log("输入框失焦");
  771. const keyboardHeight = window.innerHeight - visualViewport.height;
  772. console.log(window.innerHeight, "window.innerHeight");
  773. console.log(visualViewport.height, "visualViewport.height");
  774. console.log(keyboardHeight, "keyboardHeight");
  775. adjustFooterPosition(visualViewport.height);
  776. }, 200);
  777. };
  778. // window.addEventListener("resize", () => {
  779. // // 检测是否为iOS设备
  780. // const isIOS = /iPhone|iPad|iPod|ios/i.test(navigator.userAgent);
  781. // console.log("是否为iOS设备:", isIOS);
  782. // if (!isIOS) {
  783. // console.log("窗口大小变化");
  784. // const homePage = document.querySelector(".homepage");
  785. // homePage.style.height = `${window.innerHeight}px`;
  786. // }
  787. // });
  788. let touchmoveHandlerRef = null;
  789. const touchmoveHandler = (e) => {
  790. if (!dataStore.isFeedback) {
  791. if (historyRecordRef) {
  792. if (!historyRecordRef.value.isCollapsed) {
  793. return;
  794. }
  795. }
  796. // 判断触摸目标是否在当前活动页面的可滚动区域内
  797. const currentContainer = getCurrentScrollContainer();
  798. const isScrollableArea =
  799. currentContainer && currentContainer.contains(e.target);
  800. // 如果不在可滚动区域,则阻止滚动
  801. if (!isScrollableArea) {
  802. e.preventDefault();
  803. }
  804. }
  805. };
  806. const judgeDevice = async () => {
  807. // 延时300ms
  808. await new Promise((resolve) => setTimeout(resolve, 200));
  809. const userAgent = navigator.userAgent;
  810. isMobile.value =
  811. /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
  812. userAgent
  813. );
  814. console.log("当前设备为:", isMobile.value ? "移动端" : "PC端");
  815. };
  816. const throttledJudgeDevice = _.throttle(judgeDevice, 300, {
  817. trailing: false,
  818. });
  819. const expandHistory = () => {
  820. // if (activeTab.value == "AIchat" || activeTab.value == "AiEmotion") {
  821. // historyRecordRef.value.getHistoryList({
  822. // token: localStorage.getItem("localToken"),
  823. // model: activeTab.value == "AIchat" ? 1 : 2,
  824. // });
  825. // }
  826. if (
  827. historyRecordRef.value &&
  828. historyRecordRef.value.isCollapsed !== undefined
  829. ) {
  830. console.log("存在");
  831. historyRecordRef.value.isCollapsed = !historyRecordRef.value.isCollapsed;
  832. if (activeTab.value == "AIchat") {
  833. chatStore.aiChatCall = true;
  834. } else if (activeTab.value == "AiEmotion") {
  835. chatStore.aiEmotionCall = true;
  836. }
  837. }
  838. };
  839. const feedbackBack = () => {
  840. dataStore.isFeedback = false;
  841. };
  842. const backToHome = () => {
  843. if (isMobile.value) {
  844. console.log("用户是移动端");
  845. // 调用原生方法跳转到首页
  846. uni.postMessage({
  847. data: {
  848. val: {
  849. name: "JWopenView",
  850. extra: {
  851. data: {
  852. type: 3,
  853. },
  854. },
  855. },
  856. },
  857. });
  858. } else {
  859. console.log("用户是pc端");
  860. const env = import.meta.env.VITE_ENV;
  861. console.log("当前的环境为:", env);
  862. if (env == "development" || env == "test") {
  863. window.parent.location.href =
  864. "http://121.89.234.155:8807/hljw/homepage?menu=999999991";
  865. } else {
  866. window.parent.postMessage(
  867. {
  868. type: "NAVIGATE_TO_HOMEPAGE",
  869. menu: "999999991",
  870. },
  871. "*"
  872. );
  873. }
  874. // window.parent.location.href = window.parent.document.referrer
  875. }
  876. };
  877. // 8.18金币兑换Token start
  878. const userInfo = ref({
  879. nickname: "",
  880. img: "",
  881. jwcode: "",
  882. });
  883. const changeRule = ref({
  884. gold: 1,
  885. token: 1,
  886. });
  887. const changeLevelList = ref([
  888. { position: 10, calculatedPosition: 10 },
  889. { position: 20, calculatedPosition: 20 },
  890. { position: 50, calculatedPosition: 50 },
  891. { position: 100, calculatedPosition: 100 },
  892. { position: 200, calculatedPosition: 200 },
  893. { position: 500, calculatedPosition: 500 },
  894. { position: 1000, calculatedPosition: 1000 },
  895. ]);
  896. const activeLevel = ref(
  897. changeLevelList.value[0] || { position: 10, calculatedPosition: 10 }
  898. );
  899. const gold = ref(90);
  900. // 点击剩余次数会弹出的弹窗
  901. // 新增一个 ref 来控制弹窗的显示与隐藏
  902. const dialogVisible = ref(false);
  903. const rechargeDialogVisible = ref(false);
  904. const confirmDialogVisible = ref(false);
  905. const changeSuccessDialogVisible = ref(false);
  906. // 图片加载错误处理
  907. const handleImageError = (event) => {
  908. console.error("图片加载失败:", event.target.src);
  909. // 可以设置默认图片或重试逻辑
  910. event.target.src =
  911. "https://cdn.legu168.com/jtzy/Product/pcjingwang/images/userimg.png";
  912. };
  913. // 获取次数
  914. const showCount = async () => {
  915. try {
  916. if (
  917. !dataStore.userInfo.img ||
  918. !dataStore.userInfo.nickname ||
  919. !dataStore.userInfo.jwcode
  920. ) {
  921. console.log("缺少用户信息,调用方法");
  922. await getUserInfo();
  923. }
  924. userInfo.value.nickname = dataStore.userInfo.nickname;
  925. userInfo.value.img = dataStore.userInfo.img;
  926. userInfo.value.jwcode = dataStore.userInfo.jwcode;
  927. console.log("userInfo", userInfo.value);
  928. const [res, res2] = await Promise.all([
  929. showExchangeAPI(),
  930. getGoldCoinAPI({
  931. token: String(localStorage.getItem("localToken")),
  932. }),
  933. ]);
  934. changeLevelList.value = res.data;
  935. activeLevel.value = changeLevelList.value[0];
  936. changeRule.value.token = res.data[0].ratio;
  937. gold.value = res2.data.total;
  938. // 显示弹窗
  939. dialogVisible.value = true;
  940. console.log("dialogVisible 的值:", dialogVisible.value); // 添加日志确认
  941. } catch (e) {
  942. console.error("获取兑换列表出错", e);
  943. }
  944. };
  945. const chooseLevel = (item) => {
  946. activeLevel.value = item;
  947. };
  948. const changeToken = () => {
  949. if (gold.value < activeLevel.value.position) {
  950. rechargeDialogVisible.value = true;
  951. return;
  952. } else {
  953. confirmDialogVisible.value = true;
  954. }
  955. };
  956. const goRecharge = () => {
  957. console.log("执行前往充值方法");
  958. sessionStorage.setItem("rechargeFlag", "1");
  959. sessionStorage.setItem("activeLevel", JSON.stringify(activeLevel.value));
  960. if (isMobile.value) {
  961. console.log("用户是移动端");
  962. uni.postMessage({
  963. data: {
  964. val: {
  965. name: "JWopenView",
  966. extra: {
  967. data: {
  968. type: 4,
  969. },
  970. },
  971. },
  972. },
  973. });
  974. } else {
  975. console.log("用户是pc端");
  976. const env = import.meta.env.VITE_ENV;
  977. console.log("当前的环境为:", env);
  978. if (env == "development" || env == "test") {
  979. window.parent.location.href =
  980. "http://121.89.234.155:8807/user/myGold?token=" +
  981. encodeURIComponent(localStorage.getItem("localToken")) +
  982. "&where=xiaocaishen&successUrl=https://hwjb.homilychart.com/aixiaocaishen/homePage";
  983. // "&where=xiaocaishen&successUrl=http://192.168.1.5:3000/aixiaocaishen/homePage";
  984. } else {
  985. window.parent.location.href =
  986. "https://web.homilychart.com/user/myGold?token=" +
  987. encodeURIComponent(localStorage.getItem("localToken")) +
  988. "&where=xiaocaishen&successUrl=https://mp.homilychart.com/aixiaocaishen/homePage";
  989. }
  990. // window.parent.location.href = window.parent.document.referrer
  991. }
  992. };
  993. const goChange = async () => {
  994. try {
  995. await exchangeAPI({
  996. token: String(localStorage.getItem("localToken")),
  997. num: activeLevel.value.position,
  998. });
  999. confirmDialogVisible.value = false;
  1000. dialogVisible.value = false;
  1001. changeSuccessDialogVisible.value = true;
  1002. setTimeout(() => {
  1003. changeSuccessDialogVisible.value = false;
  1004. }, 2000);
  1005. // 刷新次数
  1006. await chatStore.getUserCount();
  1007. } catch (e) {
  1008. console.error("兑换失败", e);
  1009. }
  1010. };
  1011. // 8.18金币兑换Token end
  1012. onMounted(async () => {
  1013. // 检查是否需要显示Token规则提示框
  1014. checkTokenRuleOnPageLoad();
  1015. throttledJudgeDevice();
  1016. // 禁用全局触摸滚动
  1017. touchmoveHandlerRef = touchmoveHandler;
  1018. document.addEventListener("touchmove", touchmoveHandlerRef, {
  1019. passive: false,
  1020. });
  1021. setHeight(document.getElementById("testId")); // 给父组件发送窗口高度
  1022. // 获取次数
  1023. await chatStore.getUserCount();
  1024. // 滚动到底部
  1025. throttledSmoothScrollToBottom();
  1026. // 监听页面高度
  1027. throttledHeightListener();
  1028. // 添加输入框焦点处理
  1029. // handleInputFocus();
  1030. // 初始化视口高度变量
  1031. // updateAppHeight();
  1032. // 添加原生事件监听器
  1033. window.showCountHandler = showCount;
  1034. window.addEventListener("resize", throttledJudgeDevice);
  1035. window.receiveUniAppMessage = async function (messageData) {
  1036. console.log("收到 uni-app 消息:", messageData);
  1037. // 根据消息类型进行不同处理
  1038. switch (messageData.type) {
  1039. case "paymentSuccess":
  1040. // 处理支付成功消息
  1041. const [res1, res2] = await Promise.all([
  1042. godExchangeAPI({ state: 1 }),
  1043. getGoldCoinAPI({ token: String(localStorage.getItem("localToken")) }),
  1044. ]);
  1045. gold.value = res2.data.total;
  1046. rechargeDialogVisible.value = false;
  1047. break;
  1048. default:
  1049. console.log("未知消息类型:", messageData.type);
  1050. }
  1051. };
  1052. if (
  1053. sessionStorage.getItem("rechargeFlag") == "1" &&
  1054. getQueryVariable("successType") == "success"
  1055. ) {
  1056. await godExchangeAPI({ state: 1 });
  1057. await showCount();
  1058. activeLevel.value =
  1059. JSON.parse(sessionStorage.getItem("activeLevel")) ||
  1060. changeLevelList.value[0];
  1061. console.log("activeLevel", activeLevel.value);
  1062. sessionStorage.removeItem("activeLevel");
  1063. sessionStorage.setItem("rechargeFlag", "0");
  1064. }
  1065. });
  1066. onUnmounted(() => {
  1067. window.removeEventListener("resize", throttledJudgeDevice);
  1068. if (touchmoveHandlerRef) {
  1069. console.log("卸载touchmoveHandlerRef组件");
  1070. document.removeEventListener("touchmove", touchmoveHandlerRef);
  1071. }
  1072. // 清理AiEmotion页面的高度监听器
  1073. stopAiEmotionHeightObserver();
  1074. delete window.showCountHandler;
  1075. delete window.receiveUniAppMessage;
  1076. // 清理图表交互相关的定时器和全局函数
  1077. if (chartInteractionTimer.value) {
  1078. clearTimeout(chartInteractionTimer.value);
  1079. }
  1080. if (window.handleChartInteractionStart) {
  1081. delete window.handleChartInteractionStart;
  1082. }
  1083. if (window.handleChartInteractionEnd) {
  1084. delete window.handleChartInteractionEnd;
  1085. }
  1086. });
  1087. </script>
  1088. <template>
  1089. <div class="homepage" id="testId">
  1090. <!-- 历史记录组件 -->
  1091. <HistoryRecord
  1092. ref="historyRecordRef"
  1093. :current-type="activeTab"
  1094. @selectRecord="handleHistorySelect"
  1095. :isMobile="isMobile"
  1096. @showAnnouncement="showAnnouncement"
  1097. @showFeedback="showFeedback"
  1098. />
  1099. <div
  1100. v-if="isMobile && !historyRecordRef?.isCollapsed"
  1101. class="zhezhao"
  1102. @click="expandHistory"
  1103. ></div>
  1104. <el-container
  1105. v-if="!dataStore.isFeedback"
  1106. class="main-container"
  1107. :class="{
  1108. collapsed: !isMobile && historyRecordRef?.isCollapsed,
  1109. unCollapsed: !isMobile && !historyRecordRef?.isCollapsed,
  1110. }"
  1111. >
  1112. <!-- AI小财神头部 logo 次数 公告 -->
  1113. <el-header class="homepage-head">
  1114. <!-- logo -->
  1115. <div class="homepage-logo" v-if="isMobile">
  1116. <img
  1117. class="expand"
  1118. @click="expandHistory"
  1119. src="https://d31zlh4on95l9h.cloudfront.net/images/37fe3d79a8a700f6c674c9f0e7af066b.png"
  1120. alt="icon"
  1121. />
  1122. <img :src="logo" alt="图片加载失败" class="logo1" />
  1123. <!-- <img :src="madeInHL" alt="图片加载失败" class="logo2" /> -->
  1124. </div>
  1125. <div class="homepage-right-group" v-if="isMobile">
  1126. <div class="count-badge" @click="showCount">
  1127. <img
  1128. src="https://d31zlh4on95l9h.cloudfront.net/images/74e20c65c9ef2526477c63ad68698a50.png"
  1129. class="action-btn"
  1130. />
  1131. <div class="count-number">{{ UserCount }}</div>
  1132. <div class="clickGetCount">点击获取Token</div>
  1133. </div>
  1134. <div class="backToHomeBtn" @click="backToHome()">
  1135. <img
  1136. src="https://d31zlh4on95l9h.cloudfront.net/images/9cbc5b2eb2327bd04d015c19d8c3f1f9.png"
  1137. alt="返回首页"
  1138. class="backImg"
  1139. />
  1140. <div class="backContent">返回首页</div>
  1141. </div>
  1142. <!-- <img
  1143. :src="announcementBtn"
  1144. class="announcement-btn action-btn"
  1145. @click="showAnnouncement"
  1146. />
  1147. <img
  1148. :src="feedbackBtn"
  1149. class="announcement-btn action-btn"
  1150. @click="showFeedback"
  1151. /> -->
  1152. </div>
  1153. </el-header>
  1154. <!-- 主体部分小人 问题轮询图 对话内容 -->
  1155. <el-main class="homepage-body">
  1156. <div class="main-wrapper">
  1157. <section class="tab-section">
  1158. <div
  1159. class="tab-container"
  1160. :class="{
  1161. pcTabContainer: !isMobile,
  1162. }"
  1163. >
  1164. <div
  1165. v-for="(tab, index) in tabs"
  1166. :key="tab.name"
  1167. @click="setActiveTab(tab.name, index)"
  1168. :class="[
  1169. 'tab-item',
  1170. { active: activeIndex === index && !isAnnouncementVisible },
  1171. ]"
  1172. >
  1173. <span>{{ tab.label }}</span>
  1174. </div>
  1175. <div v-if="!isMobile" class="pc-count-badge">
  1176. <div class="pc-countBtn" @click="showCount">
  1177. <div class="pc-action-btn">
  1178. <div class="pc-count-number">{{ UserCount }}</div>
  1179. </div>
  1180. <div class="pc-clickGetCount">点击获取Token</div>
  1181. </div>
  1182. <div class="pc-backToHomeBtn" @click="backToHome()">
  1183. <img
  1184. src="https://d31zlh4on95l9h.cloudfront.net/images/9cbc5b2eb2327bd04d015c19d8c3f1f9.png"
  1185. alt="返回首页"
  1186. class="pc-backImg"
  1187. />
  1188. <div class="pc-backContent">返回首页</div>
  1189. </div>
  1190. </div>
  1191. </div>
  1192. </section>
  1193. <!-- AIchat页面的独立滚动容器 -->
  1194. <div
  1195. v-show="activeTab === 'AIchat'"
  1196. class="tab-content"
  1197. :class="{
  1198. pcTabContent: !isMobile,
  1199. }"
  1200. ref="tabContentAIchat"
  1201. >
  1202. <component
  1203. v-if="activeTab === 'AIchat'"
  1204. :is="activeComponent"
  1205. :messages="messages"
  1206. @updateMessage="updateMessage"
  1207. @sendMessage="sendMessage"
  1208. @ensureAIchat="ensureAIchat"
  1209. @enableInput="enableInput"
  1210. />
  1211. </div>
  1212. <!-- AiEmotion页面的独立滚动容器 -->
  1213. <div
  1214. v-show="activeTab === 'AiEmotion'"
  1215. class="tab-content"
  1216. :class="{
  1217. pcTabContent: !isMobile,
  1218. }"
  1219. ref="tabContentAiEmotion"
  1220. >
  1221. <component
  1222. v-if="activeTab === 'AiEmotion'"
  1223. :is="activeComponent"
  1224. :messages="messages"
  1225. @updateMessage="updateMessage"
  1226. @sendMessage="sendMessage"
  1227. @ensureAIchat="ensureAIchat"
  1228. @enableInput="enableInput"
  1229. @scrollToBottom="handleAiEmotionScrollToBottom"
  1230. @showCount="showCount"
  1231. ref="aiEmotionRef"
  1232. />
  1233. </div>
  1234. </div>
  1235. </el-main>
  1236. <!-- 尾部 问题输入框 深度思考 多语言 语音播报 -->
  1237. <el-footer
  1238. class="homepage-footer"
  1239. :class="{
  1240. pcFooter: !isMobile,
  1241. }"
  1242. id="input"
  1243. >
  1244. <!-- 第一行按钮 -->
  1245. <div class="footer-first-line">
  1246. <div class="left-group">
  1247. <!-- <img v-if="isThinking" :src="thinkActive" @click="toggleThink" class="action-btn" />
  1248. <img v-else :src="thinkNoActive" @click="toggleThink" class="action-btn" />
  1249. <img :src="languageBtn" @click="changeLanguage" class="action-btn" /> -->
  1250. <!-- 夺宝奇兵大模型按钮 -->
  1251. <img
  1252. :src="activeTab === 'AIchat' ? dbqbButton01 : dbqbButton02"
  1253. @click="setActiveTab('AIchat', 0)"
  1254. class="action-btn model-btn"
  1255. alt="夺宝奇兵大模型"
  1256. />
  1257. <!-- AI情绪大模型按钮 -->
  1258. <img
  1259. :src="
  1260. activeTab === 'AiEmotion' ? emotionButton01 : emotionButton02
  1261. "
  1262. @click="setActiveTab('AiEmotion', 1)"
  1263. class="action-btn model-btn"
  1264. alt="AI情绪大模型"
  1265. />
  1266. <!-- <img v-if="
  1267. getCurrentAudioStore().isVoiceEnabled &&
  1268. getCurrentAudioStore().isPlaying
  1269. " :src="voice" @click="toggleVoice" class="action-btn" style="animation: pulse 1.5s infinite" />
  1270. <img v-else-if="
  1271. getCurrentAudioStore().isVoiceEnabled &&
  1272. !getCurrentAudioStore().isPlaying
  1273. " :src="voiceNoActive" @click="toggleVoice" class="action-btn" />
  1274. <img v-else :src="voiceNoActive" @click="toggleVoice" class="action-btn" /> -->
  1275. </div>
  1276. </div>
  1277. <!-- 第二行输入框 -->
  1278. <div class="footer-second-line">
  1279. <!-- <img :src="msgBtn" class="msg-icon" /> -->
  1280. <div class="input-container">
  1281. <el-input
  1282. type="textarea"
  1283. v-model="message"
  1284. @focus="onFocus"
  1285. @blur="onBlur"
  1286. :autosize="{ minRows: 1, maxRows: 4 }"
  1287. class="msg-input"
  1288. @keydown.enter.exact.prevent="
  1289. isLoading || isInputDisabled ? null : sendMessage()
  1290. "
  1291. :disabled="isInputDisabled"
  1292. resize="none"
  1293. :class="{ input: !message && !inputing }"
  1294. @compositionstart="inputing = true"
  1295. @compositionend="inputing = false"
  1296. >
  1297. </el-input>
  1298. <img
  1299. :src="
  1300. isInputDisabled
  1301. ? 'https://d31zlh4on95l9h.cloudfront.net/images/aa192bcbc1682c97e1bc6fb422f2afff.png'
  1302. : 'https://d31zlh4on95l9h.cloudfront.net/images/e6ec2ae238ced85b74e0912e988f243e.png'
  1303. "
  1304. @click="sendMessage"
  1305. class="action-btn send-btn-inner"
  1306. :style="{
  1307. opacity: isInputDisabled ? 0.5 : 1,
  1308. cursor: isInputDisabled ? 'not-allowed' : 'pointer',
  1309. }"
  1310. />
  1311. </div>
  1312. </div>
  1313. </el-footer>
  1314. </el-container>
  1315. <el-container
  1316. v-else
  1317. class="main-container"
  1318. :class="{
  1319. collapsed: historyRecordRef?.isCollapsed && !isMobile,
  1320. unCollapsed: !historyRecordRef?.isCollapsed && !isMobile,
  1321. }"
  1322. >
  1323. <el-header class="homepage-head">
  1324. <!-- logo -->
  1325. <div class="homepage-logo">
  1326. <img
  1327. :src="back"
  1328. alt="返回按钮"
  1329. class="backToHomeImg"
  1330. @click="feedbackBack"
  1331. />
  1332. <!-- <img :src="madeInHL" alt="图片加载失败" class="logo2" /> -->
  1333. </div>
  1334. <div class="homepage-right-group">
  1335. <div class="count-badge" @click="showCount">
  1336. <img
  1337. src="https://d31zlh4on95l9h.cloudfront.net/images/74e20c65c9ef2526477c63ad68698a50.png"
  1338. class="action-btn"
  1339. />
  1340. <div class="count-number">{{ UserCount }}</div>
  1341. <div class="clickGetCount">点击获取Token</div>
  1342. </div>
  1343. <div class="backToHomeBtn" @click="backToHome()">
  1344. <img
  1345. src="https://d31zlh4on95l9h.cloudfront.net/images/9cbc5b2eb2327bd04d015c19d8c3f1f9.png"
  1346. alt="返回首页"
  1347. class="backImg"
  1348. />
  1349. <div class="backContent">返回首页</div>
  1350. </div>
  1351. <!-- <img
  1352. :src="announcementBtn"
  1353. class="announcement-btn action-btn"
  1354. @click="showAnnouncement"
  1355. />
  1356. <img
  1357. :src="feedbackBtn"
  1358. class="announcement-btn action-btn"
  1359. @click="showFeedback"
  1360. /> -->
  1361. </div>
  1362. </el-header>
  1363. <!-- 主体部分小人 问题轮询图 对话内容 -->
  1364. <el-main class="homepage-body">
  1365. <component :is="activeTwoTab" />
  1366. </el-main>
  1367. </el-container>
  1368. <!-- 弹窗 -->
  1369. <!-- 新增弹窗组件 -->
  1370. <el-dialog v-if="!isMobile" v-model="dialogVisible" width="48%">
  1371. <!-- 中间内容部分 -->
  1372. <div class="changeMsg">
  1373. <div class="changeInfo">
  1374. <div class="changeImg">
  1375. <img
  1376. :src="userInfo.img"
  1377. alt="头像"
  1378. class="changeImgClass"
  1379. @error="handleImageError"
  1380. />
  1381. </div>
  1382. <div class="changeContent">
  1383. <div class="changeUsername">{{ userInfo.nickname }}</div>
  1384. <div class="changeJwcode">精网号{{ userInfo.jwcode }}</div>
  1385. </div>
  1386. </div>
  1387. <div class="changeRule">
  1388. 兑换规则{{ changeRule.gold }}金币={{ changeRule.token }}Token
  1389. </div>
  1390. </div>
  1391. <div class="changeLevel">
  1392. <div class="changeLevelTitle">兑换Token</div>
  1393. <div class="changeLevelContent">
  1394. <div
  1395. class="changeLevelItems"
  1396. v-for="item in changeLevelList"
  1397. :key="item"
  1398. :class="{
  1399. changeLevelItemsActive: item.position == activeLevel.position,
  1400. }"
  1401. @click="chooseLevel(item)"
  1402. >
  1403. <div class="changeLevelItem">
  1404. <div class="changeLevelItemToken">
  1405. <img
  1406. src="https://d31zlh4on95l9h.cloudfront.net/images/403ef762dd2f335df3b0c9e3fe488375.png"
  1407. alt="token"
  1408. class="changeLevelItemTokenImg"
  1409. />
  1410. {{ item.calculatedPosition }}
  1411. </div>
  1412. <div class="changeLevelItemToken">{{ item.position }} 金币</div>
  1413. </div>
  1414. </div>
  1415. </div>
  1416. </div>
  1417. <div class="changeNow">
  1418. 应付金额
  1419. <div class="changePay">{{ activeLevel.position }}</div>
  1420. (金币余额{{ gold }})
  1421. </div>
  1422. <div class="changeBtn" @click="changeToken">立即兑换</div>
  1423. </el-dialog>
  1424. <el-dialog v-else v-model="dialogVisible" width="80%">
  1425. <!-- 中间内容部分 -->
  1426. <div class="changeMsg">
  1427. <div class="changeInfo">
  1428. <div class="changeImg">
  1429. <img
  1430. :src="userInfo.img"
  1431. alt="头像"
  1432. class="changeImgClass"
  1433. @error="handleImageError"
  1434. />
  1435. </div>
  1436. <div class="changeContent">
  1437. <div class="changeJwcode">精网号{{ userInfo.jwcode }}</div>
  1438. </div>
  1439. </div>
  1440. </div>
  1441. <div class="changeLevel">
  1442. <div class="changeLevelTitle">
  1443. 兑换Token
  1444. <div class="changeRule">
  1445. (兑换规则{{ changeRule.gold }}金币={{ changeRule.token }}Token)
  1446. </div>
  1447. </div>
  1448. <div class="changeLevelContent">
  1449. <div
  1450. class="changeLevelItems"
  1451. v-for="item in changeLevelList"
  1452. :key="item"
  1453. :class="{
  1454. changeLevelItemsActive: item.position == activeLevel.position,
  1455. }"
  1456. @click="chooseLevel(item)"
  1457. >
  1458. <div class="changeLevelItem">
  1459. <div class="changeLevelItemToken">
  1460. <img
  1461. src="https://d31zlh4on95l9h.cloudfront.net/images/403ef762dd2f335df3b0c9e3fe488375.png"
  1462. alt="token"
  1463. class="changeLevelItemTokenImg"
  1464. />
  1465. {{ item.calculatedPosition }}
  1466. </div>
  1467. <div class="changeLevelItemToken">{{ item.position }} 金币</div>
  1468. </div>
  1469. </div>
  1470. </div>
  1471. </div>
  1472. <div class="changeNow">
  1473. 应付金额
  1474. <div class="changePay">{{ activeLevel.position }}</div>
  1475. (金币余额{{ gold }})
  1476. </div>
  1477. <div class="changeBtn" @click="changeToken">立即兑换</div>
  1478. </el-dialog>
  1479. <el-dialog
  1480. v-model="rechargeDialogVisible"
  1481. :width="isMobile ? '60%' : '30%'"
  1482. :show-close="false"
  1483. >
  1484. <div class="rechargeDialogTitle">温馨提示</div>
  1485. <div class="rechargeDialogContent">
  1486. 尊敬的用户您好您当前的金币余额不足无法进行兑换可充值金币后进行兑换点击下方的前往充值可进行充值
  1487. </div>
  1488. <div class="rechargeDialogBtnGroup">
  1489. <div class="recharge" @click="goRecharge">前往充值</div>
  1490. <div
  1491. class="rechargeDialogCancel"
  1492. @click="rechargeDialogVisible = false"
  1493. >
  1494. 取消
  1495. </div>
  1496. </div>
  1497. </el-dialog>
  1498. <el-dialog
  1499. v-model="confirmDialogVisible"
  1500. :width="isMobile ? '60%' : '30%'"
  1501. :show-close="false"
  1502. :align-center="isMobile"
  1503. >
  1504. <div class="confirmDialogTitle">兑换</div>
  1505. <div class="confirmDialogContent">
  1506. 尊敬的用户您好您确认要花费{{ activeLevel.position }}金币兑换{{
  1507. activeLevel.calculatedPosition
  1508. }}Token吗
  1509. </div>
  1510. <div class="confirmDialogBtnGroup">
  1511. <div class="confirmDialogConfirm" @click="goChange()">确认</div>
  1512. <div class="confirmDialogCancel" @click="confirmDialogVisible = false">
  1513. 取消
  1514. </div>
  1515. </div>
  1516. </el-dialog>
  1517. <el-dialog
  1518. v-model="changeSuccessDialogVisible"
  1519. :width="isMobile ? '60%' : '30%'"
  1520. :show-close="false"
  1521. class="changeSuccessDialog"
  1522. :align-center="isMobile"
  1523. >
  1524. <div class="changeSuccessDialogTitle">兑换成功</div>
  1525. <div class="changeSuccessDialogContent">
  1526. 尊敬的用户恭喜您成功兑换{{ activeLevel.calculatedPosition }} Token
  1527. </div>
  1528. </el-dialog>
  1529. <!-- Token规则提示框 -->
  1530. <div
  1531. v-if="tokenRuleDialogVisible"
  1532. class="tokenRuleDialog"
  1533. @click="closeTokenRuleDialog"
  1534. >
  1535. <div class="tokenRuleDialogContent" @click.stop>
  1536. <div class="tokenRuleDialogClose" @click="closeTokenRuleDialog">
  1537. <el-icon><Close /></el-icon>
  1538. </div>
  1539. <div class="tokenRuleDialogTitle">Token规则</div>
  1540. <div class="tokenRuleSection">
  1541. <div class="tokenRuleSectionTitle">Token消耗规则</div>
  1542. <div class="tokenRuleItem">
  1543. "夺宝奇兵大模型""AI情绪大模型"中搜索股票若搜索成功内容正常生成则会消耗1Token
  1544. </div>
  1545. <div class="tokenRuleItem">
  1546. "夺宝奇兵大模型""AI情绪大模型"中搜索股票若搜索有误无法生成内容则不会消耗Token
  1547. </div>
  1548. <div class="tokenRuleItem">
  1549. 搜索同一只股票产出内容相同时只扣除1Token
  1550. </div>
  1551. <div class="tokenRuleItem">
  1552. "夺宝奇兵""AI小财神"中Token是互通的
  1553. </div>
  1554. </div>
  1555. <div class="tokenRuleSection">
  1556. <div class="tokenRuleSectionTitle">Token兑换规则</div>
  1557. <div class="tokenRuleItem">
  1558. 点击右上角"获取Token"即可进入Token兑换页进行金币兑换Token
  1559. </div>
  1560. <div class="tokenRuleItem">
  1561. 金币兑换Token的比例为1金币=1Token一经兑换不予退还
  1562. </div>
  1563. </div>
  1564. <div class="tokenRuleNote">
  1565. 注意报告生成过程中请耐心等待在此期间请勿进行页面刷新操作以免导致报告生成进程中断
  1566. </div>
  1567. </div>
  1568. </div>
  1569. </div>
  1570. </template>
  1571. <style scoped>
  1572. /* 标签栏 */
  1573. .tab-container {
  1574. display: flex;
  1575. margin-bottom: 10px;
  1576. height: 100%;
  1577. position: relative;
  1578. justify-content: center;
  1579. align-items: center;
  1580. gap: 25vw;
  1581. /* 新增右对齐 */
  1582. }
  1583. .pcTabContainer {
  1584. }
  1585. .tab-item {
  1586. cursor: pointer;
  1587. padding: 8px 12px;
  1588. font-size: clamp(18px, 3vw, 20px);
  1589. /* color: #999; */
  1590. color: #fff;
  1591. transition: all 0.3s;
  1592. border-bottom: 2px solid transparent;
  1593. font-weight: bold;
  1594. }
  1595. .tab-item.active {
  1596. /* color: #000;
  1597. border-color: #000; */
  1598. background: linear-gradient(0deg, #ffffff, #fec13e);
  1599. -webkit-background-clip: text;
  1600. background-clip: text;
  1601. -webkit-text-fill-color: transparent;
  1602. color: transparent;
  1603. border-color: #fec13e;
  1604. }
  1605. .tab-item:not(.active):hover {
  1606. color: #999999;
  1607. }
  1608. .tab-content {
  1609. overflow-y: auto;
  1610. overflow-x: hidden;
  1611. scroll-behavior: smooth;
  1612. height: 100%;
  1613. /* 添加平滑滚动效果 */
  1614. }
  1615. .pcTabContent {
  1616. padding: 0 6%;
  1617. }
  1618. .backToHomeImg {
  1619. width: 40px;
  1620. height: 40px;
  1621. margin-left: 10px;
  1622. }
  1623. @media (max-width: 768px) {
  1624. .tab-container {
  1625. gap: 15px;
  1626. padding: 0 10px;
  1627. }
  1628. .tab-item {
  1629. font-size: clamp(14px, 3vw, 16px);
  1630. padding: 6px 10px;
  1631. }
  1632. }
  1633. </style>
  1634. <style scoped>
  1635. html {
  1636. height: 100dvh;
  1637. overflow: hidden !important;
  1638. position: fixed;
  1639. margin: 0;
  1640. padding: 0;
  1641. -webkit-overflow-scrolling: auto;
  1642. /* 禁用 iOS 弹性滚动 */
  1643. }
  1644. body {
  1645. height: 100dvh;
  1646. overflow: clip;
  1647. margin: 0;
  1648. padding: 0;
  1649. -webkit-overflow-scrolling: auto;
  1650. /* 禁用 iOS 弹性滚动 */
  1651. position: fixed;
  1652. }
  1653. #app {
  1654. overflow: hidden;
  1655. height: 100%;
  1656. margin: 0;
  1657. padding: 0;
  1658. }
  1659. .homepage {
  1660. /* height: var(--app-height, 100vh); */
  1661. height: var(--app-height, 100vh);
  1662. margin: 0 auto;
  1663. background-image: url("https://d31zlh4on95l9h.cloudfront.net/images/98c8230d386012c9f1e70bf05a30de5e.png");
  1664. background-size: 100% 100%;
  1665. background-repeat: no-repeat;
  1666. background-position: center;
  1667. display: flex;
  1668. flex-direction: row;
  1669. /* 改为水平布局 */
  1670. overflow: hidden;
  1671. position: fixed;
  1672. top: 0;
  1673. left: 0;
  1674. right: 0;
  1675. bottom: 0;
  1676. width: 100%;
  1677. /* -webkit-overflow-scrolling: touch; */
  1678. }
  1679. .zhezhao {
  1680. width: 100%;
  1681. height: 100%;
  1682. background-color: rgba(0, 0, 0, 0.5);
  1683. z-index: 100;
  1684. position: fixed;
  1685. }
  1686. @media (min-width: 769px) {
  1687. .main-container {
  1688. flex: 1;
  1689. transition: margin-left 0.3s ease;
  1690. display: flex;
  1691. flex-direction: column;
  1692. overflow: hidden;
  1693. }
  1694. .main-container.unCollapsed {
  1695. margin-left: 300px; /* 为历史记录组件留出空间 */
  1696. }
  1697. /* 当历史记录组件折叠时调整主容器边距 */
  1698. .main-container.collapsed {
  1699. margin-left: 40px;
  1700. }
  1701. }
  1702. /* 移动端适配 */
  1703. @media (max-width: 768px) {
  1704. .homepage {
  1705. background-image: url("https://d31zlh4on95l9h.cloudfront.net/images/90d31d7052e729c63acb9e2cb94d1307.png");
  1706. }
  1707. }
  1708. .homepage .el-container {
  1709. height: 100%;
  1710. flex-direction: column;
  1711. display: flex;
  1712. width: 100%;
  1713. overflow: hidden;
  1714. /* 防止容器滚动 */
  1715. }
  1716. .el-container .el-header {
  1717. flex-shrink: 0;
  1718. /* 防止头部压缩 */
  1719. height: auto;
  1720. min-height: 60px;
  1721. padding: 5px 0;
  1722. position: sticky;
  1723. top: 0;
  1724. z-index: 10;
  1725. /* background-color: rgba(255, 255, 255, 0.9); */
  1726. }
  1727. .el-container .el-main {
  1728. flex: 1;
  1729. /* 自动占据剩余空间 */
  1730. overflow: hidden;
  1731. /* 主容器不滚动 */
  1732. display: flex;
  1733. flex-direction: column;
  1734. min-height: 0;
  1735. /* 允许内容区域缩小 */
  1736. position: relative;
  1737. height: auto;
  1738. }
  1739. .el-container .el-footer {
  1740. flex-shrink: 0;
  1741. height: auto;
  1742. min-height: 70px;
  1743. position: sticky;
  1744. bottom: 0;
  1745. z-index: 20;
  1746. background-color: rgba(211, 24, 24, 0);
  1747. box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);
  1748. -webkit-transform: translateZ(0);
  1749. transform: translateZ(0);
  1750. padding-bottom: env(safe-area-inset-bottom, 0);
  1751. /* 适配iPhone X及以上的底部安全区域 */
  1752. }
  1753. .homepage-head {
  1754. padding: 0px;
  1755. display: flex;
  1756. position: relative;
  1757. justify-content: space-between;
  1758. width: 100%;
  1759. }
  1760. .homepage-right-group {
  1761. display: flex;
  1762. /* gap: 8px; */
  1763. align-items: center;
  1764. margin-left: auto;
  1765. margin-right: 5px;
  1766. }
  1767. .homepage-right-group .action-btn {
  1768. height: 40px;
  1769. }
  1770. .count-badge {
  1771. position: relative;
  1772. cursor: pointer;
  1773. }
  1774. .count-badge:hover {
  1775. transform: scale(1.05);
  1776. }
  1777. .count-number {
  1778. position: absolute;
  1779. top: 16px;
  1780. right: 0px;
  1781. width: 68%;
  1782. text-align: center;
  1783. color: #6a00ff;
  1784. font-size: 14px;
  1785. font-weight: bold;
  1786. }
  1787. .clickGetCount {
  1788. width: 100%;
  1789. text-align: center;
  1790. color: white;
  1791. font-size: 12px;
  1792. }
  1793. .backToHomeBtn {
  1794. display: flex;
  1795. flex-direction: column;
  1796. justify-content: center;
  1797. align-items: center;
  1798. cursor: pointer;
  1799. }
  1800. .backToHomeBtn:hover {
  1801. transform: scale(1.05);
  1802. }
  1803. .backImg {
  1804. width: 60%;
  1805. height: auto;
  1806. }
  1807. .backContent {
  1808. width: 100%;
  1809. text-align: center;
  1810. color: white;
  1811. font-size: 12px;
  1812. white-space: nowrap;
  1813. }
  1814. .pc-count-badge {
  1815. width: 200px;
  1816. height: 100%;
  1817. position: absolute;
  1818. right: 20px;
  1819. display: flex;
  1820. }
  1821. .pc-countBtn {
  1822. width: 65%;
  1823. height: 100%;
  1824. position: relative;
  1825. cursor: pointer;
  1826. }
  1827. .pc-countBtn:hover {
  1828. transform: scale(1.05);
  1829. }
  1830. .pc-action-btn {
  1831. width: 100%;
  1832. height: 70%;
  1833. background-image: url("https://d31zlh4on95l9h.cloudfront.net/images/74e20c65c9ef2526477c63ad68698a50.png");
  1834. background-repeat: no-repeat;
  1835. background-size: 100% 100%;
  1836. }
  1837. .pc-count-number {
  1838. position: absolute;
  1839. top: 15px;
  1840. right: 4px;
  1841. width: 68%;
  1842. text-align: center;
  1843. color: #6a00ff;
  1844. font-size: 15px;
  1845. font-weight: bold;
  1846. }
  1847. .pc-clickGetCount {
  1848. width: 100%;
  1849. text-align: center;
  1850. color: white;
  1851. font-size: 12px;
  1852. }
  1853. .pc-backToHomeBtn {
  1854. width: 35%;
  1855. height: 100%;
  1856. display: flex;
  1857. flex-direction: column;
  1858. align-items: center;
  1859. cursor: pointer;
  1860. }
  1861. .pc-backImg {
  1862. width: auto;
  1863. height: 70%;
  1864. }
  1865. .pc-backContent {
  1866. width: 100%;
  1867. text-align: center;
  1868. color: white;
  1869. font-size: 12px;
  1870. }
  1871. .pc-backToHomeBtn:hover {
  1872. transform: scale(1.05);
  1873. }
  1874. .homepage-right-group .announcement-btn {
  1875. cursor: pointer;
  1876. transition: transform 0.3s;
  1877. }
  1878. .homepage-right-group .announcement-btn:hover {
  1879. transform: scale(1.3);
  1880. }
  1881. .homepage-body {
  1882. padding: 0px;
  1883. display: flex;
  1884. flex-direction: column;
  1885. flex: 1;
  1886. min-height: 0;
  1887. /* 允许内容区域缩小 */
  1888. overflow: hidden;
  1889. }
  1890. .main-wrapper {
  1891. height: 100%;
  1892. display: flex;
  1893. flex-direction: column;
  1894. flex: 1;
  1895. min-height: 0;
  1896. /* 允许内容区域缩小 */
  1897. }
  1898. .tab-section {
  1899. flex-shrink: 0;
  1900. /* 禁止伸缩 */
  1901. }
  1902. .tab-content {
  1903. flex: 1;
  1904. overflow-y: auto;
  1905. min-height: 0;
  1906. /* 关键:允许内容收缩 */
  1907. }
  1908. .homepage-logo {
  1909. height: 100%;
  1910. width: fit-content;
  1911. display: flex;
  1912. /* flex-direction: column; */
  1913. align-items: center;
  1914. justify-content: center;
  1915. margin-left: 20px;
  1916. margin-right: auto;
  1917. position: relative;
  1918. gap: 10px;
  1919. }
  1920. .expand {
  1921. font-size: 2.5rem;
  1922. cursor: pointer;
  1923. color: white;
  1924. }
  1925. .logo1 {
  1926. width: 110px;
  1927. height: auto;
  1928. margin-bottom: 8px;
  1929. }
  1930. .logo2 {
  1931. width: 80px;
  1932. height: auto;
  1933. }
  1934. /* 尾部 */
  1935. .homepage-footer {
  1936. display: flex;
  1937. flex-direction: column;
  1938. gap: 5px;
  1939. flex-shrink: 0;
  1940. /* width: 100%; */
  1941. background-color: #fff;
  1942. }
  1943. .pcFooter {
  1944. margin: 0 6% 4%;
  1945. }
  1946. .footer-first-line {
  1947. display: flex;
  1948. justify-content: space-between;
  1949. align-items: center;
  1950. padding: 5px 15px;
  1951. flex-shrink: 0;
  1952. }
  1953. .left-group {
  1954. display: flex;
  1955. gap: 15px;
  1956. }
  1957. .action-btn {
  1958. cursor: pointer;
  1959. transition: transform 0.2s;
  1960. height: 28px;
  1961. }
  1962. .model-btn {
  1963. height: 32px;
  1964. transition: all 0.3s ease;
  1965. }
  1966. .model-btn:hover {
  1967. transform: scale(1.1);
  1968. }
  1969. .send-btn {
  1970. margin-left: 10px;
  1971. height: 33px !important;
  1972. width: auto;
  1973. /* margin-right: 5px; */
  1974. }
  1975. .input-container {
  1976. position: relative;
  1977. width: 100%;
  1978. }
  1979. .send-btn-inner {
  1980. position: absolute;
  1981. right: 10px;
  1982. top: 50%;
  1983. transform: translateY(-50%);
  1984. height: 28px !important;
  1985. width: auto;
  1986. z-index: 10;
  1987. transition: all 0.3s ease;
  1988. }
  1989. .send-btn-inner:hover {
  1990. transform: translateY(-50%) scale(1.1);
  1991. }
  1992. /* 音频播放动画 */
  1993. @keyframes pulse {
  1994. 0% {
  1995. transform: scale(1);
  1996. }
  1997. 50% {
  1998. transform: scale(1.1);
  1999. }
  2000. 100% {
  2001. transform: scale(1);
  2002. }
  2003. }
  2004. .footer-second-line {
  2005. position: relative;
  2006. display: flex;
  2007. align-items: center;
  2008. padding: 5px 15px 10px;
  2009. flex-shrink: 0;
  2010. }
  2011. .msg-icon {
  2012. position: absolute;
  2013. left: 25px;
  2014. top: 50%;
  2015. transform: translateY(-50%);
  2016. width: 24px;
  2017. z-index: 999;
  2018. }
  2019. .msg-input:deep(.el-textarea__inner) {
  2020. border: none !important;
  2021. box-shadow: none !important;
  2022. overflow-y: auto !important;
  2023. transition: all 0.2s ease-out;
  2024. resize: none !important;
  2025. line-height: 1.5 !important;
  2026. max-height: 100px !important;
  2027. padding-right: 45px !important;
  2028. }
  2029. .msg-input {
  2030. min-height: 34px;
  2031. width: 100%;
  2032. border-radius: 5px;
  2033. font-size: 16px;
  2034. transition: all 0.3s ease-out;
  2035. overflow-y: hidden;
  2036. box-shadow: 0 4px 12px rgba(89, 24, 241, 0.3);
  2037. background: #fff;
  2038. z-index: 5;
  2039. /* 添加iOS设备特殊处理 */
  2040. -webkit-appearance: none;
  2041. appearance: none;
  2042. }
  2043. .msg-input:focus {
  2044. outline: none;
  2045. }
  2046. .input::before {
  2047. content: "请输入股票名称或股票代码...";
  2048. position: absolute;
  2049. left: 11px;
  2050. top: 5px;
  2051. color: var(--el-text-color-secondary);
  2052. pointer-events: none;
  2053. white-space: nowrap;
  2054. overflow-x: hidden;
  2055. text-overflow: ellipsis;
  2056. width: 80%;
  2057. z-index: 6;
  2058. }
  2059. .changeMsg {
  2060. display: flex;
  2061. width: 100%;
  2062. margin-bottom: 30px;
  2063. flex-wrap: wrap;
  2064. gap: 20px;
  2065. }
  2066. .changeInfo {
  2067. display: flex;
  2068. background-color: #f8f8f8;
  2069. border-radius: 5px;
  2070. padding: 10px 20px;
  2071. /* width: 40%; */
  2072. white-space: nowrap;
  2073. }
  2074. .changeImg {
  2075. height: 100%;
  2076. display: flex;
  2077. justify-content: center;
  2078. align-items: center;
  2079. margin-right: 10px;
  2080. }
  2081. .changeImgClass {
  2082. width: 50px;
  2083. height: auto;
  2084. }
  2085. .changeContent {
  2086. display: flex;
  2087. flex-direction: column;
  2088. justify-content: center;
  2089. font-weight: bold;
  2090. }
  2091. .changeRule {
  2092. display: flex;
  2093. background-color: #f8f8f8;
  2094. border-radius: 5px;
  2095. text-align: center;
  2096. align-items: center;
  2097. justify-content: center;
  2098. color: #4e86fe;
  2099. white-space: nowrap;
  2100. padding: 5px 20px;
  2101. min-width: 40%;
  2102. }
  2103. .changeLevel {
  2104. display: flex;
  2105. flex-direction: column;
  2106. }
  2107. .changeLevelTitle {
  2108. font-weight: bold;
  2109. margin-bottom: 10px;
  2110. }
  2111. .changeLevelContent {
  2112. display: flex;
  2113. flex-wrap: wrap;
  2114. gap: 15px;
  2115. margin-bottom: 10px;
  2116. }
  2117. .changeLevelItems {
  2118. display: flex;
  2119. background-color: #f8f8f8;
  2120. width: 20%;
  2121. min-width: 70px;
  2122. max-width: 150px;
  2123. justify-content: center;
  2124. align-items: center;
  2125. flex-direction: column;
  2126. border-radius: 10px;
  2127. padding: 5px;
  2128. cursor: pointer;
  2129. }
  2130. .changeLevelItems:hover {
  2131. background-color: #ecf2ff;
  2132. }
  2133. .changeLevelItemsActive {
  2134. border: 1px solid #4e86fe;
  2135. background-color: #ecf2ff;
  2136. }
  2137. .changeLevelItem {
  2138. display: flex;
  2139. flex-direction: column;
  2140. justify-content: center;
  2141. align-items: center;
  2142. }
  2143. .changeLevelItemToken {
  2144. display: flex;
  2145. justify-content: center;
  2146. align-items: center;
  2147. }
  2148. .changeLevelItemTokenImg {
  2149. width: 40px;
  2150. height: 40px;
  2151. }
  2152. .changeNow {
  2153. display: flex;
  2154. white-space: nowrap;
  2155. /* font-weight: bold; */
  2156. margin-bottom: 15px;
  2157. align-items: center;
  2158. }
  2159. .changePay {
  2160. color: #4e86fe;
  2161. margin: 0px 5px;
  2162. font-size: 1.1rem;
  2163. }
  2164. .changeBtn {
  2165. width: 40%;
  2166. max-width: 350px;
  2167. background-color: #4e86fe;
  2168. color: white;
  2169. display: flex;
  2170. justify-content: center;
  2171. align-content: center;
  2172. padding: 10px;
  2173. border-radius: 5px;
  2174. cursor: pointer;
  2175. }
  2176. .changeBtn:hover {
  2177. background-color: #3a73e6;
  2178. }
  2179. .rechargeDialogTitle {
  2180. font-size: 1.7rem;
  2181. /* font-weight: bold; */
  2182. color: #4e86fe;
  2183. display: flex;
  2184. justify-content: center;
  2185. align-items: center;
  2186. letter-spacing: 10px;
  2187. }
  2188. .rechargeDialogContent {
  2189. padding: 20px;
  2190. font-size: 1.2rem;
  2191. }
  2192. .rechargeDialogBtnGroup {
  2193. display: flex;
  2194. font-size: 1.2rem;
  2195. padding: 0px 20px;
  2196. justify-content: space-between;
  2197. }
  2198. .recharge {
  2199. color: white;
  2200. background-color: #4e86fe;
  2201. padding: 10px 20px;
  2202. border-radius: 13px;
  2203. cursor: pointer;
  2204. min-width: 20%;
  2205. text-align: center;
  2206. white-space: nowrap;
  2207. }
  2208. .recharge:hover {
  2209. background-color: #3a73e6;
  2210. }
  2211. .rechargeDialogCancel {
  2212. border: 1px solid rgb(202, 202, 202);
  2213. padding: 10px 20px;
  2214. border-radius: 13px;
  2215. cursor: pointer;
  2216. min-width: 20%;
  2217. text-align: center;
  2218. }
  2219. .rechargeDialogCancel:hover {
  2220. background-color: #ecf2ff;
  2221. }
  2222. .confirmDialogTitle {
  2223. font-size: 1.7rem;
  2224. /* font-weight: bold; */
  2225. color: #4e86fe;
  2226. display: flex;
  2227. justify-content: center;
  2228. align-items: center;
  2229. letter-spacing: 10px;
  2230. }
  2231. .confirmDialogContent {
  2232. padding: 20px;
  2233. font-size: 1.2rem;
  2234. }
  2235. .confirmDialogBtnGroup {
  2236. display: flex;
  2237. font-size: 1.2rem;
  2238. padding: 0px 20px;
  2239. justify-content: space-between;
  2240. }
  2241. .confirmDialogConfirm {
  2242. color: white;
  2243. background-color: #4e86fe;
  2244. padding: 10px 20px;
  2245. border-radius: 13px;
  2246. cursor: pointer;
  2247. min-width: 20%;
  2248. text-align: center;
  2249. }
  2250. .confirmDialogConfirm:hover {
  2251. background-color: #3a73e6;
  2252. }
  2253. .confirmDialogCancel {
  2254. border: 1px solid rgb(202, 202, 202);
  2255. padding: 10px 20px;
  2256. border-radius: 13px;
  2257. cursor: pointer;
  2258. min-width: 20%;
  2259. text-align: center;
  2260. }
  2261. .confirmDialogCancel:hover {
  2262. background-color: #ecf2ff;
  2263. }
  2264. .changeSuccessDialogTitle {
  2265. font-size: 1.7rem;
  2266. font-weight: bold;
  2267. color: #de93a3;
  2268. display: flex;
  2269. justify-content: center;
  2270. align-items: center;
  2271. letter-spacing: 10px;
  2272. }
  2273. .changeSuccessDialogContent {
  2274. padding: 20px;
  2275. font-size: 1.2rem;
  2276. font-weight: bold;
  2277. text-align: center;
  2278. }
  2279. /* Token规则提示框样式 - 与DBQBmodel无权限弹出框一致 */
  2280. .tokenRuleDialog {
  2281. width: 100%;
  2282. display: flex;
  2283. justify-content: center;
  2284. align-items: center;
  2285. position: fixed;
  2286. bottom: 15%;
  2287. color: white;
  2288. z-index: 9999;
  2289. }
  2290. .tokenRuleDialogContent {
  2291. position: relative;
  2292. border-radius: 5px;
  2293. border: 1px solid white;
  2294. padding: 20px 30px;
  2295. background-color: #261176;
  2296. display: flex;
  2297. flex-direction: column;
  2298. justify-content: center;
  2299. align-items: flex-start;
  2300. max-width: 500px;
  2301. width: 70vw;
  2302. max-height: 70vh;
  2303. overflow-y: auto;
  2304. box-sizing: border-box;
  2305. }
  2306. .tokenRuleDialogClose {
  2307. border-radius: 5px;
  2308. border: 1px solid white;
  2309. background-color: #8621d9;
  2310. padding: 2px;
  2311. display: flex;
  2312. justify-content: center;
  2313. align-items: center;
  2314. position: absolute;
  2315. top: 0px;
  2316. right: 0px;
  2317. width: 24px;
  2318. height: 24px;
  2319. cursor: pointer;
  2320. color: white;
  2321. }
  2322. .tokenRuleDialogTitle {
  2323. color: #fec13e;
  2324. font-size: 20px;
  2325. font-weight: bold;
  2326. text-align: center;
  2327. margin-bottom: 20px;
  2328. width: 100%;
  2329. }
  2330. .tokenRuleSection {
  2331. margin-bottom: 15px;
  2332. width: 100%;
  2333. }
  2334. .tokenRuleSectionTitle {
  2335. color: #fec13e;
  2336. font-size: 16px;
  2337. font-weight: bold;
  2338. margin-bottom: 8px;
  2339. }
  2340. .tokenRuleItem {
  2341. color: white;
  2342. font-size: 16px;
  2343. line-height: 1.5;
  2344. margin-bottom: 6px;
  2345. }
  2346. .tokenRuleNote {
  2347. background: rgba(134, 33, 217, 0.3);
  2348. border: 1px solid #fec13e;
  2349. border-radius: 5px;
  2350. padding: 12px;
  2351. color: white;
  2352. font-size: 14px;
  2353. line-height: 1.5;
  2354. margin-top: 15px;
  2355. text-align: center;
  2356. word-wrap: break-word;
  2357. overflow-wrap: break-word;
  2358. }
  2359. @media (max-width: 768px) {
  2360. .tokenRuleDialogContent {
  2361. width: 90vw;
  2362. padding: 15px 20px;
  2363. max-height: 80vh;
  2364. }
  2365. .tokenRuleDialogTitle {
  2366. font-size: 18px;
  2367. margin-bottom: 15px;
  2368. }
  2369. .tokenRuleSectionTitle {
  2370. font-size: 14px;
  2371. margin-bottom: 6px;
  2372. }
  2373. .tokenRuleItem {
  2374. font-size: 14px;
  2375. line-height: 1.4;
  2376. margin-bottom: 5px;
  2377. word-wrap: break-word;
  2378. overflow-wrap: break-word;
  2379. }
  2380. .tokenRuleNote {
  2381. font-size: 12px;
  2382. padding: 10px;
  2383. margin-top: 12px;
  2384. line-height: 1.4;
  2385. }
  2386. .tokenRuleDialogClose {
  2387. width: 20px;
  2388. height: 20px;
  2389. }
  2390. }
  2391. @media (max-width: 768px) {
  2392. .logo1 {
  2393. max-width: 110px;
  2394. width: 25vw;
  2395. }
  2396. .homepage-right-group {
  2397. gap: 10px;
  2398. }
  2399. .homepage-right-group .action-btn {
  2400. height: 30px;
  2401. }
  2402. .count-number {
  2403. top: 10px;
  2404. font-size: 12px;
  2405. }
  2406. .backImg {
  2407. width: 40px;
  2408. height: auto;
  2409. }
  2410. .action-btn {
  2411. height: 21px;
  2412. }
  2413. .footer-second-line {
  2414. padding: 5px 10px 10px;
  2415. }
  2416. .msg-input {
  2417. /* min-height: 44px; */
  2418. /* height: 44px; */
  2419. font-size: 16px;
  2420. }
  2421. /* .changeImg {
  2422. width: 30px;
  2423. height: 30px;
  2424. } */
  2425. .changeLevel {
  2426. flex-direction: horizontal;
  2427. }
  2428. .changeLevelContent {
  2429. display: flex;
  2430. /* justify-content: center; */
  2431. }
  2432. .changeLevelItems {
  2433. flex: 0 0 calc(33% - 20px);
  2434. /* margin-right: auto; */
  2435. }
  2436. .changeLevelTitle {
  2437. align-items: center;
  2438. display: flex;
  2439. }
  2440. .changeRule {
  2441. margin-left: 10px;
  2442. width: 0%;
  2443. background-color: white;
  2444. }
  2445. .changeMsg {
  2446. gap: 10px 20px;
  2447. margin-bottom: 10px;
  2448. }
  2449. .changeImgClass {
  2450. width: 30px;
  2451. height: 30px;
  2452. }
  2453. /* .changeContent {
  2454. font-size: 0.5rem;
  2455. } */
  2456. .changeLevelItems {
  2457. font-size: 0.7rem;
  2458. min-width: 0px;
  2459. }
  2460. .changeLevelItemToken {
  2461. white-space: nowrap;
  2462. }
  2463. .changeLevelItemTokenImg {
  2464. width: 20px;
  2465. height: 20px;
  2466. }
  2467. .changeInfo {
  2468. margin: 0px auto;
  2469. }
  2470. .changeBtn {
  2471. margin: 0px auto;
  2472. }
  2473. .rechargeDialogTitle {
  2474. font-size: 1.3rem;
  2475. }
  2476. .rechargeDialogContent {
  2477. padding: 10px 0px;
  2478. font-size: 0.8rem;
  2479. }
  2480. .rechargeDialogBtnGroup {
  2481. font-size: 1rem;
  2482. }
  2483. .recharge {
  2484. padding: 5px 10px;
  2485. }
  2486. .rechargeDialogCancel {
  2487. padding: 5px 10px;
  2488. }
  2489. .confirmDialogTitle {
  2490. font-size: 1.3rem;
  2491. }
  2492. .confirmDialogContent {
  2493. padding: 10px 0px;
  2494. font-size: 0.8rem;
  2495. }
  2496. .confirmDialogBtnGroup {
  2497. font-size: 1rem;
  2498. }
  2499. .confirmDialogConfirm {
  2500. padding: 5px 10px;
  2501. }
  2502. .confirmDialogCancel {
  2503. padding: 5px 10px;
  2504. }
  2505. .changeSuccessDialogTitle {
  2506. font-size: 1.3rem;
  2507. }
  2508. .changeSuccessDialogContent {
  2509. font-size: 1rem;
  2510. }
  2511. }
  2512. </style>
  2513. <style>
  2514. .changeSuccessDialog {
  2515. background: linear-gradient(180deg, #a2dffe, #b59be1);
  2516. }
  2517. </style>