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.

1648 lines
42 KiB

5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
3 days ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
2 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
  1. <script setup>
  2. // 导入
  3. import { ref, computed, onMounted, watch, nextTick, onUnmounted, h } from "vue";
  4. import { setHeight } from "../utils/setHeight";
  5. import { getUserCountAPI } from "../api/AIxiaocaishen";
  6. import { ElMessage } from "element-plus";
  7. import AIchat from "./AIchat.vue";
  8. import AIfind from "./AIfind.vue";
  9. import Feedback from "./Feedback.vue";
  10. import Announcement from "./Announcement.vue";
  11. import { useAppBridge } from "../assets/js/useAppBridge.js";
  12. import { useDataStore } from "@/store/dataList.js";
  13. import { useChatStore } from "../store/chat";
  14. import { useEmotionAudioStore } from "../store/emotionAudio";
  15. import { useAudioStore } from "../store/audio";
  16. import _ from "lodash";
  17. import logo from "../assets/img/homePage/logo.png";
  18. import madeInHL from "../assets/img/homePage/madeInHL.png";
  19. import getCountAll from "../assets/img/homePage/get-count-all.png";
  20. import announcementBtn from "../assets/img/homePage/announcement.png";
  21. import thinkActive from "../assets/img/homePage/tail/think-active.png";
  22. import thinkNoActive from "../assets/img/homePage/tail/think-no-active.png";
  23. import languageBtn from "../assets/img/homePage/tail/language.png";
  24. import dbqbButton01 from "../assets/img/AiEmotion/dbqb-button01.png";
  25. import dbqbButton02 from "../assets/img/AiEmotion/dbqb-button02.png";
  26. import emotionButton01 from "../assets/img/AiEmotion/emotion-button01.png";
  27. import emotionButton02 from "../assets/img/AiEmotion/emotion-button02.png";
  28. import voice from "../assets/img/homePage/tail/voice.png";
  29. import voiceNoActive from "../assets/img/homePage/tail/voice-no-active.png";
  30. import sendBtn from "../assets/img/homePage/tail/send.png";
  31. import msgBtn from "../assets/img/homePage/tail/msg.png";
  32. import feedbackBtn from "../assets/img/Feedback/feedbackBtn.png";
  33. import AiEmotion from "./AiEmotion.vue";
  34. import HistoryRecord from "./components/HistoryRecord.vue";
  35. import VConsole from "vconsole";
  36. const vConsole = new VConsole();
  37. const isMobile = ref(null);
  38. // 获取 AiEmotion 组件的 ref
  39. const aiEmotionRef = ref(null);
  40. // 获取历史记录组件的 ref
  41. const historyRecordRef = ref(null);
  42. // import { useUserStore } from "../store/userPessionCode.js";
  43. const { getQueryVariable, setActiveTabIndex } = useDataStore();
  44. const dataStore = useDataStore();
  45. const chatStore = useChatStore();
  46. // 变量
  47. // 音频管理
  48. const emotionAudioStore = useEmotionAudioStore();
  49. const audioStore = useAudioStore();
  50. // 根据当前页面类型获取对应的音频store
  51. const getCurrentAudioStore = () => {
  52. return activeTab.value === "AiEmotion" ? emotionAudioStore : audioStore;
  53. };
  54. const isVoice = computed(() => {
  55. const currentStore = getCurrentAudioStore();
  56. return currentStore.isVoiceEnabled;
  57. });
  58. const toggleVoice = () => {
  59. const currentStore = getCurrentAudioStore();
  60. if (!currentStore.isVoiceEnabled) {
  61. // 如果语音功能关闭,先开启语音功能
  62. currentStore.toggleVoice();
  63. } else {
  64. // 如果语音功能开启,则切换播放/暂停状态
  65. if (currentStore.currentAudioUrl || currentStore.ttsUrl) {
  66. // 有音频时切换播放/暂停
  67. currentStore.togglePlayPause();
  68. } else {
  69. // 没有音频时关闭语音功能
  70. currentStore.toggleVoice();
  71. }
  72. }
  73. };
  74. // 将默认值改为从 sessionStorage 中获取,如果没有则使用默认值 'aifindCow'为第一个默认tab
  75. const activeTab = ref(sessionStorage.getItem("activeTabAI") || "AIchat");
  76. const activeIndex = ref(
  77. parseInt(sessionStorage.getItem("activeIndexAI") || "0")
  78. );
  79. const tabs = computed(() => [
  80. {
  81. name: "AIchat",
  82. label: "夺宝奇兵大模型",
  83. },
  84. // {
  85. // name: "AIfind",
  86. // label: "发现",
  87. // },
  88. {
  89. name: "AiEmotion",
  90. label: "AI情绪大模型",
  91. },
  92. ]);
  93. // 修改 setActiveTab 方法,添加一个可选参数 forceAIchat
  94. const setActiveTab = (tab, index, forceAIchat = false) => {
  95. isScrolling.value = false; //回复滚动到底部方法
  96. isAnnouncementVisible.value = false;
  97. // 重置输入框禁用状态,防止页面切换时状态残留
  98. console.log("tab", tab, "index", index);
  99. if (tab == "AIchat") {
  100. isInputDisabled.value = chatStore.chatInput;
  101. console.log("切换到AIchat页面,输入框状态为", isInputDisabled.value);
  102. } else if (tab == "AiEmotion") {
  103. isInputDisabled.value = chatStore.emotionInput;
  104. console.log("切换到AiEmotion页面,输入框状态为", isInputDisabled.value);
  105. } else {
  106. isInputDisabled.value = false;
  107. }
  108. if (forceAIchat && activeTab.value !== "AIchat") {
  109. activeTab.value = "AIchat";
  110. activeIndex.value = 0;
  111. sessionStorage.setItem("activeTabAI", "AIchat");
  112. sessionStorage.setItem("activeIndexAI", "0");
  113. } else {
  114. activeTab.value = tab;
  115. activeIndex.value = index;
  116. sessionStorage.setItem("activeTabAI", tab);
  117. sessionStorage.setItem("activeIndexAI", index.toString());
  118. }
  119. setActiveTabIndex(index);
  120. console.log(tab, index, "tab, index");
  121. setHeight(document.getElementById("testId")); // 给父组件发送窗口高度
  122. };
  123. // 修改 activeComponent 的计算逻辑
  124. const activeComponent = computed(() => {
  125. if (activeTab.value === "AIchat") {
  126. return AIchat;
  127. } else if (activeTab.value === "AIfind") {
  128. return AIfind;
  129. } else if (activeTab.value === "AiEmotion") {
  130. return AiEmotion; // 新增逻辑
  131. }
  132. });
  133. const activeTwoTab = computed(() => {
  134. if (isAnnouncementVisible.value) {
  135. return Announcement;
  136. } else {
  137. return Feedback;
  138. }
  139. });
  140. // 新增一个方法,调用时先判断是否处于 AIchat,若不在则跳转到 AIchat
  141. const ensureAIchat = () => {
  142. setActiveTab("AIchat", 0, true);
  143. };
  144. // 获取次数
  145. const UserCount = computed(() => chatStore.UserCount);
  146. const getCount = () => {
  147. console.log("点击了获取次数的按钮");
  148. };
  149. // 深度思考
  150. const isThinking = ref(true);
  151. const toggleThink = () => {
  152. isThinking.value = !isThinking.value;
  153. };
  154. // 发送消息
  155. const message = ref("");
  156. // 传输对象
  157. const messages = ref([]);
  158. // 信息加载状态
  159. const isLoading = computed(() => {
  160. chatStore.isLoading;
  161. });
  162. // 输入框禁用状态
  163. const isInputDisabled = ref(false);
  164. // 添加用户消息
  165. const updateMessage = (title) => {
  166. message.value = title;
  167. console.log("updateMessage 的值:", title);
  168. };
  169. watch(
  170. () => chatStore.announcementMsg,
  171. (newVal) => {
  172. console.log("监听到公告改变", chatStore.announcementMsg);
  173. if (chatStore.announcementMsg && !isInputDisabled.value) {
  174. message.value = chatStore.announcementMsg;
  175. chatStore.announcementMsg = null;
  176. }
  177. }
  178. );
  179. watch(
  180. () => dataStore.isFeedback,
  181. async (newVal) => {
  182. if (!dataStore.isFeedback) {
  183. // 重置公告页面显示状态
  184. isAnnouncementVisible.value = false;
  185. await nextTick();
  186. // 监听页面高度
  187. throttledHeightListener();
  188. }
  189. }
  190. );
  191. watch(
  192. () => chatStore.chatInput,
  193. async (newVal) => {
  194. if(activeTab.value=='AIchat'){
  195. isInputDisabled.value=chatStore.chatInput;
  196. }
  197. }
  198. );
  199. watch(
  200. () => chatStore.emotionInput,
  201. async (newVal) => {
  202. if(activeTab.value=='AiEmotion'){
  203. isInputDisabled.value=chatStore.emotionInput;
  204. }
  205. }
  206. );
  207. const sendMessage = async () => {
  208. if (
  209. localStorage.getItem("localToken") == null ||
  210. localStorage.getItem("localToken") == ""
  211. ) {
  212. ElMessage.error("请先登录");
  213. return;
  214. }
  215. // 检查输入内容是否为空
  216. if (!message.value || !message.value.trim()) {
  217. ElMessage.warning("输入内容不能为空");
  218. return;
  219. }
  220. isScrolling.value = false;
  221. // 注意:历史记录会在消息发送后自动更新,无需手动添加
  222. // 取消历史记录选中状态
  223. if (historyRecordRef) {
  224. historyRecordRef.value.selectedRecordId = null;
  225. }
  226. // 判断当前是否为 AiEmotion 组件
  227. if (activeTab.value === "AiEmotion") {
  228. // 禁用输入框
  229. isInputDisabled.value = true;
  230. chatStore.emotionInput = true;
  231. // 调用 AiEmotion 组件的 handleSendMessage 方法
  232. aiEmotionRef.value?.handleSendMessage(message.value, () => {
  233. // 打字机效果完成后的回调,重新启用输入框
  234. isInputDisabled.value = false;
  235. chatStore.emotionInput = false;
  236. });
  237. message.value = ""; // 清空输入框
  238. return;
  239. }
  240. // 调用 ensureAIchat 确保跳转到 AIchat 页面
  241. ensureAIchat();
  242. if (isInputDisabled.value) return;
  243. isInputDisabled.value = true;
  244. chatStore.chatInput = true;
  245. const messageContent = message.value;
  246. // 重置消息输入框
  247. message.value = "";
  248. setTimeout(() => {
  249. console.log("延时后添加消息", messageContent);
  250. // 发送消息时,设置 isLoading 为 true
  251. messages.value = [
  252. ...messages.value,
  253. {
  254. sender: "user",
  255. content: messageContent,
  256. audioArray: [],
  257. audioStatus: false,
  258. },
  259. ];
  260. console.log(messages.value, "messages.value");
  261. }, 200);
  262. };
  263. // 重新启用输入框的方法
  264. const enableInput = () => {
  265. console.log("解除禁用");
  266. isInputDisabled.value = false;
  267. };
  268. // 处理历史记录选择
  269. const handleHistorySelect = (stockData) => {
  270. console.log("接收到历史记录数据:", stockData);
  271. // 如果当前不在AiEmotion页面,切换到AiEmotion页面
  272. // if (activeTab.value !== 'AiEmotion') {
  273. // setActiveTab('AiEmotion', 1);
  274. // }
  275. // 等待组件渲染完成后调用addStock方法
  276. nextTick(() => {
  277. if (aiEmotionRef.value && aiEmotionRef.value.addStock) {
  278. aiEmotionRef.value.addStock(stockData);
  279. } else {
  280. console.error("AiEmotion组件或addStock方法不可用");
  281. }
  282. });
  283. };
  284. // 公告
  285. // 新增一个变量来控制是否显示公告页面
  286. const isAnnouncementVisible = ref(false);
  287. const showAnnouncement = async () => {
  288. console.log("打开公告");
  289. dataStore.isFeedback = true; // 显示用户反馈页面
  290. isScrolling.value = false; //回复滚动到底部方法
  291. isAnnouncementVisible.value = true; // 显示公告页面
  292. if (isMobile.value) {
  293. if (historyRecordRef) {
  294. historyRecordRef.value.isCollapsed = true;
  295. }
  296. }
  297. };
  298. // 跳转用户反馈
  299. const showFeedback = () => {
  300. console.log("打开用户反馈");
  301. dataStore.isFeedback = true; // 显示用户反馈页面
  302. isAnnouncementVisible.value = false; // 显示反馈页面
  303. if (isMobile.value) {
  304. if (historyRecordRef) {
  305. historyRecordRef.value.isCollapsed = true;
  306. }
  307. }
  308. };
  309. // 点击剩余次数会弹出的弹窗
  310. // 新增一个 ref 来控制弹窗的显示与隐藏
  311. const dialogVisible = ref(false);
  312. // 获取次数
  313. const showCount = () => {
  314. console.log("显示剩余次数");
  315. // 显示弹窗
  316. dialogVisible.value = true;
  317. console.log("dialogVisible 的值:", dialogVisible.value); // 添加日志确认
  318. };
  319. // 保证发送消息时,滚动屏在底部
  320. const tabContentAIchat = ref(null);
  321. const tabContentAiEmotion = ref(null);
  322. const isScrolling = ref(false); //判断用户是否在滚动
  323. // 获取当前活动页面的滚动容器
  324. const getCurrentScrollContainer = () => {
  325. if (activeTab.value === 'AIchat') {
  326. return tabContentAIchat.value;
  327. } else if (activeTab.value === 'AiEmotion') {
  328. return tabContentAiEmotion.value;
  329. }
  330. return null;
  331. };
  332. const smoothScrollToBottom = async () => {
  333. // console.log("调用滚动到底部的方法");
  334. // await nextTick();
  335. const container = getCurrentScrollContainer();
  336. // console.log(container, 'container')
  337. // console.log(isScrolling.value, 'isScrolling.value')
  338. if (!container) return;
  339. await nextTick(); // 确保在DOM更新后执行
  340. if (!isScrolling.value) {
  341. container.scrollTop = container.scrollHeight - container.offsetHeight;
  342. // container.scrollTop = container.scrollHeight;
  343. // container.scrollTop = container.offsetHeight;
  344. // container.scrollTop = container.scrollHeight + container.offsetHeight;
  345. // console.log(container.scrollHeight, container.offsetHeight, container.scrollHeight - container.offsetHeight, container.scrollTop, "总长度", "可视长度", "位置")
  346. }
  347. };
  348. const throttledSmoothScrollToBottom = _.throttle(smoothScrollToBottom, 300, {
  349. trailing: false,
  350. });
  351. watch(
  352. () => chatStore.messages.length,
  353. () => {
  354. // console.log('messages变化了')
  355. // 只有在AIchat页面时才执行自动滚动
  356. if (activeTab.value === "AIchat") {
  357. throttledSmoothScrollToBottom();
  358. }
  359. // setTimeout(throttledSmoothScrollToBottom, 100);
  360. },
  361. { deep: false, immediate: true }
  362. );
  363. watch(
  364. () => chatStore.dbqbClickRecord,
  365. async (newValue, oldValue) => {
  366. const container = getCurrentScrollContainer();
  367. if (!container) return;
  368. await nextTick(); // 确保在DOM更新后执行
  369. container.scrollTop = 0;
  370. }
  371. );
  372. watch(
  373. activeTab,
  374. async () => {
  375. console.log("activeTab变化了", activeTab.value);
  376. if (activeTab.value == "AIchat" || activeTab.value == "AiEmotion") {
  377. if (historyRecordRef.value && historyRecordRef.value.getHistoryList) {
  378. const result = historyRecordRef.value.getHistoryList({
  379. model: activeTab.value == "AIchat" ? 1 : 2,
  380. token: localStorage.getItem("localToken"),
  381. });
  382. }
  383. }
  384. if (activeTab.value === "AIchat") {
  385. isScrolling.value = false; //回复滚动到底部方法
  386. setTimeout(() => {
  387. // throttledSmoothScrollToBottom();
  388. }, 100);
  389. }
  390. // AiEmotion页面不执行自动滚动,避免刷新后滚动到底部
  391. // setTimeout(throttledSmoothScrollToBottom, 100);
  392. },
  393. { deep: true, immediate: true }
  394. );
  395. // 获取token的核心函数
  396. const fnGetToken = () => {
  397. // console.log('进入fnGetToken')
  398. window.JWready = (ress) => {
  399. // console.log('进入JWready')
  400. try {
  401. ress = JSON.parse(ress);
  402. // console.log(ress, 'ress')
  403. } catch (error) {
  404. console.log(error, "fnGetToken error");
  405. } //platform为5是app端
  406. // platform.value = ress.data.platform
  407. // 处理平台判断
  408. console.log(ress.data.platform, "ress.data.platform");
  409. if (!ress.data.platform) {
  410. // 非App环境通过URL参数获取
  411. localStorage.setItem(
  412. "localToken",
  413. decodeURIComponent(String(getQueryVariable("token")))
  414. );
  415. // localStorage.setItem('localToken', "+SsksARQgUHIbIG3rRnnbZi0+fEeMx8pywnIlrmTxo5EOPR/wjWDV7w7+ZUseiBtf9kFa/atmNx6QfSpv5w")
  416. } else {
  417. // App环境通过桥接获取
  418. useAppBridge().packageFun(
  419. "JWgetStorage",
  420. (response) => {
  421. const res = JSON.parse(response); // 解析返回的结果
  422. localStorage.setItem("localToken", res.data);
  423. // localStorage.setItem('localToken', "+SsksARQgUHIbIG3rRnnbZi0+fEeMx8pywnIlrmTxo5EOPR/wjWDV7w7+ZUseiBtf9kFa/atmNx6QfSpv5w")
  424. },
  425. 5,
  426. {
  427. key: "token",
  428. }
  429. );
  430. }
  431. };
  432. // console.log('出来了')
  433. // 触发App桥接
  434. useAppBridge().packageFun("JWwebReady", () => {}, 5, {});
  435. };
  436. // 在setTimeout中延迟执行
  437. setTimeout(() => {
  438. fnGetToken();
  439. }, 800);
  440. const heightListener = () => {
  441. const tabContainer = getCurrentScrollContainer();
  442. if (!tabContainer) return;
  443. let befortop = 0;
  444. const scrollHandler = () => {
  445. const aftertop = tabContainer.scrollTop;
  446. // 新增底部判断逻辑
  447. const isBottom =
  448. aftertop + tabContainer.offsetHeight + 70 >= tabContainer.scrollHeight;
  449. if (activeTab.value === "AIchat") {
  450. if (aftertop - befortop > 0) {
  451. // console.log("向下滚动");
  452. isScrolling.value = true;
  453. } else {
  454. // console.log("向上滚动");
  455. isScrolling.value = true;
  456. }
  457. // 添加底部状态检测
  458. if (isBottom) {
  459. // console.log("滚动到底部");
  460. isScrolling.value = false;
  461. }
  462. }
  463. befortop = aftertop;
  464. };
  465. // console.log(isScrolling.value, 'isScrolling.value')
  466. tabContainer.addEventListener("scroll", scrollHandler);
  467. };
  468. const throttledHeightListener = _.throttle(heightListener, 500, {
  469. trailing: false,
  470. });
  471. // const goToRecharge = () => {
  472. // console.log("点击充值");
  473. // // http://39.101.133.168:8919/payment/recharge/index?
  474. // // url=http%3A%2F%2Flocalhost%3A8080%2FLiveActivity%2Fpck
  475. // // &platform=1
  476. // // &token=+S4h5QEE1hTIb4CxphrnbZi0+fEeMx8pywnIlrmTmo4QO6IolWnVWu5r+J4rKXMwK41UPfKqyIp+RvWmtM8
  477. // const userAgent = navigator.userAgent.toLowerCase();
  478. // const mobileKeywords = ["mobile", "android", "iphone", "ipad", "ipod"];
  479. // const isMobile = mobileKeywords.some((keyword) =>
  480. // userAgent.includes(keyword)
  481. // );
  482. // console.log(isMobile ? "手机" : "电脑");
  483. // const url = encodeURI("http://39.101.133.168:8857/aixiaocaishen/homePage");
  484. // console.log(url, "url");
  485. // const platform = isMobile ? 2 : 1;
  486. // const token = encodeURIComponent(localStorage.getItem("localToken"));
  487. // console.log(token, "token");
  488. // const rechargeUrl =
  489. // "http://39.101.133.168:8919/payment/recharge/index?" +
  490. // "url=" +
  491. // url +
  492. // "&platform=" +
  493. // platform +
  494. // "&token=" +
  495. // token;
  496. // console.log(rechargeUrl, "rechargeUrl");
  497. // window.location.href = rechargeUrl;
  498. // // window.open(rechargeUrl)
  499. // };
  500. const adjustFooterPosition = (height) => {
  501. const html = document.querySelector("html");
  502. const body = document.querySelector("body");
  503. const isAndroid = /Android/i.test(navigator.userAgent);
  504. if (isAndroid) {
  505. console.log("是安卓设备");
  506. console.log("window.visualViewport", window.visualViewport.height);
  507. const homePage = document.querySelector(".homepage");
  508. homePage.style.height = `${height}px`;
  509. // homePage.style.height = `460px`;
  510. html.scrollTop = 0;
  511. } else {
  512. console.log("非安卓设备");
  513. console.log("调整底部位置", height);
  514. const homePage = document.querySelector(".homepage");
  515. homePage.style.height = `${height}px`;
  516. html.scrollTop = 0;
  517. }
  518. setTimeout(() => {
  519. // 隐藏滚动条
  520. html.style.overflow = "hidden";
  521. body.style.overflow = "hidden";
  522. }, 200);
  523. };
  524. // 是否正在输入法组合
  525. const inputing = ref(false);
  526. const onFocus = function () {
  527. const visualViewport = window.visualViewport;
  528. // 获取可视区域高度
  529. setTimeout(() => {
  530. console.log("输入框聚焦");
  531. console.log(visualViewport.height, "visualViewport.height");
  532. const keyboardHeight = window.innerHeight - visualViewport.height;
  533. console.log(window.innerHeight, "window.innerHeight");
  534. console.log(keyboardHeight, "keyboardHeight");
  535. adjustFooterPosition(visualViewport.height);
  536. }, 200);
  537. };
  538. const onBlur = function () {
  539. inputing.value = false;
  540. const visualViewport = window.visualViewport;
  541. setTimeout(() => {
  542. console.log("输入框失焦");
  543. const keyboardHeight = window.innerHeight - visualViewport.height;
  544. console.log(window.innerHeight, "window.innerHeight");
  545. console.log(visualViewport.height, "visualViewport.height");
  546. console.log(keyboardHeight, "keyboardHeight");
  547. adjustFooterPosition(visualViewport.height);
  548. }, 200);
  549. };
  550. // window.addEventListener("resize", () => {
  551. // // 检测是否为iOS设备
  552. // const isIOS = /iPhone|iPad|iPod|ios/i.test(navigator.userAgent);
  553. // console.log("是否为iOS设备:", isIOS);
  554. // if (!isIOS) {
  555. // console.log("窗口大小变化");
  556. // const homePage = document.querySelector(".homepage");
  557. // homePage.style.height = `${window.innerHeight}px`;
  558. // }
  559. // });
  560. let touchmoveHandlerRef = null;
  561. const touchmoveHandler = (e) => {
  562. if (!dataStore.isFeedback) {
  563. if (historyRecordRef) {
  564. if (!historyRecordRef.value.isCollapsed) {
  565. return;
  566. }
  567. }
  568. // 判断触摸目标是否在当前活动页面的可滚动区域内
  569. const currentContainer = getCurrentScrollContainer();
  570. const isScrollableArea = currentContainer && currentContainer.contains(e.target);
  571. // 如果不在可滚动区域,则阻止滚动
  572. if (!isScrollableArea) {
  573. e.preventDefault();
  574. }
  575. }
  576. };
  577. const judgeDevice = async () => {
  578. // 延时300ms
  579. await new Promise((resolve) => setTimeout(resolve, 200));
  580. const userAgent = navigator.userAgent;
  581. isMobile.value =
  582. /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
  583. userAgent
  584. );
  585. console.log("当前设备为:", isMobile.value ? "移动端" : "PC端");
  586. };
  587. const throttledJudgeDevice = _.throttle(judgeDevice, 300, {
  588. trailing: false,
  589. });
  590. const expandHistory = () => {
  591. // if (activeTab.value == "AIchat" || activeTab.value == "AiEmotion") {
  592. // historyRecordRef.value.getHistoryList({
  593. // token: localStorage.getItem("localToken"),
  594. // model: activeTab.value == "AIchat" ? 1 : 2,
  595. // });
  596. // }
  597. if (
  598. historyRecordRef.value &&
  599. historyRecordRef.value.isCollapsed !== undefined
  600. ) {
  601. console.log("存在");
  602. historyRecordRef.value.isCollapsed = !historyRecordRef.value.isCollapsed;
  603. if (activeTab.value == "AIchat") {
  604. chatStore.aiChatCall = true;
  605. } else if (activeTab.value == "AiEmotion") {
  606. chatStore.aiEmotionCall = true;
  607. }
  608. }
  609. };
  610. const backToHome = () => {
  611. if (isMobile.value) {
  612. console.log("用户是移动端");
  613. // 调用原生方法跳转到首页
  614. uni.postMessage({
  615. data: {
  616. val: {
  617. name: "JWopenView",
  618. extra: {
  619. data: {
  620. type: 3,
  621. },
  622. },
  623. },
  624. },
  625. });
  626. } else {
  627. console.log("用户是pc端");
  628. const env = import.meta.env.VITE_ENV;
  629. console.log("当前的环境为:", env);
  630. if (env == "development" || env == "test") {
  631. window.parent.location.href =
  632. "http://121.89.234.155:8807/hljw/homepage?menu=999999991";
  633. } else if (env == "product") {
  634. window.parent.location.href =
  635. "https://web.homilychart.com/product/hljw/homepage?menu=999999991";
  636. } else if (env == "production") {
  637. window.parent.location.href =
  638. "https://web.homilychart.com/hljw/homepage?menu=999999991";
  639. }
  640. // window.parent.location.href = window.parent.document.referrer
  641. }
  642. };
  643. onMounted(async () => {
  644. throttledJudgeDevice();
  645. // 禁用全局触摸滚动
  646. touchmoveHandlerRef = touchmoveHandler;
  647. document.addEventListener("touchmove", touchmoveHandlerRef, {
  648. passive: false,
  649. });
  650. setHeight(document.getElementById("testId")); // 给父组件发送窗口高度
  651. // 获取次数
  652. await chatStore.getUserCount();
  653. // 滚动到底部
  654. throttledSmoothScrollToBottom();
  655. // 监听页面高度
  656. throttledHeightListener();
  657. // 添加输入框焦点处理
  658. // handleInputFocus();
  659. // 初始化视口高度变量
  660. // updateAppHeight();
  661. // 添加原生事件监听器
  662. window.addEventListener("resize", throttledJudgeDevice);
  663. });
  664. onUnmounted(() => {
  665. window.removeEventListener("resize", throttledJudgeDevice);
  666. if (touchmoveHandlerRef) {
  667. console.log("卸载touchmoveHandlerRef组件");
  668. document.removeEventListener("touchmove", touchmoveHandlerRef);
  669. }
  670. });
  671. </script>
  672. <template>
  673. <div class="homepage" id="testId">
  674. <!-- 历史记录组件 -->
  675. <HistoryRecord
  676. ref="historyRecordRef"
  677. :current-type="activeTab"
  678. @selectRecord="handleHistorySelect"
  679. :isMobile="isMobile"
  680. @showAnnouncement="showAnnouncement"
  681. @showFeedback="showFeedback"
  682. />
  683. <div
  684. v-if="isMobile && !historyRecordRef?.isCollapsed"
  685. class="zhezhao"
  686. @click="expandHistory"
  687. ></div>
  688. <el-container
  689. v-if="!dataStore.isFeedback"
  690. class="main-container"
  691. :class="{
  692. collapsed: historyRecordRef?.isCollapsed && !isMobile,
  693. unCollapsed: !historyRecordRef?.isCollapsed && !isMobile,
  694. }"
  695. >
  696. <!-- AI小财神头部 logo 次数 公告 -->
  697. <el-header class="homepage-head">
  698. <!-- logo -->
  699. <div class="homepage-logo" v-if="isMobile">
  700. <img
  701. class="expand"
  702. @click="expandHistory"
  703. src="https://d31zlh4on95l9h.cloudfront.net/images/37fe3d79a8a700f6c674c9f0e7af066b.png"
  704. alt="icon"
  705. />
  706. <img :src="logo" alt="图片加载失败" class="logo1" />
  707. <!-- <img :src="madeInHL" alt="图片加载失败" class="logo2" /> -->
  708. </div>
  709. <div class="homepage-right-group" v-if="isMobile">
  710. <div class="count-badge" @click="showCount">
  711. <img
  712. src="https://d31zlh4on95l9h.cloudfront.net/images/74e20c65c9ef2526477c63ad68698a50.png"
  713. class="action-btn"
  714. />
  715. <div class="count-number">{{ UserCount }}</div>
  716. <div class="clickGetCount">点击获取次数</div>
  717. </div>
  718. <div class="backToHomeBtn" @click="backToHome()">
  719. <img
  720. src="https://d31zlh4on95l9h.cloudfront.net/images/d8b388e461423f79087ddbe016002217.png"
  721. alt="返回首页"
  722. class="backImg"
  723. />
  724. <div class="backContent">返回首页</div>
  725. </div>
  726. <!-- <img
  727. :src="announcementBtn"
  728. class="announcement-btn action-btn"
  729. @click="showAnnouncement"
  730. />
  731. <img
  732. :src="feedbackBtn"
  733. class="announcement-btn action-btn"
  734. @click="showFeedback"
  735. /> -->
  736. </div>
  737. </el-header>
  738. <!-- 主体部分小人 问题轮询图 对话内容 -->
  739. <el-main class="homepage-body">
  740. <div class="main-wrapper">
  741. <section class="tab-section">
  742. <div
  743. class="tab-container"
  744. :class="{
  745. pcTabContainer: !isMobile,
  746. }"
  747. >
  748. <div
  749. v-for="(tab, index) in tabs"
  750. :key="tab.name"
  751. @click="setActiveTab(tab.name, index)"
  752. :class="[
  753. 'tab-item',
  754. { active: activeIndex === index && !isAnnouncementVisible },
  755. ]"
  756. >
  757. <span>{{ tab.label }}</span>
  758. </div>
  759. <div v-if="!isMobile" class="pc-count-badge">
  760. <div class="pc-countBtn" @click="showCount">
  761. <div class="pc-action-btn">
  762. <div class="pc-count-number">{{ UserCount }}</div>
  763. </div>
  764. <div class="pc-clickGetCount">点击获取次数</div>
  765. </div>
  766. <div class="pc-backToHomeBtn" @click="backToHome()">
  767. <img
  768. src="https://d31zlh4on95l9h.cloudfront.net/images/d8b388e461423f79087ddbe016002217.png"
  769. alt="返回首页"
  770. class="pc-backImg"
  771. />
  772. <div class="pc-backContent">返回首页</div>
  773. </div>
  774. </div>
  775. </div>
  776. </section>
  777. <!-- AIchat页面的独立滚动容器 -->
  778. <div
  779. v-show="activeTab === 'AIchat'"
  780. class="tab-content"
  781. :class="{
  782. pcTabContent: !isMobile,
  783. }"
  784. ref="tabContentAIchat"
  785. >
  786. <component
  787. v-if="activeTab === 'AIchat'"
  788. :is="activeComponent"
  789. :messages="messages"
  790. @updateMessage="updateMessage"
  791. @sendMessage="sendMessage"
  792. @ensureAIchat="ensureAIchat"
  793. @enableInput="enableInput"
  794. />
  795. </div>
  796. <!-- AiEmotion页面的独立滚动容器 -->
  797. <div
  798. v-show="activeTab === 'AiEmotion'"
  799. class="tab-content"
  800. :class="{
  801. pcTabContent: !isMobile,
  802. }"
  803. ref="tabContentAiEmotion"
  804. >
  805. <component
  806. v-if="activeTab === 'AiEmotion'"
  807. :is="activeComponent"
  808. :messages="messages"
  809. @updateMessage="updateMessage"
  810. @sendMessage="sendMessage"
  811. @ensureAIchat="ensureAIchat"
  812. @enableInput="enableInput"
  813. ref="aiEmotionRef"
  814. />
  815. </div>
  816. </div>
  817. </el-main>
  818. <!-- 尾部 问题输入框 深度思考 多语言 语音播报 -->
  819. <el-footer
  820. class="homepage-footer"
  821. :class="{
  822. pcFooter: !isMobile,
  823. }"
  824. id="input"
  825. >
  826. <!-- 第一行按钮 -->
  827. <div class="footer-first-line">
  828. <div class="left-group">
  829. <!-- <img v-if="isThinking" :src="thinkActive" @click="toggleThink" class="action-btn" />
  830. <img v-else :src="thinkNoActive" @click="toggleThink" class="action-btn" />
  831. <img :src="languageBtn" @click="changeLanguage" class="action-btn" /> -->
  832. <!-- 夺宝奇兵大模型按钮 -->
  833. <img
  834. :src="activeTab === 'AIchat' ? dbqbButton01 : dbqbButton02"
  835. @click="setActiveTab('AIchat', 0)"
  836. class="action-btn model-btn"
  837. alt="夺宝奇兵大模型"
  838. />
  839. <!-- AI情绪大模型按钮 -->
  840. <img
  841. :src="
  842. activeTab === 'AiEmotion' ? emotionButton01 : emotionButton02
  843. "
  844. @click="setActiveTab('AiEmotion', 1)"
  845. class="action-btn model-btn"
  846. alt="AI情绪大模型"
  847. />
  848. <!-- <img v-if="
  849. getCurrentAudioStore().isVoiceEnabled &&
  850. getCurrentAudioStore().isPlaying
  851. " :src="voice" @click="toggleVoice" class="action-btn" style="animation: pulse 1.5s infinite" />
  852. <img v-else-if="
  853. getCurrentAudioStore().isVoiceEnabled &&
  854. !getCurrentAudioStore().isPlaying
  855. " :src="voiceNoActive" @click="toggleVoice" class="action-btn" />
  856. <img v-else :src="voiceNoActive" @click="toggleVoice" class="action-btn" /> -->
  857. </div>
  858. </div>
  859. <!-- 第二行输入框 -->
  860. <div class="footer-second-line">
  861. <!-- <img :src="msgBtn" class="msg-icon" /> -->
  862. <div class="input-container">
  863. <el-input
  864. type="textarea"
  865. v-model="message"
  866. @focus="onFocus"
  867. @blur="onBlur"
  868. :autosize="{ minRows: 1, maxRows: 4 }"
  869. class="msg-input"
  870. @keydown.enter.exact.prevent="
  871. isLoading || isInputDisabled ? null : sendMessage()
  872. "
  873. :disabled="isInputDisabled"
  874. resize="none"
  875. :class="{ input: !message && !inputing }"
  876. @compositionstart="inputing = true"
  877. @compositionend="inputing = false"
  878. >
  879. </el-input>
  880. <img
  881. :src="
  882. isInputDisabled
  883. ? 'https://d31zlh4on95l9h.cloudfront.net/images/aa192bcbc1682c97e1bc6fb422f2afff.png'
  884. : 'https://d31zlh4on95l9h.cloudfront.net/images/e6ec2ae238ced85b74e0912e988f243e.png'
  885. "
  886. @click="sendMessage"
  887. class="action-btn send-btn-inner"
  888. :style="{
  889. opacity: isInputDisabled ? 0.5 : 1,
  890. cursor: isInputDisabled ? 'not-allowed' : 'pointer',
  891. }"
  892. />
  893. </div>
  894. </div>
  895. </el-footer>
  896. </el-container>
  897. <el-container
  898. v-else
  899. class="main-container"
  900. :class="{
  901. collapsed: historyRecordRef?.isCollapsed && !isMobile,
  902. unCollapsed: !historyRecordRef?.isCollapsed && !isMobile,
  903. }"
  904. >
  905. <el-header class="homepage-head">
  906. <!-- logo -->
  907. <!-- <div class="homepage-logo">
  908. <img :src="logo" alt="图片加载失败" class="logo1" />
  909. <img :src="madeInHL" alt="图片加载失败" class="logo2" />
  910. </div> -->
  911. <div class="homepage-right-group">
  912. <div class="count-badge" @click="showCount">
  913. <img
  914. src="https://d31zlh4on95l9h.cloudfront.net/images/74e20c65c9ef2526477c63ad68698a50.png"
  915. class="action-btn"
  916. />
  917. <div class="count-number">{{ UserCount }}</div>
  918. <div class="clickGetCount">点击获取次数</div>
  919. </div>
  920. <div class="backToHomeBtn" @click="backToHome()">
  921. <img
  922. src="https://d31zlh4on95l9h.cloudfront.net/images/d8b388e461423f79087ddbe016002217.png"
  923. alt="返回首页"
  924. class="backImg"
  925. />
  926. <div class="backContent">返回首页</div>
  927. </div>
  928. <!-- <img
  929. :src="announcementBtn"
  930. class="announcement-btn action-btn"
  931. @click="showAnnouncement"
  932. />
  933. <img
  934. :src="feedbackBtn"
  935. class="announcement-btn action-btn"
  936. @click="showFeedback"
  937. /> -->
  938. </div>
  939. </el-header>
  940. <!-- 主体部分小人 问题轮询图 对话内容 -->
  941. <el-main class="homepage-body">
  942. <component :is="activeTwoTab" />
  943. </el-main>
  944. </el-container>
  945. <!-- 弹窗 -->
  946. <!-- 新增弹窗组件 -->
  947. <el-dialog v-model="dialogVisible" max-width="65%">
  948. <!-- 自定义标题插槽实现居中显示 -->
  949. <template #header>
  950. <div style="text-align: center">
  951. <span>活动规则</span>
  952. </div>
  953. </template>
  954. <!-- 中间内容部分 -->
  955. <div class="ruleContent">
  956. <p>试运行期间AI小财神可以检索全市场数据</p>
  957. <p>每个市场20支股票股票详情参见公告页面</p>
  958. <!-- <p>弘历会员每人每日拥有10次检索机会</p> -->
  959. </div>
  960. <!-- <template #footer> -->
  961. <!-- 添加一个div来包裹按钮并设置样式使其居中 -->
  962. <!-- <div style="text-align: center"> -->
  963. <!-- <el-button style="background-color: orange; color: white; border: none" @click="goToRecharge"> -->
  964. <!-- 去充值 -->
  965. <!-- </el-button> -->
  966. <!-- </div> -->
  967. <!-- </template> -->
  968. </el-dialog>
  969. </div>
  970. </template>
  971. <style scoped>
  972. /* 标签栏 */
  973. .tab-container {
  974. display: flex;
  975. margin-bottom: 10px;
  976. height: 100%;
  977. position: relative;
  978. justify-content: center;
  979. align-items: center;
  980. gap: 25vw;
  981. /* 新增右对齐 */
  982. }
  983. .pcTabContainer {
  984. }
  985. .tab-item {
  986. cursor: pointer;
  987. padding: 8px 12px;
  988. font-size: clamp(18px, 3vw, 20px);
  989. /* color: #999; */
  990. color: #fff;
  991. transition: all 0.3s;
  992. border-bottom: 2px solid transparent;
  993. font-weight: bold;
  994. }
  995. .tab-item.active {
  996. /* color: #000;
  997. border-color: #000; */
  998. background: linear-gradient(0deg, #ffffff, #fec13e);
  999. -webkit-background-clip: text;
  1000. background-clip: text;
  1001. -webkit-text-fill-color: transparent;
  1002. color: transparent;
  1003. border-color: #fec13e;
  1004. }
  1005. .tab-item:not(.active):hover {
  1006. color: #999999;
  1007. }
  1008. .tab-content {
  1009. overflow-y: auto;
  1010. overflow-x: hidden;
  1011. scroll-behavior: smooth;
  1012. height: 100%;
  1013. /* 添加平滑滚动效果 */
  1014. }
  1015. /* .pcTabContent {
  1016. margin: 0 6%;
  1017. } */
  1018. @media (max-width: 768px) {
  1019. .tab-container {
  1020. gap: 15px;
  1021. padding: 0 10px;
  1022. }
  1023. .tab-item {
  1024. font-size: clamp(14px, 3vw, 16px);
  1025. padding: 6px 10px;
  1026. }
  1027. }
  1028. </style>
  1029. <style scoped>
  1030. html {
  1031. height: 100dvh;
  1032. overflow: hidden !important;
  1033. position: fixed;
  1034. margin: 0;
  1035. padding: 0;
  1036. -webkit-overflow-scrolling: auto;
  1037. /* 禁用 iOS 弹性滚动 */
  1038. }
  1039. body {
  1040. height: 100dvh;
  1041. overflow: clip;
  1042. margin: 0;
  1043. padding: 0;
  1044. -webkit-overflow-scrolling: auto;
  1045. /* 禁用 iOS 弹性滚动 */
  1046. position: fixed;
  1047. }
  1048. #app {
  1049. overflow: hidden;
  1050. height: 100%;
  1051. margin: 0;
  1052. padding: 0;
  1053. }
  1054. .homepage {
  1055. /* height: var(--app-height, 100vh); */
  1056. height: var(--app-height, 100vh);
  1057. margin: 0 auto;
  1058. background-image: url("https://d31zlh4on95l9h.cloudfront.net/images/2dc3c13a74100b906e809d26b66db211.png");
  1059. background-size: 100% 100%;
  1060. background-repeat: no-repeat;
  1061. background-position: center;
  1062. display: flex;
  1063. flex-direction: row;
  1064. /* 改为水平布局 */
  1065. overflow: hidden;
  1066. position: fixed;
  1067. top: 0;
  1068. left: 0;
  1069. right: 0;
  1070. bottom: 0;
  1071. width: 100%;
  1072. /* -webkit-overflow-scrolling: touch; */
  1073. }
  1074. .main-container {
  1075. flex: 1;
  1076. transition: margin-left 0.3s ease;
  1077. display: flex;
  1078. flex-direction: column;
  1079. overflow: hidden;
  1080. }
  1081. .main-container.unCollapsed {
  1082. margin-left: 300px; /* 为历史记录组件留出空间 */
  1083. }
  1084. /* 当历史记录组件折叠时调整主容器边距 */
  1085. .main-container.collapsed {
  1086. margin-left: 3%;
  1087. }
  1088. .zhezhao {
  1089. width: 100%;
  1090. height: 100%;
  1091. background-color: rgba(0, 0, 0, 0.5);
  1092. z-index: 100;
  1093. position: fixed;
  1094. }
  1095. /* 移动端适配 */
  1096. @media (max-width: 768px) {
  1097. .homepage {
  1098. background-image: url("https://d31zlh4on95l9h.cloudfront.net/images/90d31d7052e729c63acb9e2cb94d1307.png");
  1099. }
  1100. .main-container {
  1101. /* margin-left: 280px; */
  1102. }
  1103. .main-container.unCollapsed {
  1104. margin-left: 280px;
  1105. }
  1106. .main-container.collapsed {
  1107. margin-left: 40px;
  1108. }
  1109. }
  1110. .homepage .el-container {
  1111. height: 100%;
  1112. flex-direction: column;
  1113. display: flex;
  1114. width: 100%;
  1115. overflow: hidden;
  1116. /* 防止容器滚动 */
  1117. }
  1118. .el-container .el-header {
  1119. flex-shrink: 0;
  1120. /* 防止头部压缩 */
  1121. height: auto;
  1122. min-height: 60px;
  1123. padding: 5px 0;
  1124. position: sticky;
  1125. top: 0;
  1126. z-index: 10;
  1127. /* background-color: rgba(255, 255, 255, 0.9); */
  1128. }
  1129. .el-container .el-main {
  1130. flex: 1;
  1131. /* 自动占据剩余空间 */
  1132. overflow: hidden;
  1133. /* 主容器不滚动 */
  1134. display: flex;
  1135. flex-direction: column;
  1136. min-height: 0;
  1137. /* 允许内容区域缩小 */
  1138. position: relative;
  1139. height: auto;
  1140. }
  1141. .el-container .el-footer {
  1142. flex-shrink: 0;
  1143. height: auto;
  1144. min-height: 70px;
  1145. position: sticky;
  1146. bottom: 0;
  1147. z-index: 20;
  1148. background-color: rgba(211, 24, 24, 0);
  1149. box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);
  1150. -webkit-transform: translateZ(0);
  1151. transform: translateZ(0);
  1152. padding-bottom: env(safe-area-inset-bottom, 0);
  1153. /* 适配iPhone X及以上的底部安全区域 */
  1154. }
  1155. .homepage-head {
  1156. padding: 0px;
  1157. display: flex;
  1158. position: relative;
  1159. justify-content: space-between;
  1160. width: 100%;
  1161. }
  1162. .homepage-right-group {
  1163. display: flex;
  1164. gap: 8px;
  1165. align-items: center;
  1166. margin-left: auto;
  1167. margin-right: 20px;
  1168. }
  1169. .homepage-right-group .action-btn {
  1170. height: 40px;
  1171. }
  1172. .count-badge {
  1173. position: relative;
  1174. cursor: pointer;
  1175. }
  1176. .count-badge:hover {
  1177. transform: scale(1.05);
  1178. }
  1179. .count-number {
  1180. position: absolute;
  1181. top: 16px;
  1182. right: 0px;
  1183. width: 68%;
  1184. text-align: center;
  1185. color: #6a00ff;
  1186. font-size: 14px;
  1187. font-weight: bold;
  1188. }
  1189. .clickGetCount {
  1190. width: 100%;
  1191. text-align: center;
  1192. color: white;
  1193. font-size: 12px;
  1194. }
  1195. .backToHomeBtn {
  1196. display: flex;
  1197. flex-direction: column;
  1198. justify-content: center;
  1199. align-items: center;
  1200. }
  1201. .backImg {
  1202. width: 100%;
  1203. height: auto;
  1204. }
  1205. .backContent {
  1206. width: 100%;
  1207. text-align: center;
  1208. color: white;
  1209. font-size: 12px;
  1210. white-space: nowrap;
  1211. }
  1212. .pc-count-badge {
  1213. width: 200px;
  1214. height: 100%;
  1215. position: absolute;
  1216. right: 20px;
  1217. display: flex;
  1218. }
  1219. .pc-countBtn {
  1220. width: 65%;
  1221. height: 100%;
  1222. position: relative;
  1223. }
  1224. .pc-countBtn:hover {
  1225. transform: scale(1.05);
  1226. }
  1227. .pc-action-btn {
  1228. width: 100%;
  1229. height: 70%;
  1230. background-image: url("https://d31zlh4on95l9h.cloudfront.net/images/74e20c65c9ef2526477c63ad68698a50.png");
  1231. background-repeat: no-repeat;
  1232. background-size: 100% 100%;
  1233. }
  1234. .pc-count-number {
  1235. position: absolute;
  1236. top: 15px;
  1237. right: 4px;
  1238. width: 68%;
  1239. text-align: center;
  1240. color: #6a00ff;
  1241. font-size: 15px;
  1242. font-weight: bold;
  1243. }
  1244. .pc-clickGetCount {
  1245. width: 100%;
  1246. text-align: center;
  1247. color: white;
  1248. font-size: 12px;
  1249. }
  1250. .pc-backToHomeBtn {
  1251. width: 35%;
  1252. height: 100%;
  1253. display: flex;
  1254. flex-direction: column;
  1255. align-items: center;
  1256. }
  1257. .pc-backImg {
  1258. width: auto;
  1259. height: 70%;
  1260. }
  1261. .pc-backContent {
  1262. width: 100%;
  1263. text-align: center;
  1264. color: white;
  1265. font-size: 12px;
  1266. }
  1267. .pc-backToHomeBtn:hover {
  1268. transform: scale(1.05);
  1269. }
  1270. .homepage-right-group .announcement-btn {
  1271. cursor: pointer;
  1272. transition: transform 0.3s;
  1273. }
  1274. .homepage-right-group .announcement-btn:hover {
  1275. transform: scale(1.3);
  1276. }
  1277. .homepage-body {
  1278. padding: 0px;
  1279. display: flex;
  1280. flex-direction: column;
  1281. flex: 1;
  1282. min-height: 0;
  1283. /* 允许内容区域缩小 */
  1284. overflow: hidden;
  1285. }
  1286. .main-wrapper {
  1287. height: 100%;
  1288. display: flex;
  1289. flex-direction: column;
  1290. flex: 1;
  1291. min-height: 0;
  1292. /* 允许内容区域缩小 */
  1293. }
  1294. .tab-section {
  1295. flex-shrink: 0;
  1296. /* 禁止伸缩 */
  1297. }
  1298. .tab-content {
  1299. flex: 1;
  1300. overflow-y: auto;
  1301. min-height: 0;
  1302. /* 关键:允许内容收缩 */
  1303. }
  1304. .homepage-logo {
  1305. height: 100%;
  1306. width: fit-content;
  1307. display: flex;
  1308. /* flex-direction: column; */
  1309. align-items: center;
  1310. justify-content: center;
  1311. margin-left: 20px;
  1312. margin-right: auto;
  1313. position: relative;
  1314. gap: 10px;
  1315. }
  1316. .expand {
  1317. font-size: 2.5rem;
  1318. cursor: pointer;
  1319. color: white;
  1320. }
  1321. .logo1 {
  1322. width: 110px;
  1323. height: auto;
  1324. margin-bottom: 8px;
  1325. }
  1326. .logo2 {
  1327. width: 80px;
  1328. height: auto;
  1329. }
  1330. /* 尾部 */
  1331. .homepage-footer {
  1332. display: flex;
  1333. flex-direction: column;
  1334. gap: 5px;
  1335. flex-shrink: 0;
  1336. /* width: 100%; */
  1337. background-color: #fff;
  1338. }
  1339. .pcFooter {
  1340. margin: 0 6% 4%;
  1341. }
  1342. .footer-first-line {
  1343. display: flex;
  1344. justify-content: space-between;
  1345. align-items: center;
  1346. padding: 5px 15px;
  1347. flex-shrink: 0;
  1348. }
  1349. .left-group {
  1350. display: flex;
  1351. gap: 15px;
  1352. }
  1353. .action-btn {
  1354. cursor: pointer;
  1355. transition: transform 0.2s;
  1356. height: 28px;
  1357. }
  1358. .model-btn {
  1359. height: 32px;
  1360. transition: all 0.3s ease;
  1361. }
  1362. .model-btn:hover {
  1363. transform: scale(1.1);
  1364. }
  1365. .send-btn {
  1366. margin-left: 10px;
  1367. height: 33px !important;
  1368. width: auto;
  1369. /* margin-right: 5px; */
  1370. }
  1371. .input-container {
  1372. position: relative;
  1373. width: 100%;
  1374. }
  1375. .send-btn-inner {
  1376. position: absolute;
  1377. right: 10px;
  1378. top: 50%;
  1379. transform: translateY(-50%);
  1380. height: 28px !important;
  1381. width: auto;
  1382. z-index: 10;
  1383. transition: all 0.3s ease;
  1384. }
  1385. .send-btn-inner:hover {
  1386. transform: translateY(-50%) scale(1.1);
  1387. }
  1388. /* 音频播放动画 */
  1389. @keyframes pulse {
  1390. 0% {
  1391. transform: scale(1);
  1392. }
  1393. 50% {
  1394. transform: scale(1.1);
  1395. }
  1396. 100% {
  1397. transform: scale(1);
  1398. }
  1399. }
  1400. .footer-second-line {
  1401. position: relative;
  1402. display: flex;
  1403. align-items: center;
  1404. padding: 5px 15px 10px;
  1405. flex-shrink: 0;
  1406. }
  1407. .msg-icon {
  1408. position: absolute;
  1409. left: 25px;
  1410. top: 50%;
  1411. transform: translateY(-50%);
  1412. width: 24px;
  1413. z-index: 999;
  1414. }
  1415. .msg-input:deep(.el-textarea__inner) {
  1416. border: none !important;
  1417. box-shadow: none !important;
  1418. overflow-y: auto !important;
  1419. transition: all 0.2s ease-out;
  1420. resize: none !important;
  1421. line-height: 1.5 !important;
  1422. max-height: 100px !important;
  1423. padding-right: 45px !important;
  1424. }
  1425. .msg-input {
  1426. min-height: 34px;
  1427. width: 100%;
  1428. border-radius: 5px;
  1429. font-size: 16px;
  1430. transition: all 0.3s ease-out;
  1431. overflow-y: hidden;
  1432. box-shadow: 0 4px 12px rgba(89, 24, 241, 0.3);
  1433. background: #fff;
  1434. z-index: 5;
  1435. /* 添加iOS设备特殊处理 */
  1436. -webkit-appearance: none;
  1437. appearance: none;
  1438. }
  1439. .msg-input:focus {
  1440. outline: none;
  1441. }
  1442. .input::before {
  1443. content: "请输入股票名称或股票代码...";
  1444. position: absolute;
  1445. left: 11px;
  1446. top: 5px;
  1447. color: var(--el-text-color-secondary);
  1448. pointer-events: none;
  1449. white-space: nowrap;
  1450. overflow-x: hidden;
  1451. text-overflow: ellipsis;
  1452. width: 80%;
  1453. z-index: 6;
  1454. }
  1455. @media (max-width: 768px) {
  1456. .action-btn {
  1457. height: 21px;
  1458. }
  1459. .footer-second-line {
  1460. padding: 5px 10px 10px;
  1461. }
  1462. .msg-input {
  1463. /* min-height: 44px; */
  1464. /* height: 44px; */
  1465. font-size: 16px;
  1466. }
  1467. }
  1468. .ruleContent {
  1469. text-align: center;
  1470. }
  1471. </style>