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.

1071 lines
28 KiB

4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
3 months ago
3 months ago
3 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
  1. <script setup>
  2. // 导入
  3. import { ref, computed, onMounted, watch, nextTick, onUnmounted } 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 { useAppBridge } from "../assets/js/useAppBridge.js";
  11. import { useDataStore } from "@/store/dataList.js";
  12. import { useChatStore } from "../store/chat";
  13. import { useAudioStore } from "../store/audio";
  14. import _ from "lodash";
  15. import logo from "../assets/img/homePage/logo.png";
  16. import madeInHL from "../assets/img/homePage/madeInHL.png";
  17. import getCountAll from "../assets/img/homePage/get-count-all.png";
  18. import announcementBtn from "../assets/img/homePage/announcement.png";
  19. import thinkActive from "../assets/img/homePage/tail/think-active.png";
  20. import thinkNoActive from "../assets/img/homePage/tail/think-no-active.png";
  21. import languageBtn from "../assets/img/homePage/tail/language.png";
  22. import dbqbButton01 from "../assets/img/AiEmotion/dbqb-button01.png";
  23. import dbqbButton02 from "../assets/img/AiEmotion/dbqb-button02.png";
  24. import emotionButton01 from "../assets/img/AiEmotion/emotion-button01.png";
  25. import emotionButton02 from "../assets/img/AiEmotion/emotion-button02.png";
  26. import voice from "../assets/img/homePage/tail/voice.png";
  27. import voiceNoActive from "../assets/img/homePage/tail/voice-no-active.png";
  28. import sendBtn from "../assets/img/homePage/tail/send.png";
  29. import msgBtn from "../assets/img/homePage/tail/msg.png";
  30. import feedbackBtn from "../assets/img/Feedback/feedbackBtn.png";
  31. import AiEmotion from "./AiEmotion.vue";
  32. // import VConsole from 'vconsole';
  33. // const vConsole = new VConsole();
  34. // 获取 AiEmotion 组件的 ref
  35. const aiEmotionRef = ref(null);
  36. // import { useUserStore } from "../store/userPessionCode.js";
  37. const { getQueryVariable, setActiveTabIndex } = useDataStore();
  38. const dataStore = useDataStore();
  39. const chatStore = useChatStore();
  40. // 变量
  41. // 音频管理
  42. const audioStore = useAudioStore();
  43. const isVoice = computed(() => audioStore.isVoiceEnabled);
  44. const toggleVoice = () => {
  45. audioStore.toggleVoice();
  46. };
  47. // 将默认值改为从 sessionStorage 中获取,如果没有则使用默认值 'aifindCow'为第一个默认tab
  48. const activeTab = ref(sessionStorage.getItem("activeTabAI") || "AIchat");
  49. const activeIndex = ref(
  50. parseInt(sessionStorage.getItem("activeIndexAI") || "0")
  51. );
  52. const tabs = computed(() => [
  53. {
  54. name: "AIchat",
  55. label: "夺宝奇兵大模型",
  56. },
  57. // {
  58. // name: "AIfind",
  59. // label: "发现",
  60. // },
  61. {
  62. name: "AiEmotion",
  63. label: "AI情绪大模型",
  64. },
  65. ]);
  66. // 修改 setActiveTab 方法,添加一个可选参数 forceAIchat
  67. const setActiveTab = (tab, index, forceAIchat = false) => {
  68. isScrolling.value = false; //回复滚动到底部方法
  69. isAnnouncementVisible.value = false;
  70. if (forceAIchat && activeTab.value !== "AIchat") {
  71. activeTab.value = "AIchat";
  72. activeIndex.value = 0;
  73. sessionStorage.setItem("activeTabAI", "AIchat");
  74. sessionStorage.setItem("activeIndexAI", "0");
  75. } else {
  76. activeTab.value = tab;
  77. activeIndex.value = index;
  78. sessionStorage.setItem("activeTabAI", tab);
  79. sessionStorage.setItem("activeIndexAI", index.toString());
  80. }
  81. setActiveTabIndex(index);
  82. console.log(tab, index, "tab, index");
  83. setHeight(document.getElementById("testId")); // 给父组件发送窗口高度
  84. };
  85. // 修改 activeComponent 的计算逻辑
  86. const activeComponent = computed(() => {
  87. if (isAnnouncementVisible.value) {
  88. return Announcement;
  89. }
  90. if (activeTab.value === "AIchat") {
  91. return AIchat;
  92. } else if (activeTab.value === "AIfind") {
  93. return AIfind;
  94. } else if (activeTab.value === "AiEmotion") {
  95. return AiEmotion; // 新增逻辑
  96. }
  97. });
  98. // 新增一个方法,调用时先判断是否处于 AIchat,若不在则跳转到 AIchat
  99. const ensureAIchat = () => {
  100. setActiveTab("AIchat", 0, true);
  101. };
  102. // 获取次数
  103. const UserCount = computed(() => chatStore.UserCount);
  104. const getCount = () => {
  105. console.log("点击了获取次数的按钮");
  106. };
  107. // 深度思考
  108. const isThinking = ref(true);
  109. const toggleThink = () => {
  110. isThinking.value = !isThinking.value;
  111. };
  112. // 发送消息
  113. const message = ref("");
  114. // 传输对象
  115. const messages = ref([]);
  116. // 信息加载状态
  117. const isLoading = computed(() => {
  118. chatStore.isLoading;
  119. });
  120. // 添加用户消息
  121. const updateMessage = (title) => {
  122. message.value = title;
  123. // console.log("updateMessage 的值:", title);
  124. };
  125. const sendMessage = async () => {
  126. if (
  127. localStorage.getItem("localToken") == null ||
  128. localStorage.getItem("localToken") == ""
  129. ) {
  130. ElMessage.error("请先登录");
  131. return;
  132. }
  133. isScrolling.value = false;
  134. // 判断当前是否为 AiEmotion 组件
  135. if (activeTab.value === "AiEmotion") {
  136. // 调用 AiEmotion 组件的 handleSendMessage 方法
  137. aiEmotionRef.value?.handleSendMessage(message.value);
  138. message.value = ""; // 清空输入框
  139. return;
  140. }
  141. // 调用 ensureAIchat 确保跳转到 AIchat 页面
  142. ensureAIchat();
  143. console.log(chatStore.isLoading, "isLoading.value1111");
  144. if (!message.value) return;
  145. if (chatStore.isLoading) return;
  146. chatStore.setLoading(true);
  147. console.log(chatStore.isLoading, "isLoading.value2222");
  148. const messageContent = message.value;
  149. // 重置消息输入框
  150. message.value = "";
  151. setTimeout(() => {
  152. console.log("延时后添加消息", messageContent);
  153. // 发送消息时,设置 isLoading 为 true
  154. messages.value = [
  155. ...messages.value,
  156. {
  157. sender: "user",
  158. content: messageContent,
  159. timestamp: new Date().toISOString(),
  160. },
  161. ];
  162. console.log(messages.value, "messages.value");
  163. }, 200);
  164. };
  165. // 公告
  166. // 引入公告组件
  167. import Announcement from "./Announcement.vue";
  168. // 新增一个变量来控制是否显示公告页面
  169. const isAnnouncementVisible = ref(false);
  170. const showAnnouncement = async () => {
  171. console.log("打开公告");
  172. dataStore.isFeedback = false; // 显示用户反馈页面
  173. isScrolling.value = false; //回复滚动到底部方法
  174. setActiveTab("", -1); // 清空当前选中状态
  175. isAnnouncementVisible.value = true; // 显示公告页面
  176. };
  177. // 跳转用户反馈
  178. const showFeedback = () => {
  179. console.log("打开用户反馈");
  180. dataStore.isFeedback = true; // 显示用户反馈页面
  181. };
  182. // 点击剩余次数会弹出的弹窗
  183. // 新增一个 ref 来控制弹窗的显示与隐藏
  184. const dialogVisible = ref(false);
  185. // 获取次数
  186. const showCount = () => {
  187. console.log("显示剩余次数");
  188. // 显示弹窗
  189. dialogVisible.value = true;
  190. console.log("dialogVisible 的值:", dialogVisible.value); // 添加日志确认
  191. };
  192. // 保证发送消息时,滚动屏在底部
  193. const tabContent = ref(null);
  194. const isScrolling = ref(false); //判断用户是否在滚动
  195. const smoothScrollToBottom = async () => {
  196. console.log("调用滚动到底部的方法");
  197. // await nextTick();
  198. const container = tabContent.value;
  199. // console.log(container, 'container')
  200. // console.log(isScrolling.value, 'isScrolling.value')
  201. if (!container) return;
  202. await nextTick(); // 确保在DOM更新后执行
  203. if (!isScrolling.value) {
  204. container.scrollTop = container.scrollHeight - container.offsetHeight;
  205. // container.scrollTop = container.scrollHeight;
  206. // container.scrollTop = container.offsetHeight;
  207. // container.scrollTop = container.scrollHeight + container.offsetHeight;
  208. // console.log(container.scrollHeight, container.offsetHeight, container.scrollHeight - container.offsetHeight, container.scrollTop, "总长度", "可视长度", "位置")
  209. }
  210. };
  211. const throttledSmoothScrollToBottom = _.throttle(smoothScrollToBottom, 300, {
  212. trailing: false,
  213. });
  214. watch(
  215. () => chatStore.messages,
  216. () => {
  217. // console.log('messages变化了')
  218. throttledSmoothScrollToBottom();
  219. // setTimeout(throttledSmoothScrollToBottom, 100);
  220. },
  221. { deep: true, immediate: true }
  222. );
  223. watch(
  224. activeTab,
  225. async () => {
  226. console.log("activeTab变化了", activeTab.value);
  227. if (activeTab.value === "AIchat") {
  228. isScrolling.value = false; //回复滚动到底部方法
  229. setTimeout(() => {
  230. throttledSmoothScrollToBottom();
  231. }, 100);
  232. }
  233. // setTimeout(throttledSmoothScrollToBottom, 100);
  234. },
  235. { deep: true, immediate: true }
  236. );
  237. // 获取token的核心函数
  238. const fnGetToken = () => {
  239. // console.log('进入fnGetToken')
  240. window.JWready = (ress) => {
  241. // console.log('进入JWready')
  242. try {
  243. ress = JSON.parse(ress);
  244. // console.log(ress, 'ress')
  245. } catch (error) {
  246. console.log(error, "fnGetToken error");
  247. } //platform为5是app端
  248. // platform.value = ress.data.platform
  249. // 处理平台判断
  250. console.log(ress.data.platform, "ress.data.platform");
  251. if (!ress.data.platform) {
  252. // 非App环境通过URL参数获取
  253. localStorage.setItem(
  254. "localToken",
  255. decodeURIComponent(String(getQueryVariable("token")))
  256. );
  257. // localStorage.setItem('localToken', "+SsksARQgUHIbIG3rRnnbZi0+fEeMx8pywnIlrmTxo5EOPR/wjWDV7w7+ZUseiBtf9kFa/atmNx6QfSpv5w")
  258. } else {
  259. // App环境通过桥接获取
  260. useAppBridge().packageFun(
  261. "JWgetStorage",
  262. (response) => {
  263. const res = JSON.parse(response); // 解析返回的结果
  264. localStorage.setItem("localToken", res.data);
  265. // localStorage.setItem('localToken', "+SsksARQgUHIbIG3rRnnbZi0+fEeMx8pywnIlrmTxo5EOPR/wjWDV7w7+ZUseiBtf9kFa/atmNx6QfSpv5w")
  266. },
  267. 5,
  268. {
  269. key: "token",
  270. }
  271. );
  272. }
  273. };
  274. // console.log('出来了')
  275. // 触发App桥接
  276. useAppBridge().packageFun("JWwebReady", () => {}, 5, {});
  277. };
  278. // 在setTimeout中延迟执行
  279. setTimeout(() => {
  280. fnGetToken();
  281. }, 800);
  282. const heightListener = () => {
  283. const tabContainer = tabContent.value;
  284. let befortop = 0;
  285. const scrollHandler = () => {
  286. const aftertop = tabContainer.scrollTop;
  287. // 新增底部判断逻辑
  288. const isBottom =
  289. aftertop + tabContainer.offsetHeight + 70 >= tabContainer.scrollHeight;
  290. if (activeTab.value === "AIchat") {
  291. if (aftertop - befortop > 0) {
  292. // console.log('向下滚动');
  293. isScrolling.value = true;
  294. } else {
  295. // console.log('向上滚动');
  296. isScrolling.value = true;
  297. }
  298. // 添加底部状态检测
  299. if (isBottom) {
  300. // console.log('滚动到底部');
  301. isScrolling.value = false;
  302. }
  303. }
  304. befortop = aftertop;
  305. };
  306. // console.log(isScrolling.value, 'isScrolling.value')
  307. tabContainer.addEventListener("scroll", scrollHandler);
  308. };
  309. const throttledHeightListener = _.throttle(heightListener, 500, {
  310. trailing: false,
  311. });
  312. const goToRecharge = () => {
  313. console.log("点击充值");
  314. // http://39.101.133.168:8919/payment/recharge/index?
  315. // url=http%3A%2F%2Flocalhost%3A8080%2FLiveActivity%2Fpck
  316. // &platform=1
  317. // &token=+S4h5QEE1hTIb4CxphrnbZi0+fEeMx8pywnIlrmTmo4QO6IolWnVWu5r+J4rKXMwK41UPfKqyIp+RvWmtM8
  318. const userAgent = navigator.userAgent.toLowerCase();
  319. const mobileKeywords = ["mobile", "android", "iphone", "ipad", "ipod"];
  320. const isMobile = mobileKeywords.some((keyword) =>
  321. userAgent.includes(keyword)
  322. );
  323. console.log(isMobile ? "手机" : "电脑");
  324. const url = encodeURI("http://39.101.133.168:8857/aixiaocaishen/homePage");
  325. console.log(url, "url");
  326. const platform = isMobile ? 2 : 1;
  327. const token = encodeURIComponent(localStorage.getItem("localToken"));
  328. console.log(token, "token");
  329. const rechargeUrl =
  330. "http://39.101.133.168:8919/payment/recharge/index?" +
  331. "url=" +
  332. url +
  333. "&platform=" +
  334. platform +
  335. "&token=" +
  336. token;
  337. console.log(rechargeUrl, "rechargeUrl");
  338. window.location.href = rechargeUrl;
  339. // window.open(rechargeUrl)
  340. };
  341. const adjustFooterPosition = (height) => {
  342. console.log("调整底部位置", height);
  343. const footer = document.querySelector(".el-footer");
  344. const main = document.querySelector(".el-main");
  345. const homePage = document.querySelector(".homepage");
  346. const app = document.getElementById("app");
  347. // Footer 的默认高度(假设为 60px) // 动态推高 Footer
  348. // footer.style.bottom = `${keyboardHeight}px`;
  349. // 给 Main 区域留出 Footer + 键盘的空间
  350. homePage.style.height = `${height}px`;
  351. // app.style.height = `${height}px`;
  352. void homePage.offsetHeight;
  353. const html = document.querySelector("html");
  354. const body = document.querySelector("body");
  355. html.style.height = `${height}px`;
  356. body.style.height = `${height}px`;
  357. html.scrollTop = 0;
  358. setTimeout(() => {
  359. // 隐藏滚动条
  360. html.style.overflow = "hidden";
  361. body.style.overflow = "hidden";
  362. }, 200);
  363. // console.log(html.offsetHeight, 'html')
  364. // console.log(html.clientHeight, 'html')
  365. // console.log(html.scrollHeight, 'htmlScrollHeight')
  366. // console.log(body.clientHeight, 'body')
  367. // console.log(body.scrollHeight, 'bodyScrollHeight')
  368. // console.log(homePage.offsetHeight, 'homePage')
  369. // console.log(homePage.clientHeight, 'homePageClientHeight')
  370. // console.log(homePage.scrollHeight, 'homePageScrollHeight')
  371. // console.log(window.innerHeight, 'window.innerHeight')
  372. // console.log(window.visualViewport.height, 'window.visualViewport.height')
  373. // console.log(main.offsetHeight, 'main')
  374. // console.log(main.clientHeight, 'mainClientHeight')
  375. // console.log(main.scrollHeight, 'mainScrollHeight')
  376. };
  377. const onFocus = function () {
  378. const visualViewport = window.visualViewport;
  379. // 获取可视区域高度
  380. setTimeout(() => {
  381. console.log("输入框聚焦");
  382. console.log(visualViewport.height, "visualViewport.height");
  383. const keyboardHeight = window.innerHeight - visualViewport.height;
  384. console.log(window.innerHeight, "window.innerHeight");
  385. console.log(keyboardHeight, "keyboardHeight");
  386. adjustFooterPosition(visualViewport.height);
  387. }, 200);
  388. };
  389. const onBlur = function () {
  390. const visualViewport = window.visualViewport;
  391. setTimeout(() => {
  392. console.log("输入框失焦");
  393. const keyboardHeight = window.innerHeight - visualViewport.height;
  394. console.log(window.innerHeight, "window.innerHeight");
  395. console.log(visualViewport.height, "visualViewport.height");
  396. console.log(keyboardHeight, "keyboardHeight");
  397. adjustFooterPosition(visualViewport.height);
  398. }, 200);
  399. };
  400. window.addEventListener("resize", () => {
  401. // 检测是否为iOS设备
  402. const isIOS = /iPhone|iPad|iPod|ios/i.test(navigator.userAgent);
  403. console.log("是否为iOS设备:", isIOS);
  404. if (!isIOS) {
  405. console.log("窗口大小变化");
  406. const homePage = document.querySelector(".homepage");
  407. homePage.style.height = `${window.innerHeight}px`;
  408. }
  409. });
  410. // 禁用全局触摸滚动
  411. document.addEventListener(
  412. "touchmove",
  413. (e) => {
  414. if (!dataStore.isFeedback) {
  415. // 判断触摸目标是否在可滚动区域内
  416. const isScrollableArea = e.target.closest(".tab-content");
  417. // 如果不在可滚动区域,则阻止滚动
  418. if (!isScrollableArea) {
  419. e.preventDefault();
  420. }
  421. }
  422. },
  423. { passive: false }
  424. );
  425. onMounted(async () => {
  426. const isPhone =
  427. /phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone/i.test(
  428. navigator.userAgent
  429. );
  430. !isPhone &&
  431. localStorage.setItem(
  432. "localToken",
  433. decodeURIComponent(String(getQueryVariable("token")))
  434. );
  435. setHeight(document.getElementById("testId")); // 给父组件发送窗口高度
  436. // 获取次数
  437. await chatStore.getUserCount();
  438. // 滚动到底部
  439. throttledSmoothScrollToBottom();
  440. // 监听页面高度
  441. throttledHeightListener();
  442. // 添加输入框焦点处理
  443. // handleInputFocus();
  444. // 初始化视口高度变量
  445. // updateAppHeight();
  446. });
  447. </script>
  448. <template>
  449. <div class="homepage" id="testId">
  450. <el-container v-if="!dataStore.isFeedback">
  451. <!-- AI小财神头部 logo 次数 公告 -->
  452. <el-header class="homepage-head">
  453. <!-- logo -->
  454. <div class="homepage-logo">
  455. <img :src="logo" alt="图片加载失败" class="logo1" />
  456. <img :src="madeInHL" alt="图片加载失败" class="logo2" />
  457. </div>
  458. <div class="homepage-right-group">
  459. <div class="count-badge" @click="showCount">
  460. <img :src="getCountAll" class="action-btn" />
  461. <div class="count-number">{{ UserCount }}</div>
  462. </div>
  463. <img
  464. :src="announcementBtn"
  465. class="announcement-btn action-btn"
  466. @click="showAnnouncement"
  467. />
  468. <img
  469. :src="feedbackBtn"
  470. class="announcement-btn action-btn"
  471. @click="showFeedback"
  472. />
  473. </div>
  474. </el-header>
  475. <!-- 主体部分小人 问题轮询图 对话内容 -->
  476. <el-main class="homepage-body">
  477. <div class="main-wrapper">
  478. <section class="tab-section">
  479. <div class="tab-container">
  480. <div
  481. v-for="(tab, index) in tabs"
  482. :key="tab.name"
  483. @click="setActiveTab(tab.name, index)"
  484. :class="[
  485. 'tab-item',
  486. { active: activeIndex === index && !isAnnouncementVisible },
  487. ]"
  488. >
  489. <span>{{ tab.label }}</span>
  490. </div>
  491. </div>
  492. </section>
  493. <div class="tab-content" ref="tabContent">
  494. <component
  495. :is="activeComponent"
  496. :messages="messages"
  497. @updateMessage="updateMessage"
  498. @sendMessage="sendMessage"
  499. @ensureAIchat="ensureAIchat"
  500. ref="aiEmotionRef"
  501. />
  502. </div>
  503. </div>
  504. </el-main>
  505. <!-- 尾部 问题输入框 深度思考 多语言 语音播报 -->
  506. <el-footer class="homepage-footer" id="input">
  507. <!-- 第一行按钮 -->
  508. <div class="footer-first-line">
  509. <div class="left-group">
  510. <!-- <img v-if="isThinking" :src="thinkActive" @click="toggleThink" class="action-btn" />
  511. <img v-else :src="thinkNoActive" @click="toggleThink" class="action-btn" />
  512. <img :src="languageBtn" @click="changeLanguage" class="action-btn" /> -->
  513. <!-- 夺宝奇兵大模型按钮 -->
  514. <img
  515. :src="activeTab === 'AIchat' ? dbqbButton01 : dbqbButton02"
  516. @click="setActiveTab('AIchat', 0)"
  517. class="action-btn model-btn"
  518. alt="夺宝奇兵大模型"
  519. />
  520. <!-- AI情绪大模型按钮 -->
  521. <img
  522. :src="
  523. activeTab === 'AiEmotion' ? emotionButton01 : emotionButton02
  524. "
  525. @click="setActiveTab('AiEmotion', 1)"
  526. class="action-btn model-btn"
  527. alt="AI情绪大模型"
  528. />
  529. <img
  530. v-if="isVoice"
  531. :src="voice"
  532. @click="toggleVoice"
  533. class="action-btn"
  534. />
  535. <img
  536. v-else
  537. :src="voiceNoActive"
  538. @click="toggleVoice"
  539. class="action-btn"
  540. />
  541. </div>
  542. <img
  543. v-if="!chatStore.isLoading"
  544. :src="sendBtn"
  545. @click="sendMessage"
  546. class="action-btn send-btn"
  547. />
  548. <!-- <div v-else @click="chatStore.setLoading(false)"> -->
  549. <div v-else>
  550. <el-icon class="is-loading">
  551. <Loading />
  552. </el-icon>
  553. </div>
  554. </div>
  555. <!-- 第二行输入框 -->
  556. <div class="footer-second-line">
  557. <img :src="msgBtn" class="msg-icon" />
  558. <el-input
  559. type="textarea"
  560. v-model="message"
  561. @focus="onFocus"
  562. @blur="onBlur"
  563. :autosize="{ minRows: 1, maxRows: 4 }"
  564. placeholder="给AI小财神发消息..."
  565. class="msg-input"
  566. @keydown.enter.exact.prevent="isLoading ? null : sendMessage()"
  567. resize="none"
  568. >
  569. </el-input>
  570. </div>
  571. </el-footer>
  572. </el-container>
  573. <el-container v-else>
  574. <el-header class="homepage-head">
  575. <!-- logo -->
  576. <div class="homepage-logo">
  577. <img :src="logo" alt="图片加载失败" class="logo1" />
  578. <img :src="madeInHL" alt="图片加载失败" class="logo2" />
  579. </div>
  580. <div class="homepage-right-group">
  581. <div class="count-badge" @click="showCount">
  582. <img :src="getCountAll" class="action-btn" />
  583. <div class="count-number">{{ UserCount }}</div>
  584. </div>
  585. <img
  586. :src="announcementBtn"
  587. class="announcement-btn action-btn"
  588. @click="showAnnouncement"
  589. />
  590. <img
  591. :src="feedbackBtn"
  592. class="announcement-btn action-btn"
  593. @click="showFeedback"
  594. />
  595. </div>
  596. </el-header>
  597. <!-- 主体部分小人 问题轮询图 对话内容 -->
  598. <el-main class="homepage-body">
  599. <feedback :is="Feedback" />
  600. </el-main>
  601. </el-container>
  602. <!-- 弹窗 -->
  603. <!-- 新增弹窗组件 -->
  604. <el-dialog v-model="dialogVisible" max-width="65%">
  605. <!-- 自定义标题插槽实现居中显示 -->
  606. <template #header>
  607. <div style="text-align: center">
  608. <span>活动规则</span>
  609. </div>
  610. </template>
  611. <!-- 中间内容部分 -->
  612. <div class="ruleContent">
  613. <p>试运行期间AI小财神可以检索全市场数据</p>
  614. <p>每个市场20支股票股票详情参见公告页面</p>
  615. <p>弘历会员每人每日拥有10次检索机会</p>
  616. </div>
  617. <!-- <template #footer> -->
  618. <!-- 添加一个div来包裹按钮并设置样式使其居中 -->
  619. <!-- <div style="text-align: center"> -->
  620. <!-- <el-button style="background-color: orange; color: white; border: none" @click="goToRecharge"> -->
  621. <!-- 去充值 -->
  622. <!-- </el-button> -->
  623. <!-- </div> -->
  624. <!-- </template> -->
  625. </el-dialog>
  626. </div>
  627. </template>
  628. <style scoped>
  629. /* 标签栏 */
  630. .tab-container {
  631. display: flex;
  632. gap: 30px;
  633. margin-right: 40px;
  634. margin-left: 40px;
  635. margin-bottom: 10px;
  636. padding: 0 20px;
  637. justify-content: space-between;
  638. height: 100%;
  639. /* 新增右对齐 */
  640. }
  641. .tab-item {
  642. cursor: pointer;
  643. padding: 8px 12px;
  644. font-size: clamp(18px, 3vw, 20px);
  645. /* color: #999; */
  646. color: #fff;
  647. transition: all 0.3s;
  648. border-bottom: 2px solid transparent;
  649. font-weight: bold;
  650. }
  651. .tab-item.active {
  652. /* color: #000;
  653. border-color: #000; */
  654. background: linear-gradient(0deg, #ffffff, #fec13e);
  655. -webkit-background-clip: text;
  656. background-clip: text;
  657. -webkit-text-fill-color: transparent;
  658. color: transparent;
  659. border-color: #fec13e;
  660. }
  661. .tab-item:not(.active):hover {
  662. color: #999999;
  663. }
  664. .tab-content {
  665. overflow-y: auto;
  666. overflow-x: hidden;
  667. scroll-behavior: smooth;
  668. height: 100%;
  669. /* 添加平滑滚动效果 */
  670. }
  671. @media (max-width: 768px) {
  672. .tab-container {
  673. gap: 15px;
  674. padding: 0 10px;
  675. }
  676. .tab-item {
  677. font-size: clamp(14px, 3vw, 16px);
  678. padding: 6px 10px;
  679. }
  680. }
  681. </style>
  682. <style scoped>
  683. html {
  684. height: 100dvh;
  685. overflow: hidden !important;
  686. position: fixed;
  687. margin: 0;
  688. padding: 0;
  689. -webkit-overflow-scrolling: auto;
  690. /* 禁用 iOS 弹性滚动 */
  691. }
  692. body {
  693. height: 100dvh;
  694. overflow: clip;
  695. margin: 0;
  696. padding: 0;
  697. -webkit-overflow-scrolling: auto;
  698. /* 禁用 iOS 弹性滚动 */
  699. position: fixed;
  700. }
  701. #app {
  702. overflow: hidden;
  703. height: 100%;
  704. margin: 0;
  705. padding: 0;
  706. }
  707. .homepage {
  708. /* height: var(--app-height, 100vh); */
  709. height: var(--app-height, 100vh);
  710. margin: 0 auto;
  711. background-image: url(/src/assets/img/homePage/bk01.jpg);
  712. background-size: 100% 100%;
  713. background-repeat: no-repeat;
  714. background-position: center;
  715. display: flex;
  716. overflow: hidden;
  717. position: fixed;
  718. top: 0;
  719. left: 0;
  720. right: 0;
  721. bottom: 0;
  722. width: 100%;
  723. /* -webkit-overflow-scrolling: touch; */
  724. }
  725. .homepage .el-container {
  726. height: 100%;
  727. flex-direction: column;
  728. display: flex;
  729. width: 100%;
  730. overflow: hidden;
  731. /* 防止容器滚动 */
  732. }
  733. .el-container .el-header {
  734. flex-shrink: 0;
  735. /* 防止头部压缩 */
  736. height: auto;
  737. min-height: 60px;
  738. padding: 5px 0;
  739. position: sticky;
  740. top: 0;
  741. z-index: 10;
  742. /* background-color: rgba(255, 255, 255, 0.9); */
  743. }
  744. .el-container .el-main {
  745. flex: 1;
  746. /* 自动占据剩余空间 */
  747. overflow: hidden;
  748. /* 主容器不滚动 */
  749. display: flex;
  750. flex-direction: column;
  751. min-height: 0;
  752. /* 允许内容区域缩小 */
  753. position: relative;
  754. height: auto;
  755. }
  756. .el-container .el-footer {
  757. flex-shrink: 0;
  758. height: auto;
  759. min-height: 70px;
  760. position: sticky;
  761. bottom: 0;
  762. z-index: 20;
  763. background-color: rgba(211, 24, 24, 0);
  764. box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);
  765. -webkit-transform: translateZ(0);
  766. transform: translateZ(0);
  767. padding-bottom: env(safe-area-inset-bottom, 0);
  768. /* 适配iPhone X及以上的底部安全区域 */
  769. }
  770. .homepage-head {
  771. padding: 0px;
  772. display: flex;
  773. position: relative;
  774. justify-content: space-between;
  775. width: 100%;
  776. }
  777. .homepage-right-group {
  778. display: flex;
  779. gap: 8px;
  780. align-items: center;
  781. margin-left: auto;
  782. margin-right: 20px;
  783. }
  784. .homepage-right-group .action-btn {
  785. height: 40px;
  786. }
  787. .homepage-right-group .count-badge {
  788. position: relative;
  789. cursor: pointer;
  790. }
  791. .homepage-right-group .count-badge .count-number {
  792. position: absolute;
  793. top: 6px;
  794. right: 20px;
  795. color: #573dfc;
  796. font-size: 12px;
  797. font-weight: bold;
  798. }
  799. .homepage-right-group .announcement-btn {
  800. cursor: pointer;
  801. transition: transform 0.3s;
  802. }
  803. .homepage-right-group .announcement-btn:hover {
  804. transform: scale(1.3);
  805. }
  806. .homepage-body {
  807. padding: 0px;
  808. display: flex;
  809. flex-direction: column;
  810. flex: 1;
  811. min-height: 0;
  812. /* 允许内容区域缩小 */
  813. overflow: hidden;
  814. }
  815. .main-wrapper {
  816. height: 100%;
  817. display: flex;
  818. flex-direction: column;
  819. flex: 1;
  820. min-height: 0;
  821. /* 允许内容区域缩小 */
  822. }
  823. .tab-section {
  824. flex-shrink: 0;
  825. /* 禁止伸缩 */
  826. }
  827. .tab-content {
  828. flex: 1;
  829. overflow-y: auto;
  830. min-height: 0;
  831. /* 关键:允许内容收缩 */
  832. }
  833. .homepage-logo {
  834. height: 100%;
  835. width: fit-content;
  836. display: flex;
  837. flex-direction: column;
  838. align-items: center;
  839. justify-content: center;
  840. margin-left: 20px;
  841. margin-right: auto;
  842. position: relative;
  843. }
  844. @media (max-width: 768px) {
  845. .homepage-logo {
  846. margin-left: 10px;
  847. left: 0;
  848. }
  849. }
  850. .logo1 {
  851. width: 120px;
  852. height: auto;
  853. margin-bottom: 8px;
  854. }
  855. .logo2 {
  856. width: 80px;
  857. height: auto;
  858. }
  859. /* 尾部 */
  860. .homepage-footer {
  861. display: flex;
  862. flex-direction: column;
  863. gap: 5px;
  864. flex-shrink: 0;
  865. width: 100%;
  866. background-color: #fff;
  867. }
  868. .footer-first-line {
  869. display: flex;
  870. justify-content: space-between;
  871. align-items: center;
  872. padding: 5px 15px;
  873. flex-shrink: 0;
  874. }
  875. .left-group {
  876. display: flex;
  877. gap: 15px;
  878. }
  879. .action-btn {
  880. cursor: pointer;
  881. transition: transform 0.2s;
  882. height: 28px;
  883. }
  884. .action-btn:hover {
  885. transform: scale(1.05);
  886. }
  887. .model-btn {
  888. height: 32px;
  889. transition: all 0.3s ease;
  890. }
  891. .model-btn:hover {
  892. transform: scale(1.1);
  893. }
  894. .send-btn {
  895. margin-left: auto;
  896. margin-right: 5px;
  897. }
  898. .footer-second-line {
  899. position: relative;
  900. display: flex;
  901. align-items: center;
  902. padding: 5px 15px 10px;
  903. flex-shrink: 0;
  904. }
  905. .msg-icon {
  906. position: absolute;
  907. left: 25px;
  908. top: 50%;
  909. transform: translateY(-50%);
  910. width: 24px;
  911. z-index: 999;
  912. }
  913. .msg-input:deep(.el-textarea__inner) {
  914. border: none !important;
  915. box-shadow: none !important;
  916. overflow-y: auto !important;
  917. transition: all 0.2s ease-out;
  918. padding: 8px 20px 8px 45px !important;
  919. resize: none !important;
  920. line-height: 1.5 !important;
  921. max-height: 100px !important;
  922. }
  923. .msg-input {
  924. min-height: 34px;
  925. width: 100%;
  926. border-radius: 20px;
  927. font-size: 16px;
  928. transition: all 0.3s ease-out;
  929. overflow-y: hidden;
  930. box-shadow: 0 4px 12px rgba(89, 24, 241, 0.3);
  931. background: #fff;
  932. z-index: 5;
  933. /* 添加iOS设备特殊处理 */
  934. -webkit-appearance: none;
  935. appearance: none;
  936. }
  937. .msg-input:focus {
  938. outline: none;
  939. }
  940. @media (max-width: 768px) {
  941. .action-btn {
  942. height: 21px;
  943. }
  944. .footer-second-line {
  945. padding: 5px 10px 10px;
  946. }
  947. .msg-input {
  948. font-size: 16px;
  949. }
  950. }
  951. .ruleContent {
  952. text-align: center;
  953. }
  954. </style>