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.

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