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.

2973 lines
74 KiB

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