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.

2308 lines
62 KiB

2 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
2 weeks ago
4 weeks ago
3 weeks ago
4 weeks ago
4 weeks ago
2 weeks ago
4 weeks ago
4 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
3 weeks ago
2 weeks ago
3 weeks ago
2 weeks ago
3 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
4 weeks ago
3 weeks ago
4 weeks ago
2 weeks ago
3 weeks ago
3 weeks ago
2 weeks ago
3 weeks ago
2 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
2 weeks ago
4 weeks ago
2 weeks ago
4 weeks ago
2 weeks ago
4 weeks ago
2 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
2 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
2 weeks ago
3 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
4 weeks ago
3 weeks ago
4 weeks ago
3 weeks ago
3 weeks ago
4 weeks ago
3 weeks ago
2 weeks ago
3 weeks ago
2 weeks ago
2 weeks ago
3 weeks ago
2 weeks ago
3 weeks ago
2 weeks ago
2 weeks ago
3 weeks ago
4 weeks ago
4 weeks ago
2 weeks ago
2 weeks ago
4 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
  1. <template>
  2. <div class="ai-emotion-container" ref="userInputDisplayRef">
  3. <!-- 金轮 -->
  4. <div class="golden-wheel">
  5. <img src="@/assets/img/AiEmotion/金轮.png" class="golden-wheel-img" alt="金轮图标"
  6. :class="{ 'rotating-image': isRotating }" />
  7. </div>
  8. <!-- 消息显示区域 -->
  9. <div class="user-input-display">
  10. <div v-for="(message, index) in messages" :key="index" class="message-container">
  11. <!-- 用户输入内容 -->
  12. <div v-if="message.sender === 'user'" class="message-bubble user-message">
  13. {{ message.text }}
  14. </div>
  15. <!-- AI返回结果 -->
  16. <div v-if="message.sender === 'ai'" class="message-bubble ai-message">
  17. {{ message.text }}
  18. </div>
  19. </div>
  20. </div>
  21. </div>
  22. <!-- 股票标签页 -->
  23. <StockTabs />
  24. <!-- 加载提示 -->
  25. <div v-if="isLoading" class="loading-container">
  26. <div class="loading-content">
  27. <div class="loading-spinner"></div>
  28. <div class="loading-text">AI小财神正在分析中请稍候...</div>
  29. </div>
  30. </div>
  31. <!-- 渲染整个页面 -->
  32. <div v-if="isPageLoaded" class="class01">
  33. <div class="class00">
  34. <!-- 四维矩阵图 -->
  35. <div class="class02">
  36. <div class="container">
  37. <!-- <img class="item" :src="item" alt="思维矩阵图片" /> -->
  38. <div class="span01">
  39. {{ stockName }}{{ stockName ? '量子四维矩阵图' : '' }}
  40. </div>
  41. </div>
  42. <span class="span02">{{ displayDate }}</span>
  43. </div>
  44. <div class="class0201">
  45. <img src="@/assets/img/AiEmotion/L1.png" alt="情绪监控图标">
  46. </div>
  47. <!-- 温度计图表 -->
  48. <div class="class03">
  49. <div class="class003">
  50. <div class="content1">
  51. <img class="img01" src="@/assets/img/AiEmotion/温度计.png" alt="温度计图标">
  52. <span class="title1">股票温度计</span>
  53. </div>
  54. <div class="div00">
  55. <div class="div01">股票温度{{ data2 ?? "NA" }}</div>
  56. <div class="div02">市场温度{{ data1 }}</div>
  57. </div>
  58. </div>
  59. <marketTemperature ref="marketTemperatureRef" />
  60. </div>
  61. </div>
  62. <div class="class0301">
  63. <img src="@/assets/img/AiEmotion/L2.png" alt="情绪解码图标">
  64. </div>
  65. <!-- 情绪解码器图表 -->
  66. <div class="class04">
  67. <div class="class0401">
  68. <img class="img02" src='@/assets/img/AiEmotion/emotionDecod.png' alt="情绪解码器图标">
  69. <span class="title2">情绪解码器</span>
  70. </div>
  71. <div class="class0402">
  72. <emotionDecod ref="emotionDecodRef"></emotionDecod>
  73. </div>
  74. </div>
  75. <div class="class0403">
  76. <img src="@/assets/img/AiEmotion/L3.png" alt="情绪推演图标">
  77. </div>
  78. <!-- 情绪探底雷达图表 -->
  79. <div class="class05">
  80. <div class="class0502">
  81. <img class="img03" src="@/assets/img/AiEmotion/探底雷达.png" alt="探底雷达图表">
  82. <span class="title3">情绪探底雷达</span>
  83. </div>
  84. <div class="class0503">
  85. <emotionalBottomRadar ref="emotionalBottomRadarRef"></emotionalBottomRadar>
  86. </div>
  87. </div>
  88. <div class="class0501">
  89. <img src="@/assets/img/AiEmotion/L4.png" alt="情绪套利">
  90. </div>
  91. <!-- 情绪能量转化器图表 -->
  92. <div class="class06">
  93. <div class="class0601">
  94. <img class="img04" src="@/assets/img/AiEmotion/能量转化器.png" alt="能量转化器图标">
  95. <span class="title4">情绪能量转化器</span>
  96. </div>
  97. <div class="class0603">
  98. <emoEnergyConverter ref="emoEnergyConverterRef"></emoEnergyConverter>
  99. </div>
  100. </div>
  101. <!-- 核心看点 -->
  102. <div class="class0702">
  103. <img src="@/assets/img/AiEmotion/核心看点.png" alt="核心看点字样">
  104. </div>
  105. <div class="bk-image">
  106. <div class="text-container">
  107. <p><span class="title">🔍 情绪监控-金融宇宙的量子检测网络</span>
  108. <span class="content">核心任务:构建全市场情绪引力场雷达实时监测资金流向和情绪波动</span>
  109. </p>
  110. <p><span class="title">🧠 情绪解码-主力思维的神经破译矩阵</span>
  111. <span class="content">核心任务:解构资金行为的量子密码破译主力操盘意图和策略布局</span>
  112. </p>
  113. <p><span class="title">🔮 情绪推演-未来战争的时空推演舱</span>
  114. <span class="content">核心任务:基于情绪数据推演未来走势预测市场转折点和机会窗口</span>
  115. </p>
  116. <p><span class="title">💰 情绪套利-财富裂变的粒子对撞机</span>
  117. <span class="content">核心任务:将情绪差转化为收益粒子流实现情绪能量的价值转换</span>
  118. </p>
  119. </div>
  120. </div>
  121. <!-- 核心逻辑 -->
  122. <div class="class0700">
  123. <img src="@/assets/img/AiEmotion/核心逻辑.png" alt="核心逻辑字样">
  124. </div>
  125. <div class="class08">
  126. <div class="lz-img">
  127. <img src="@/assets/img/AiEmotion/量子神经决策树.png" alt="树标题">
  128. </div>
  129. <div class="scaled-img">
  130. <!-- <img src="@/assets/img/AiEmotion/tree02.jpg" alt="树图片"> -->
  131. </div>
  132. </div>
  133. <!-- 场景应用 -->
  134. <div class="class09" ref="scenarioApplicationRef">
  135. <img src="@/assets/img/AiEmotion/场景应用.png" alt="场景应用标题">
  136. <div class="bk-image">
  137. <div class="conclusion-container" v-if="parsedConclusion">
  138. <div class="conclusion-item" v-if="(parsedConclusion.one1 || parsedConclusion.one2) && moduleVisibility.one">
  139. <h4 class="conclusion-title">{{ displayedTitles.one }}</h4>
  140. <p class="conclusion-text" v-if="parsedConclusion.one1">{{ displayedTexts.one1 }}</p>
  141. <p class="conclusion-text" v-if="parsedConclusion.one2">{{ displayedTexts.one2 }}</p>
  142. </div>
  143. <div class="conclusion-item" v-if="parsedConclusion.two && moduleVisibility.two">
  144. <h4 class="conclusion-title">{{ displayedTitles.two }}</h4>
  145. <p class="conclusion-text">{{ displayedTexts.two }}</p>
  146. </div>
  147. <div class="conclusion-item" v-if="parsedConclusion.three && moduleVisibility.three">
  148. <h4 class="conclusion-title">{{ displayedTitles.three }}</h4>
  149. <p class="conclusion-text">{{ displayedTexts.three }}</p>
  150. </div>
  151. <div class="conclusion-item" v-if="parsedConclusion.four && moduleVisibility.four">
  152. <h4 class="conclusion-title">{{ displayedTitles.four }}</h4>
  153. <p class="conclusion-text">{{ displayedTexts.four }}</p>
  154. </div>
  155. <!-- AI生成内容免责声明 -->
  156. <div class="disclaimer-item" v-if="parsedConclusion && moduleVisibility.disclaimer">
  157. <p class="disclaimer-text">{{ displayedTexts.disclaimer }}</p>
  158. </div>
  159. </div>
  160. <div class="conclusion-placeholder" v-else>
  161. <p>等待股票分析结论...</p>
  162. </div>
  163. </div>
  164. </div>
  165. </div>
  166. </template>
  167. <script setup>
  168. import { ref, computed, watch, nextTick, onMounted, onUnmounted } from 'vue';
  169. import { getReplyAPI, getConclusionAPI } from '@/api/AiEmotionApi.js'; // 导入工作流接口方法
  170. import axios from 'axios';
  171. import item from '@/assets/img/AiEmotion/bk01.png'; // 导入思维矩阵图片
  172. import emotionDecod from '@/views/components/emotionDecod.vue'; // 导入情绪解码组件
  173. import emotionalBottomRadar from '@/views/components/emotionalBottomRadar.vue'; // 导入情绪探底雷达图组件
  174. import emoEnergyConverter from '@/views/components/emoEnergyConverter.vue'; // 导入情绪能量转化器组件
  175. import marketTemperature from '@/views/components/marketTemperature.vue';
  176. import StockTabs from '@/views/components/StockTabs.vue'; // 导入股票标签页组件
  177. import blueBorderImg from '@/assets/img/AiEmotion/blueBorder.png' //导入蓝色背景框图片
  178. import { ElMessage } from 'element-plus';
  179. import { useEmotionStore } from '@/store/emotion'; // 导入Pinia store
  180. import { useAudioStore } from '@/store/audio.js'; // 导入音频store
  181. import { Howl, Howler } from 'howler'; // 导入音频播放库
  182. import { reactive } from 'vue';
  183. // 使用Pinia store
  184. const emotionStore = useEmotionStore();
  185. const audioStore = useAudioStore();
  186. // 组件引用
  187. const marketTemperatureRef = ref(null); // 引用市场温度计组件
  188. const emoEnergyConverterRef = ref(null)
  189. const emotionDecodRef = ref(null)
  190. const emotionalBottomRadarRef = ref(null)
  191. const userInputDisplayRef = ref(null);//消息区域的引用
  192. // 响应式数据
  193. const messages = ref([]);
  194. const isPageLoaded = ref(false); // 控制页面是否显示
  195. const isLoading = ref(false); // 控制加载状态
  196. const isRotating = ref(false);//控制旋转
  197. const version1 = ref(2); // 版本号
  198. const conclusionData = ref(''); // 存储第二个工作流接口返回的结论数据
  199. // 自动滚动相关数据
  200. const isAutoScrolling = ref(false);
  201. const currentSection = ref(0);
  202. const sectionRefs = ref([]);
  203. const scenarioApplicationRef = ref(null); // 场景应用部分的引用
  204. const hasTriggeredAudio = ref(false); // 是否已触发音频播放
  205. const hasTriggeredTypewriter = ref(false); // 是否已触发打字机效果
  206. const intersectionObserver = ref(null); // 存储observer实例
  207. // 显示的文本内容(用于打字机效果)
  208. const displayedTexts = ref({
  209. one1: '',
  210. one2: '',
  211. two: '',
  212. three: '',
  213. four: '',
  214. disclaimer: ''
  215. });
  216. // 显示的标题内容(用于打字机效果)
  217. const displayedTitles = ref({
  218. one: '',
  219. two: '',
  220. three: '',
  221. four: ''
  222. });
  223. // 模块显示状态
  224. const moduleVisibility = ref({
  225. one: false,
  226. two: false,
  227. three: false,
  228. four: false,
  229. disclaimer: false
  230. });
  231. const typewriterTimers = ref([]);
  232. // 记录每个股票是否已经显示过打字机效果
  233. const stockTypewriterShown = ref(new Map());
  234. // 记录每个股票是否已经播放过音频
  235. const stockAudioPlayed = ref(new Map());
  236. // 音频播放相关数据
  237. const audioUrl = ref('');
  238. const isAudioPlaying = ref(false);
  239. // 计算属性 - 从store获取当前股票数据
  240. const currentStock = computed(() => emotionStore.activeStock);
  241. const stockName = computed(() => currentStock.value?.stockInfo.name || "");
  242. const displayDate = computed(() => {
  243. if (!currentStock.value?.apiData) return "";
  244. const lastData = currentStock.value.apiData.GSWDJ?.at(-1);
  245. return lastData ? lastData[0] : "";
  246. });
  247. const data1 = computed(() => {
  248. if (!currentStock.value?.apiData) return null;
  249. const lastData = currentStock.value.apiData.GSWDJ?.at(-1);
  250. return lastData ? Math.round(lastData[1]) : null;
  251. });
  252. const data2 = computed(() => {
  253. if (!currentStock.value?.apiData) return null;
  254. const lastData = currentStock.value.apiData.GSWDJ?.at(-1);
  255. return lastData ? Math.round(lastData[2]) : null;
  256. });
  257. const currentConclusion = computed(() => {
  258. return currentStock.value?.conclusionData || '';
  259. });
  260. const parsedConclusion = computed(() => {
  261. if (!currentConclusion.value) return null;
  262. try {
  263. return JSON.parse(currentConclusion.value);
  264. } catch (error) {
  265. console.error('解析结论数据失败:', error);
  266. return null;
  267. }
  268. });
  269. // 监听当前股票变化,重新渲染图表
  270. watch(currentStock, (newStock) => {
  271. if (newStock && newStock.apiData) {
  272. isPageLoaded.value = true;
  273. isLoading.value = false; // 数据加载完成,关闭加载状态
  274. // 停止当前播放的音频
  275. stopAudio();
  276. // 清理正在进行的打字机效果定时器
  277. clearTypewriterTimers();
  278. // 重置触发状态,让每个股票都能独立触发效果
  279. hasTriggeredAudio.value = false;
  280. hasTriggeredTypewriter.value = false;
  281. // 获取股票代码作为唯一标识
  282. const stockCode = newStock.stockInfo?.code || newStock.stockInfo?.symbol;
  283. // 检查该股票是否已经显示过打字机效果
  284. if (stockCode && stockTypewriterShown.value.has(stockCode)) {
  285. // 如果已经显示过,直接显示完整文本和标题
  286. if (newStock.conclusionData) {
  287. try {
  288. const conclusion = JSON.parse(newStock.conclusionData);
  289. displayedTexts.value = {
  290. one1: conclusion.one1 || '',
  291. one2: conclusion.one2 || '',
  292. two: conclusion.two || '',
  293. three: conclusion.three || '',
  294. four: conclusion.four || '',
  295. disclaimer: '该内容由AI生成,请注意甄别'
  296. };
  297. displayedTitles.value = {
  298. one: 'L1: 情绪监控',
  299. two: 'L2: 情绪解码',
  300. three: 'L3: 情绪推演',
  301. four: 'L4: 情绪套利'
  302. };
  303. // 显示所有有内容的模块
  304. moduleVisibility.value = {
  305. one: !!(conclusion.one1 || conclusion.one2),
  306. two: !!conclusion.two,
  307. three: !!conclusion.three,
  308. four: !!conclusion.four,
  309. disclaimer: true
  310. };
  311. } catch (error) {
  312. console.error('解析结论数据失败:', error);
  313. }
  314. }
  315. } else {
  316. // 如果没有显示过,清空显示文本,等待打字机效果
  317. displayedTexts.value = {
  318. one1: '',
  319. one2: '',
  320. two: '',
  321. three: '',
  322. four: '',
  323. disclaimer: ''
  324. };
  325. displayedTitles.value = {
  326. one: '',
  327. two: '',
  328. three: '',
  329. four: ''
  330. };
  331. moduleVisibility.value = {
  332. one: false,
  333. two: false,
  334. three: false,
  335. four: false,
  336. disclaimer: false
  337. };
  338. }
  339. nextTick(() => {
  340. renderCharts(newStock.apiData);
  341. console.log('0000000000000000000000000',newStock.apiData)
  342. // 检查场景应用部分是否已经在视口中,如果是则立即触发效果
  343. setTimeout(() => {
  344. if (scenarioApplicationRef.value && parsedConclusion.value) {
  345. const stockCode = newStock.stockInfo?.code || newStock.stockInfo?.symbol;
  346. // 如果该股票已经显示过,不需要再处理
  347. if (stockCode && stockTypewriterShown.value.has(stockCode)) {
  348. return;
  349. }
  350. const rect = scenarioApplicationRef.value.getBoundingClientRect();
  351. const isInViewport = rect.top < window.innerHeight && rect.bottom > 0;
  352. if (isInViewport) {
  353. console.log('股票切换后检测到场景应用部分在视口中');
  354. if (stockCode) {
  355. // 检查该股票是否是第一次触发
  356. if (!stockTypewriterShown.value.has(stockCode)) {
  357. // 该股票第一次:播放音频和打字机效果
  358. if (audioUrl.value) {
  359. console.log('该股票第一次进入场景应用,开始打字机效果和音频播放');
  360. hasTriggeredTypewriter.value = true;
  361. hasTriggeredAudio.value = true;
  362. startTypewriterEffect(parsedConclusion.value);
  363. if (!stockAudioPlayed.value.has(stockCode)) {
  364. console.log('开始音频播放');
  365. stockAudioPlayed.value.set(stockCode, true);
  366. playAudio(audioUrl.value);
  367. }
  368. stockTypewriterShown.value.set(stockCode, true);
  369. } else {
  370. console.log('音频尚未准备好,等待音频加载完成后再触发效果(股票切换后)');
  371. return;
  372. }
  373. } else {
  374. // 非第一次或已经触发过:直接显示完整内容,不播放音频和打字机效果
  375. console.log('非第一次股票切换或已触发过,直接显示完整内容');
  376. // 直接显示完整内容
  377. const conclusion = parsedConclusion.value;
  378. displayedTexts.value = {
  379. one1: conclusion.one1 || '',
  380. one2: conclusion.one2 || '',
  381. two: conclusion.two || '',
  382. three: conclusion.three || '',
  383. four: conclusion.four || '',
  384. disclaimer: '该内容由AI内容生成,请注意甄别'
  385. };
  386. displayedTitles.value = {
  387. one: 'L1: 情绪监控',
  388. two: 'L2: 情绪解码',
  389. three: 'L3: 情绪推演',
  390. four: 'L4: 情绪套利'
  391. };
  392. moduleVisibility.value = {
  393. one: !!(conclusion.one1 || conclusion.one2),
  394. two: !!conclusion.two,
  395. three: !!conclusion.three,
  396. four: !!conclusion.four,
  397. disclaimer: true
  398. };
  399. }
  400. }
  401. }
  402. }
  403. }, 500); // 延迟500ms确保数据完全加载
  404. });
  405. } else {
  406. isPageLoaded.value = false;
  407. }
  408. }, { immediate: true });
  409. // 监听parsedConclusion变化,准备数据但不立即触发打字机效果
  410. watch(parsedConclusion, (newConclusion) => {
  411. if (newConclusion) {
  412. console.log('场景应用结论数据:', newConclusion);
  413. // 不再立即开始打字机效果,等待滚动到场景应用部分时触发
  414. // 尝试多种可能的语音URL字段名
  415. let voiceUrl = null;
  416. if (newConclusion.url) {
  417. // 清理URL字符串,去除空格、反引号等特殊字符
  418. voiceUrl = newConclusion.url.toString().trim().replace(/[`\s]/g, '');
  419. } else if (newConclusion.audioUrl) {
  420. voiceUrl = newConclusion.audioUrl.toString().trim().replace(/[`\s]/g, '');
  421. } else if (newConclusion.voice_url) {
  422. voiceUrl = newConclusion.voice_url.toString().trim().replace(/[`\s]/g, '');
  423. } else if (newConclusion.audio) {
  424. voiceUrl = newConclusion.audio.toString().trim().replace(/[`\s]/g, '');
  425. } else if (newConclusion.tts_url) {
  426. voiceUrl = newConclusion.tts_url.toString().trim().replace(/[`\s]/g, '');
  427. }
  428. if (voiceUrl && voiceUrl.startsWith('http')) {
  429. console.log('找到并清理后的语音URL:', voiceUrl);
  430. audioUrl.value = voiceUrl;
  431. console.log('音频URL已准备,检查是否需要立即触发效果');
  432. // 音频准备好后,检查场景应用部分是否已经在视口中
  433. nextTick(() => {
  434. setTimeout(() => {
  435. if (scenarioApplicationRef.value && currentStock.value?.stockInfo) {
  436. const stockCode = currentStock.value.stockInfo.code || currentStock.value.stockInfo.symbol;
  437. const rect = scenarioApplicationRef.value.getBoundingClientRect();
  438. const isInViewport = rect.top < window.innerHeight && rect.bottom > 0;
  439. if (isInViewport && parsedConclusion.value && stockCode) {
  440. // 如果该股票已经显示过,不需要再处理
  441. if (stockTypewriterShown.value.has(stockCode)) {
  442. return;
  443. }
  444. // 该股票第一次:播放音频和打字机效果
  445. console.log('该股票第一次音频准备完成且场景应用部分在视口中,立即触发效果');
  446. hasTriggeredTypewriter.value = true;
  447. hasTriggeredAudio.value = true;
  448. startTypewriterEffect(parsedConclusion.value);
  449. if (!stockAudioPlayed.value.has(stockCode)) {
  450. console.log('立即开始音频播放');
  451. stockAudioPlayed.value.set(stockCode, true);
  452. playAudio(audioUrl.value);
  453. }
  454. stockTypewriterShown.value.set(stockCode, true);
  455. }
  456. }
  457. }, 100); // 短暂延迟确保DOM更新完成
  458. });
  459. } else {
  460. console.log('未找到有效的语音URL,原始URL:', newConclusion.url);
  461. console.log('结论数据中的所有字段:', Object.keys(newConclusion));
  462. }
  463. }
  464. }, { immediate: true });
  465. // 打字机效果函数
  466. function startTypewriterEffect(conclusion) {
  467. console.log('开始打字机效果,结论数据:', conclusion);
  468. // 详细调试各个字段
  469. console.log('L1字段 - one1:', conclusion.one1);
  470. console.log('L1字段 - one2:', conclusion.one2);
  471. console.log('L2字段 - two:', conclusion.two);
  472. console.log('L3字段 - three:', conclusion.three);
  473. console.log('L4字段 - four:', conclusion.four);
  474. // 清除之前的定时器
  475. typewriterTimers.value.forEach(timer => clearTimeout(timer));
  476. typewriterTimers.value = [];
  477. // 重置显示文本和状态
  478. displayedTexts.value = {
  479. one1: '',
  480. one2: '',
  481. two: '',
  482. three: '',
  483. four: '',
  484. disclaimer: ''
  485. };
  486. displayedTitles.value = {
  487. one: '',
  488. two: '',
  489. three: '',
  490. four: ''
  491. };
  492. moduleVisibility.value = {
  493. one: false,
  494. two: false,
  495. three: false,
  496. four: false,
  497. disclaimer: false
  498. };
  499. // 定义打字速度(毫秒)
  500. const typeSpeed = 200;
  501. let totalDelay = 0;
  502. // 定义模块配置
  503. const modules = [
  504. {
  505. key: 'one',
  506. title: 'L1: 情绪监控',
  507. contents: [
  508. { key: 'one1', text: conclusion.one1 },
  509. { key: 'one2', text: conclusion.one2 }
  510. ]
  511. },
  512. {
  513. key: 'two',
  514. title: 'L2: 情绪解码',
  515. contents: [
  516. { key: 'two', text: conclusion.two }
  517. ]
  518. },
  519. {
  520. key: 'three',
  521. title: 'L3: 情绪推演',
  522. contents: [
  523. { key: 'three', text: conclusion.three }
  524. ]
  525. },
  526. {
  527. key: 'four',
  528. title: 'L4: 情绪套利',
  529. contents: [
  530. { key: 'four', text: conclusion.four }
  531. ]
  532. }
  533. ];
  534. // 按模块顺序处理
  535. modules.forEach((module) => {
  536. // 检查模块是否有内容
  537. const hasContent = module.contents.some(content => content.text && content.text.trim());
  538. console.log(`模块 ${module.key} 是否有内容:`, hasContent, '内容:', module.contents.map(c => c.text));
  539. if (!hasContent) return;
  540. console.log(`开始显示模块 ${module.key}`);
  541. // 显示模块
  542. const showModuleTimer = setTimeout(() => {
  543. moduleVisibility.value[module.key] = true;
  544. console.log(`模块 ${module.key} 已设置为可见`);
  545. }, totalDelay);
  546. typewriterTimers.value.push(showModuleTimer);
  547. totalDelay += 100;
  548. // 打字机效果显示标题
  549. const title = module.title;
  550. for (let i = 0; i <= title.length; i++) {
  551. const timer = setTimeout(() => {
  552. displayedTitles.value[module.key] = title.substring(0, i);
  553. }, totalDelay + i * typeSpeed);
  554. typewriterTimers.value.push(timer);
  555. }
  556. totalDelay += title.length * typeSpeed + 300; // 标题完成后间隔
  557. // 打字机效果显示内容
  558. module.contents.forEach((content) => {
  559. if (content.text && content.text.trim()) {
  560. const text = content.text;
  561. for (let i = 0; i <= text.length; i++) {
  562. const timer = setTimeout(() => {
  563. displayedTexts.value[content.key] = text.substring(0, i);
  564. }, totalDelay + i * typeSpeed);
  565. typewriterTimers.value.push(timer);
  566. }
  567. totalDelay += text.length * typeSpeed + 500; // 内容完成后间隔
  568. }
  569. });
  570. totalDelay += 800; // 模块间间隔
  571. });
  572. // 添加免责声明的打字机效果(在所有模块显示完成后)
  573. const disclaimerText = '该内容由AI内容生成,请注意甄别';
  574. // 显示免责声明模块
  575. const showDisclaimerTimer = setTimeout(() => {
  576. moduleVisibility.value.disclaimer = true;
  577. }, totalDelay);
  578. typewriterTimers.value.push(showDisclaimerTimer);
  579. totalDelay += 100;
  580. // 打字机效果显示免责声明
  581. for (let i = 0; i <= disclaimerText.length; i++) {
  582. const timer = setTimeout(() => {
  583. displayedTexts.value.disclaimer = disclaimerText.substring(0, i);
  584. }, totalDelay + i * typeSpeed);
  585. typewriterTimers.value.push(timer);
  586. }
  587. }
  588. // 清理定时器的函数
  589. function clearTypewriterTimers() {
  590. typewriterTimers.value.forEach(timer => clearTimeout(timer));
  591. typewriterTimers.value = [];
  592. }
  593. // 音频播放函数
  594. function playAudio(url) {
  595. console.log('尝试播放音频:', url);
  596. if (!url) {
  597. console.warn('音频URL为空,跳过播放');
  598. isAudioPlaying.value = false;
  599. return;
  600. }
  601. // 检查是否启用了语音功能
  602. console.log('语音功能状态:', audioStore.isVoiceEnabled);
  603. if (!audioStore.isVoiceEnabled) {
  604. console.log('语音功能已关闭,跳过播放');
  605. return;
  606. }
  607. console.log('开始创建音频实例...');
  608. try {
  609. // 停止之前的音频
  610. if (audioStore.nowSound) {
  611. audioStore.nowSound.stop();
  612. }
  613. // 创建新的音频实例
  614. const newSound = new Howl({
  615. src: [url],
  616. html5: true,
  617. format: ['mp3', 'wav'],
  618. onplay: () => {
  619. isAudioPlaying.value = true;
  620. console.log('开始播放场景应用语音');
  621. },
  622. onend: () => {
  623. isAudioPlaying.value = false;
  624. console.log('场景应用语音播放结束');
  625. },
  626. onstop: () => {
  627. isAudioPlaying.value = false;
  628. console.log('场景应用语音播放停止');
  629. },
  630. onerror: (error) => {
  631. isAudioPlaying.value = false;
  632. console.error('音频播放错误:', error);
  633. }
  634. });
  635. // 保存音频实例到store
  636. audioStore.nowSound = newSound;
  637. audioStore.setAudioInstance(newSound);
  638. // 播放音频
  639. newSound.play();
  640. } catch (error) {
  641. console.error('创建音频实例失败:', error);
  642. isAudioPlaying.value = false;
  643. }
  644. }
  645. // 停止音频播放
  646. function stopAudio() {
  647. if (audioStore.nowSound) {
  648. audioStore.nowSound.stop();
  649. }
  650. isAudioPlaying.value = false;
  651. }
  652. // 触发图片旋转的方法
  653. function startImageRotation() {
  654. isRotating.value = true;
  655. // 如果你想在一段时间后停止旋转,可以添加以下代码
  656. setTimeout(() => {
  657. isRotating.value = false;
  658. }, 5000); // 5 秒后停止旋转
  659. }
  660. // 发送消息方法
  661. async function handleSendMessage(input) {
  662. console.log("发送内容:", input);
  663. // 检查用户输入内容是否为空
  664. if (input.trim()) {
  665. const userMessage = reactive({ sender: 'user', text: input });
  666. messages.value.push(userMessage);
  667. // 设置加载状态,隐藏图表页面
  668. isLoading.value = true;
  669. isPageLoaded.value = false;
  670. // 触发图片旋转
  671. isRotating.value = true;
  672. try {
  673. // 用来调用工作流接口的参数
  674. const params = {
  675. content: userMessage.text,
  676. userData: {
  677. token:
  678. "9ior41AF0xTIbIG2pRnnbZi0+fEeMx8pywnIlrmTwo5FbqJ9lWrSWOxp9MkpKiNtedtUafqvzIwpFKrwuMs",
  679. language: "cn",
  680. brainPrivilegeState: "1",
  681. swordPrivilegeState: "1",
  682. stockForecastPrivile: "1",
  683. spaceForecastPrivile: "1",
  684. aibullPrivilegeState: "1",
  685. aigoldBullPrivilegeS: "1",
  686. airadarPrivilegeStat: "1",
  687. marketList: "hk,cn,usa,my,sg,vi,in,gb",
  688. },
  689. };
  690. const result = await getReplyAPI(params);
  691. const response = await result.json(); // 解析返回的 JSON 数据
  692. console.log("工作流接口返回数据:", response);
  693. // 解析 data 字段
  694. const parsedData = JSON.parse(response.data); // 将字符串形式的 JSON 转换为对象
  695. console.log("解析后的数据:", parsedData);
  696. if (parsedData && parsedData.market && parsedData.code) {
  697. console.log("工作流接口返回股票信息:", parsedData);
  698. // 调用第二个工作流接口
  699. const conclusionParams = {
  700. content: input.trim(),
  701. userData: {
  702. token:
  703. "9ior41AF0xTIbIG2pRnnbZi0+fEeMx8pywnIlrmTwo5FbqJ9lWrSWOxp9MkpKiNtedtUafqvzIwpFKrwuMs",
  704. language: "cn",
  705. marketList: "hk,cn,usa,my,sg,vi,in,gb",
  706. },
  707. code: parsedData.code,
  708. market: parsedData.market,
  709. };
  710. console.log('======================================')
  711. // 取消自动滚动效果
  712. // console.log('第二个工作流接口开始调用,立即开始缓慢滚动');
  713. // startAutoScroll();
  714. // 同时调用第二个数据流接口和fetchData方法
  715. const [conclusionResult, fetchDataResult] = await Promise.all([
  716. getConclusionAPI(conclusionParams),
  717. fetchData(parsedData.code, parsedData.market, parsedData.name || "未知股票", input.trim())
  718. ]);
  719. // 处理结论接口返回的数据
  720. const conclusionResponse = await conclusionResult.json();
  721. console.log("第二个工作流接口返回数据:", conclusionResponse);
  722. // 将结论数据存储到响应式变量和store中
  723. if (conclusionResponse && conclusionResponse.data) {
  724. conclusionData.value = conclusionResponse.data;
  725. // 将结论数据存储到store中的当前激活股票
  726. emotionStore.updateActiveStockConclusion(conclusionResponse.data);
  727. console.log("结论数据已存储到响应式变量和store中:", conclusionData.value);
  728. }
  729. console.log('------------------------------------')
  730. } else {
  731. ElMessage.error('工作流接口未返回非股票信息');
  732. }
  733. } catch (error) {
  734. ElMessage.error('请求工作流接口失败,请检查网络连接');
  735. } finally {
  736. // 停止图片旋转
  737. isRotating.value = false;
  738. }
  739. } else {
  740. ElMessage.error('消息发送失败,请检查网络连接');
  741. }
  742. }
  743. // 请求数据接口
  744. async function fetchData(code, market, stockName, queryText) {
  745. try {
  746. const stockDataParams = {
  747. // token: '+XgqsgdW0RLIbIG2pxnnbZi0+fEeMx8pywnIlrmTxtkSaPZ9xjSOWrxq+s0rL3RrfNhXPvGtz9srFfjwu8A',
  748. token: '9ior41AF0xTIbIG2pRnnbZi0+fEeMx8pywnIlrmTwo5FbqJ9lWrSWOxp9MkpKiNtedtUafqvzIwpFKrwuMs',
  749. market: market,
  750. code: code,
  751. language: 'cn',
  752. version: version1.value
  753. };
  754. const stockDataResult = await axios.post(
  755. // "http://39.101.133.168:8828/link/api/aiEmotion/client/getAiEmotionData",
  756. 'https://api.homilychart.com/link/api/aiEmotion/client/getAiEmotionData',
  757. stockDataParams,
  758. {
  759. headers: {
  760. "Content-Type": "application/json",
  761. },
  762. }
  763. );
  764. const stockDataResponse = stockDataResult.data; // 获取返回所有的数据
  765. console.log('图表数据接口返回数据:', stockDataResponse.data);
  766. if (stockDataResponse.code === 200 && stockDataResponse.data) {
  767. console.log(stockDataResponse.code)
  768. // 创建股票数据对象
  769. const stockData = {
  770. queryText: queryText,
  771. stockInfo: {
  772. name: stockName,
  773. code: code,
  774. market: market
  775. },
  776. apiData: stockDataResponse.data,
  777. conclusionData: conclusionData.value, // 包含结论数据
  778. timestamp: new Date().toISOString()
  779. };
  780. // 将股票数据添加到store中
  781. emotionStore.addStock(stockData);
  782. console.log('股票数据已添加到store');
  783. } else {
  784. ElMessage.error('获取接口数据失败');
  785. }
  786. } catch (error) {
  787. ElMessage.error('获取接口数据失败。。。');
  788. }
  789. }
  790. // 渲染组件图表的方法
  791. function renderCharts(data) {
  792. nextTick(() => {
  793. // 添加小延迟确保DOM完全更新
  794. setTimeout(() => {
  795. try {
  796. // 深拷贝数据避免污染原始数据
  797. const clonedData = JSON.parse(JSON.stringify(data));
  798. console.log('已深拷贝数据,避免污染原始数据');
  799. console.log('所有数据1111111111111:', clonedData)
  800. // 渲染股市温度计图表
  801. if (marketTemperatureRef.value && clonedData.GSWDJ) {
  802. console.log("开始渲染股市温度计图表");
  803. console.log("股市温度计数据", clonedData.GSWDJ);
  804. marketTemperatureRef.value.initChart(clonedData.GSWDJ, clonedData.KLine20, clonedData.WDRL);
  805. console.log("股市温度计图表已渲染");
  806. }
  807. // 渲染情绪解码器图表
  808. if (emotionDecodRef.value && clonedData.QXJMQ) {
  809. console.log("开始渲染情绪解码器图表");
  810. console.log("情绪解码器数据", clonedData.QXJMQ);
  811. emotionDecodRef.value.initQXNLZHEcharts(clonedData.KLine20, clonedData.QXJMQ);
  812. console.log("情绪解码器图表已渲染");
  813. }
  814. // 渲染情绪探底雷达图表
  815. if (emotionalBottomRadarRef.value && clonedData.QXTDLD) {
  816. console.log("开始渲染情绪探底雷达图表");
  817. console.log("探底雷达数据", clonedData.QXTDLD);
  818. emotionalBottomRadarRef.value.initEmotionalBottomRadar(
  819. clonedData.KLine20,
  820. clonedData.QXTDLD
  821. );
  822. console.log("情绪探底雷达图表已渲染");
  823. }
  824. // 渲染情绪能量转化器图表
  825. if (emoEnergyConverterRef.value && clonedData.QXNLZHQ) {
  826. console.log("开始渲染情绪能量转化器图表");
  827. console.log("KLine20:", clonedData.KLine20);
  828. console.log("QXNLZHQ:", clonedData.QXNLZHQ);
  829. emoEnergyConverterRef.value.initQXNLZHEcharts(clonedData.KLine20, clonedData.QXNLZHQ);
  830. console.log("情绪能量转化器图表已渲染");
  831. }
  832. } catch (error) {
  833. console.error('图表渲染过程中发生错误:', error);
  834. ElMessage.error('图表渲染失败,请重试');
  835. }
  836. }, 100); // 100ms延迟确保DOM稳定
  837. });
  838. }
  839. const scrollToBottom = async () => {
  840. const container = userInputDisplayRef.value;
  841. if (!container) return;
  842. await nextTick();
  843. console.log(container.scrollHeight, "container.scrollHeight");
  844. console.log(container.scrollTop, "container.scrollTop");
  845. console.log(container.offsetHeight, "container.offsetHeight");
  846. container.scrollTop = container.scrollHeight - container.offsetHeight;
  847. };
  848. // 检查数据是否已加载完成
  849. function isDataLoaded() {
  850. // 检查页面是否已加载
  851. if (!isPageLoaded.value) {
  852. console.log('页面数据尚未加载完成');
  853. return false;
  854. }
  855. // 检查当前股票数据是否存在
  856. if (!currentStock.value || !currentStock.value.apiData) {
  857. console.log('股票数据尚未加载完成');
  858. return false;
  859. }
  860. // 检查图表组件是否已渲染
  861. const requiredRefs = [
  862. marketTemperatureRef.value,
  863. emotionDecodRef.value,
  864. emotionalBottomRadarRef.value,
  865. emoEnergyConverterRef.value
  866. ];
  867. const allRefsLoaded = requiredRefs.every(ref => ref !== null);
  868. if (!allRefsLoaded) {
  869. console.log('图表组件尚未完全加载');
  870. return false;
  871. }
  872. console.log('所有数据和组件已加载完成,可以开始滚动');
  873. return true;
  874. }
  875. // 自动滚动函数
  876. function startAutoScroll() {
  877. if (isAutoScrolling.value) return;
  878. // 检查数据是否已加载完成
  879. if (!isDataLoaded()) {
  880. console.log('数据尚未加载完成,延迟1秒后重试');
  881. setTimeout(() => {
  882. startAutoScroll();
  883. }, 1000);
  884. return;
  885. }
  886. isAutoScrolling.value = true;
  887. console.log('开始流畅自动滚动');
  888. // 获取页面总高度
  889. const documentHeight = document.documentElement.scrollHeight;
  890. const windowHeight = window.innerHeight;
  891. const maxScrollTop = documentHeight - windowHeight;
  892. // 滚动参数
  893. const scrollDuration = 15000; // 总滚动时间15秒
  894. const scrollStep = maxScrollTop / (scrollDuration / 50); // 每50ms滚动的距离
  895. let currentScrollTop = 0;
  896. function smoothScroll() {
  897. if (currentScrollTop < maxScrollTop) {
  898. currentScrollTop += scrollStep;
  899. if (currentScrollTop > maxScrollTop) {
  900. currentScrollTop = maxScrollTop;
  901. }
  902. window.scrollTo({
  903. top: currentScrollTop,
  904. behavior: 'auto' // 使用auto而不是smooth,因为我们自己控制滚动
  905. });
  906. // 继续滚动
  907. setTimeout(smoothScroll, 50);
  908. } else {
  909. console.log('流畅自动滚动完成');
  910. isAutoScrolling.value = false;
  911. }
  912. }
  913. // 延迟1秒开始滚动
  914. setTimeout(smoothScroll, 1000);
  915. }
  916. // 设置Intersection Observer监听场景应用部分
  917. function setupIntersectionObserver() {
  918. if (!scenarioApplicationRef.value) return;
  919. const observer = new IntersectionObserver(
  920. (entries) => {
  921. entries.forEach((entry) => {
  922. if (entry.isIntersecting) {
  923. console.log('场景应用部分进入视口');
  924. // 获取当前股票代码
  925. const stockCode = currentStock.value?.stockInfo?.code || currentStock.value?.stockInfo?.symbol;
  926. if (parsedConclusion.value && stockCode) {
  927. // 检查该股票是否是第一次触发
  928. if (!stockTypewriterShown.value.has(stockCode)) {
  929. // 该股票第一次:播放音频和打字机效果
  930. if (audioUrl.value) {
  931. console.log('该股票第一次进入场景应用,开始打字机效果和音频播放');
  932. hasTriggeredTypewriter.value = true;
  933. hasTriggeredAudio.value = true;
  934. // 开始打字机效果
  935. startTypewriterEffect(parsedConclusion.value);
  936. // 播放音频
  937. if (!stockAudioPlayed.value.has(stockCode)) {
  938. console.log('开始音频播放');
  939. stockAudioPlayed.value.set(stockCode, true);
  940. playAudio(audioUrl.value);
  941. }
  942. // 记录该股票已显示过
  943. stockTypewriterShown.value.set(stockCode, true);
  944. } else {
  945. console.log('音频尚未准备好,等待音频加载完成后再触发效果');
  946. return;
  947. }
  948. } else {
  949. // 非第一次或已经触发过:直接显示完整内容,不播放音频和打字机效果
  950. console.log('非第一次进入场景应用或已触发过,直接显示完整内容');
  951. // 直接显示完整内容
  952. const conclusion = parsedConclusion.value;
  953. displayedTexts.value = {
  954. one1: conclusion.one1 || '',
  955. one2: conclusion.one2 || '',
  956. two: conclusion.two || '',
  957. three: conclusion.three || '',
  958. four: conclusion.four || '',
  959. disclaimer: '该内容由AI内容生成,请注意甄别'
  960. };
  961. displayedTitles.value = {
  962. one: 'L1: 情绪监控',
  963. two: 'L2: 情绪解码',
  964. three: 'L3: 情绪推演',
  965. four: 'L4: 情绪套利'
  966. };
  967. // 显示所有有内容的模块
  968. moduleVisibility.value = {
  969. one: !!(conclusion.one1 || conclusion.one2),
  970. two: !!conclusion.two,
  971. three: !!conclusion.three,
  972. four: !!conclusion.four,
  973. disclaimer: true
  974. };
  975. }
  976. }
  977. }
  978. });
  979. },
  980. {
  981. threshold: 0.3, // 当30%的元素进入视口时触发
  982. rootMargin: '0px 0px -100px 0px' // 提前100px触发
  983. }
  984. );
  985. observer.observe(scenarioApplicationRef.value);
  986. intersectionObserver.value = observer;
  987. }
  988. // 手动触发自动滚动
  989. function triggerAutoScroll() {
  990. // 检查是否正在滚动
  991. if (isAutoScrolling.value) {
  992. console.log('自动滚动正在进行中,请稍候');
  993. return;
  994. }
  995. // 检查数据是否已准备好
  996. if (!isDataLoaded()) {
  997. console.log('数据尚未准备完成,请等待数据加载后再试');
  998. // 可以显示提示信息给用户
  999. return;
  1000. }
  1001. console.log('手动触发自动滚动');
  1002. startAutoScroll();
  1003. }
  1004. // 页面挂载完成后触发图片旋转和设置滚动监听
  1005. onMounted(() => {
  1006. startImageRotation();
  1007. // 等待DOM完全渲染后设置监听器
  1008. nextTick(() => {
  1009. setupIntersectionObserver();
  1010. // 移除自动滚动触发,改为在第二个工作流接口调用时触发
  1011. });
  1012. });
  1013. // 组件卸载时清理定时器、音频和observer
  1014. onUnmounted(() => {
  1015. clearTypewriterTimers();
  1016. stopAudio();
  1017. // 重置触发状态
  1018. hasTriggeredAudio.value = false;
  1019. hasTriggeredTypewriter.value = false;
  1020. // 清理Intersection Observer
  1021. if (intersectionObserver.value) {
  1022. intersectionObserver.value.disconnect();
  1023. intersectionObserver.value = null;
  1024. }
  1025. });
  1026. // 导出方法供外部使用
  1027. defineExpose({
  1028. handleSendMessage,
  1029. triggerAutoScroll
  1030. });
  1031. </script>
  1032. <style scoped>
  1033. .container {
  1034. padding-top: 2%;
  1035. }
  1036. .class003 {
  1037. padding-top: 8%;
  1038. padding-left: 10%;
  1039. }
  1040. .class003 .div02 {
  1041. background-image: url('@/assets/img/AiEmotion/redBorder.png');
  1042. background-repeat: no-repeat;
  1043. background-size: 100% 100%;
  1044. width: 35%;
  1045. min-height: 40px;
  1046. float: left;
  1047. margin-left: 9%;
  1048. margin-top: 2%;
  1049. text-align: center;
  1050. font-size: 24px;
  1051. color: white;
  1052. display: flex;
  1053. justify-content: center;
  1054. align-items: center;
  1055. }
  1056. .class003 .div01 {
  1057. background-image: url('@/assets/img/AiEmotion/blueBorder.png');
  1058. background-repeat: no-repeat;
  1059. background-size: 100% 100%;
  1060. width: 35%;
  1061. min-height: 40px;
  1062. float: left;
  1063. margin-left: 9%;
  1064. text-align: center;
  1065. font-size: 24px;
  1066. color: white;
  1067. display: flex;
  1068. justify-content: center;
  1069. align-items: center;
  1070. }
  1071. .golden-wheel {
  1072. width: 100%;
  1073. display: flex;
  1074. justify-content: center;
  1075. }
  1076. .golden-wheel-img {
  1077. width: 60%;
  1078. max-width: 500px;
  1079. height: auto;
  1080. }
  1081. /* 定义旋转动画 */
  1082. @keyframes rotate {
  1083. from {
  1084. transform: rotate(0deg);
  1085. }
  1086. to {
  1087. transform: rotate(360deg);
  1088. }
  1089. }
  1090. /* 应用动画到图片 */
  1091. .rotating-image {
  1092. animation: rotate 5s linear;
  1093. /* 5 秒完成一次旋转,线性速度*/
  1094. will-change: transform;
  1095. /* 优化动画性能 */
  1096. }
  1097. .bk-image {
  1098. background-image: url("@/assets/img/AiEmotion/bk03.png");
  1099. background-size: 100% 100%;
  1100. background-repeat: no-repeat;
  1101. width: 95%;
  1102. height: auto;
  1103. margin: 0 auto;
  1104. margin-top: 20px;
  1105. .conclusion-container {
  1106. padding: 20px;
  1107. border-radius: 15px;
  1108. margin: 20px;
  1109. background: linear-gradient(135deg, rgba(0, 212, 255, 0.15) 0%, rgba(0, 100, 200, 0.15) 100%);
  1110. border: 2px solid rgba(0, 212, 255, 0.4);
  1111. box-shadow: 0 8px 25px rgba(0, 212, 255, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.1);
  1112. backdrop-filter: blur(15px);
  1113. .conclusion-item {
  1114. margin-bottom: 20px;
  1115. padding: 20px;
  1116. border-radius: 12px;
  1117. background: linear-gradient(135deg, rgba(0, 212, 255, 0.2) 0%, rgba(0, 150, 255, 0.1) 100%);
  1118. border: 1px solid rgba(0, 212, 255, 0.5);
  1119. border-left: 5px solid #00d4ff;
  1120. box-shadow: 0 4px 15px rgba(0, 212, 255, 0.2), inset 0 1px 0 rgba(255, 255, 255, 0.1);
  1121. transition: all 0.3s ease;
  1122. position: relative;
  1123. overflow: hidden;
  1124. &::before {
  1125. content: '';
  1126. position: absolute;
  1127. top: 0;
  1128. left: 0;
  1129. right: 0;
  1130. height: 2px;
  1131. background: linear-gradient(90deg, #00d4ff, #0099ff, #00d4ff);
  1132. opacity: 0.8;
  1133. }
  1134. &:last-child {
  1135. margin-bottom: 0;
  1136. }
  1137. .conclusion-title {
  1138. color: #FFD700;
  1139. font-size: 22px;
  1140. font-weight: bold;
  1141. margin: 0 0 15px 0;
  1142. text-align: center;
  1143. text-shadow: 0 2px 8px rgba(0, 212, 255, 0.5), 0 0 20px rgba(0, 212, 255, 0.3);
  1144. letter-spacing: 2px;
  1145. position: relative;
  1146. &::after {
  1147. content: '';
  1148. position: absolute;
  1149. bottom: -5px;
  1150. left: 50%;
  1151. transform: translateX(-50%);
  1152. width: 60px;
  1153. height: 2px;
  1154. background: linear-gradient(90deg, transparent, #00d4ff, transparent);
  1155. }
  1156. }
  1157. .conclusion-text {
  1158. color: #ffffff;
  1159. font-size: 20px;
  1160. line-height: 1.8;
  1161. margin: 0 0 12px 0;
  1162. text-align: left;
  1163. word-wrap: break-word;
  1164. text-shadow: 0 1px 3px rgba(0, 0, 0, 0.7);
  1165. padding-left: 15px;
  1166. position: relative;
  1167. &::before {
  1168. content: '▶';
  1169. position: absolute;
  1170. left: 0;
  1171. top: 0;
  1172. color: #00d4ff;
  1173. font-size: 12px;
  1174. opacity: 0.7;
  1175. }
  1176. &:last-child {
  1177. margin-bottom: 0;
  1178. }
  1179. }
  1180. }
  1181. }
  1182. .conclusion-placeholder {
  1183. padding: 30px;
  1184. text-align: center;
  1185. border-radius: 12px;
  1186. background: rgba(255, 255, 255, 0.05);
  1187. border: 1px dashed rgba(153, 153, 153, 0.3);
  1188. p {
  1189. color: #999999;
  1190. font-size: 16px;
  1191. margin: 0;
  1192. font-style: italic;
  1193. }
  1194. }
  1195. }
  1196. /* 最后文字的颜色 */
  1197. .text-container {
  1198. position: relative;
  1199. color: white;
  1200. text-align: left;
  1201. padding: 20px;
  1202. border-radius: 15px;
  1203. margin: 20px;
  1204. background: linear-gradient(135deg, rgba(0, 212, 255, 0.15) 0%, rgba(0, 100, 200, 0.15) 100%);
  1205. border: 2px solid rgba(0, 212, 255, 0.4);
  1206. box-shadow: 0 8px 25px rgba(0, 212, 255, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.1);
  1207. backdrop-filter: blur(15px);
  1208. }
  1209. .text-container p {
  1210. margin: 0 auto;
  1211. font-size: 40px;
  1212. margin-left: 0%;
  1213. padding: 20px;
  1214. border-radius: 12px;
  1215. background: linear-gradient(135deg, rgba(0, 212, 255, 0.2) 0%, rgba(0, 150, 255, 0.1) 100%);
  1216. border: 1px solid rgba(0, 212, 255, 0.5);
  1217. border-left: 5px solid #00d4ff;
  1218. box-shadow: 0 4px 15px rgba(0, 212, 255, 0.2), inset 0 1px 0 rgba(255, 255, 255, 0.1);
  1219. transition: all 0.3s ease;
  1220. position: relative;
  1221. overflow: hidden;
  1222. text-shadow: 0 2px 8px rgba(0, 212, 255, 0.5), 0 0 20px rgba(0, 212, 255, 0.3);
  1223. letter-spacing: 2px;
  1224. }
  1225. .text-container p::before {
  1226. content: '';
  1227. position: absolute;
  1228. top: 0;
  1229. left: 0;
  1230. right: 0;
  1231. height: 2px;
  1232. background: linear-gradient(90deg, #00d4ff, #0099ff, #00d4ff);
  1233. opacity: 0.8;
  1234. }
  1235. .text-container .title {
  1236. display: block;
  1237. color: #FFD700;
  1238. font-weight: bold;
  1239. margin-bottom: 10px;
  1240. font-size: 22px;
  1241. }
  1242. .text-container .content {
  1243. display: block;
  1244. color: white;
  1245. font-size: 20px;
  1246. }
  1247. .class07 {
  1248. background-image: url("@/assets/img/AiEmotion/bk03.png");
  1249. /* 使用导入的背景图片 */
  1250. background-size: cover;
  1251. /* 确保背景图片完整显示 */
  1252. background-repeat: no-repeat;
  1253. /* 防止背景图片重复 */
  1254. width: 95%;
  1255. /* 设置容器宽度 */
  1256. height: auto;
  1257. /* 高度根据内容动态变化 */
  1258. min-height: 70rem;
  1259. /* 设置最小高度,确保图片显示 */
  1260. margin: 0 auto;
  1261. }
  1262. .class0700 {
  1263. margin: 0 auto;
  1264. width: fit-content;
  1265. margin-top: 2%;
  1266. margin-bottom: 1%;
  1267. }
  1268. .class0702 {
  1269. margin: 0 auto;
  1270. width: fit-content;
  1271. margin-top: 2%;
  1272. margin-bottom: 1%;
  1273. }
  1274. .class0402 {
  1275. width: 80%;
  1276. margin: 0 auto;
  1277. }
  1278. .class0603 {
  1279. min-width: 100%;
  1280. margin-top: 3%;
  1281. }
  1282. .class0502 {
  1283. padding-top: 7%;
  1284. display: flex;
  1285. flex-direction: column;
  1286. /* 竖向排列元素 */
  1287. gap: 1rem;
  1288. margin-left: 2rem;
  1289. }
  1290. .class0601 {
  1291. padding-top: 5rem;
  1292. display: flex;
  1293. flex-direction: column;
  1294. /* 竖向排列元素 */
  1295. gap: 1rem;
  1296. margin-left: 2rem;
  1297. }
  1298. .img03 {
  1299. width: 10%;
  1300. height: auto;
  1301. margin-left: 43%;
  1302. }
  1303. .img04 {
  1304. width: 10%;
  1305. height: auto;
  1306. margin-left: 43%;
  1307. }
  1308. .class0701 {
  1309. margin: 0 auto;
  1310. width: fit-content;
  1311. }
  1312. .class0501 {
  1313. margin: 0 auto;
  1314. width: fit-content;
  1315. margin-top: 2%;
  1316. margin-bottom: 1%;
  1317. }
  1318. .class0403 {
  1319. margin: 0 auto;
  1320. width: fit-content;
  1321. margin-top: 2%;
  1322. margin-bottom: 1%;
  1323. }
  1324. .class0301 {
  1325. margin: 0 auto;
  1326. width: fit-content;
  1327. margin-top: 2%;
  1328. margin-bottom: 1%;
  1329. }
  1330. .class0201 {
  1331. margin: 0 auto;
  1332. width: fit-content;
  1333. margin-bottom: 1%;
  1334. }
  1335. .class0401 {
  1336. padding-top: 5rem;
  1337. display: flex;
  1338. flex-direction: column;
  1339. /* 竖向排列元素 */
  1340. gap: 1rem;
  1341. margin-left: 2rem;
  1342. }
  1343. .img02 {
  1344. width: 10%;
  1345. height: auto;
  1346. margin-left: 43%;
  1347. }
  1348. .title2 {
  1349. color: white;
  1350. font-size: 20px;
  1351. font-weight: bold;
  1352. margin-left: 44%;
  1353. }
  1354. .title3 {
  1355. color: white;
  1356. font-size: 20px;
  1357. font-weight: bold;
  1358. margin-left: 43%;
  1359. }
  1360. .title4 {
  1361. color: white;
  1362. font-size: 20px;
  1363. font-weight: bold;
  1364. margin-left: 41.5%;
  1365. }
  1366. .class09 {
  1367. text-align: center;
  1368. margin-top: 2%;
  1369. margin-bottom: 1%;
  1370. }
  1371. /* 为需要放大的图片添加样式 */
  1372. .scaled-img {
  1373. background-image: url('@/assets/img/AiEmotion/tree02.jpg');
  1374. background-size: 100% 100%;
  1375. background-position: center;
  1376. background-repeat: no-repeat;
  1377. width: 70%;
  1378. height: 400px;
  1379. min-height: 400px;
  1380. text-align: center;
  1381. margin: 0 auto;
  1382. margin-top: 3%;
  1383. }
  1384. .lz-img {
  1385. text-align: center;
  1386. padding-top: 70px;
  1387. }
  1388. .class08 {
  1389. background-image: url("@/assets/img/AiEmotion/bk03.png");
  1390. background-size: 100% 100%;
  1391. background-repeat: no-repeat;
  1392. width: 95%;
  1393. height: auto;
  1394. min-height: 40rem;
  1395. margin: 0 auto;
  1396. }
  1397. .class06 {
  1398. background-image: url('@/assets/img/AiEmotion/bk03.png');
  1399. /* 使用导入的背景图片 */
  1400. background-size: 100% 100%;
  1401. /* 确保背景图片完整显示 */
  1402. background-repeat: no-repeat;
  1403. /* 防止背景图片重复 */
  1404. width: 95%;
  1405. /* 设置容器宽度 */
  1406. height: auto;
  1407. /* 高度根据内容动态变化 */
  1408. min-height: 66rem;
  1409. /* 设置最小高度,确保图片显示 */
  1410. margin: 0 auto;
  1411. }
  1412. .class05 {
  1413. background-image: url("@/assets/img/AiEmotion/bk03.png");
  1414. /* 使用导入的背景图片 */
  1415. background-size: 100% 100%;
  1416. /* 确保背景图片完整显示 */
  1417. background-repeat: no-repeat;
  1418. /* 防止背景图片重复 */
  1419. width: 95%;
  1420. /* 设置容器宽度 */
  1421. height: auto;
  1422. /* 高度根据内容动态变化 */
  1423. min-height: 86rem;
  1424. /* 设置最小高度,确保图片显示 */
  1425. margin: 0 auto;
  1426. }
  1427. .class04 {
  1428. background-image: url('@/assets/img/AiEmotion/bk03.png');
  1429. /* 使用导入的背景图片 */
  1430. background-size: 100% 100%;
  1431. /* 确保背景图片完整显示 */
  1432. background-repeat: no-repeat;
  1433. /* 防止背景图片重复 */
  1434. width: 95%;
  1435. /* 设置容器宽度 */
  1436. height: auto;
  1437. /* 高度根据内容动态变化 */
  1438. min-height: 75rem;
  1439. /* 设置最小高度,确保图片显示 */
  1440. margin: 0 auto;
  1441. }
  1442. .class03 {
  1443. background-image: url('@/assets/img/AiEmotion/bk03.png');
  1444. /* 使用导入的背景图片 */
  1445. background-size: 100% 100%;
  1446. /* 确保背景图片完整显示 */
  1447. background-repeat: no-repeat;
  1448. /* 防止背景图片重复 */
  1449. width: 100%;
  1450. /* 设置容器宽度 */
  1451. height: auto;
  1452. /* 高度根据内容动态变化 */
  1453. min-height: 70rem;
  1454. /* 设置最小高度,确保图片显示 */
  1455. margin: 0 auto;
  1456. /* display: flex;
  1457. flex-direction:row;
  1458. align-items: center;
  1459. padding: 1rem;
  1460. gap: 1rem; */
  1461. }
  1462. .class00 {
  1463. background-size: 100% 100%;
  1464. /* 确保背景图片完整显示 */
  1465. background-repeat: no-repeat;
  1466. /* 防止背景图片重复 */
  1467. width: 95%;
  1468. /* 设置容器宽度 */
  1469. height: auto;
  1470. /* 高度根据内容动态变化 */
  1471. min-height: 55rem;
  1472. /* 设置最小高度,确保图片显示 */
  1473. margin: 0 auto;
  1474. }
  1475. .content1 {
  1476. display: flex;
  1477. flex-direction: column;
  1478. /* 竖向排列元素 */
  1479. gap: 1rem;
  1480. margin-left: 10%;
  1481. }
  1482. .title1 {
  1483. color: white;
  1484. font-size: 30px;
  1485. font-weight: bold;
  1486. margin-left: 0%;
  1487. }
  1488. .img01 {
  1489. width: 10%;
  1490. height: auto;
  1491. }
  1492. .div00 {
  1493. display: flex;
  1494. flex-direction: column;
  1495. /* 竖向排列元素 */
  1496. margin-left: 37%;
  1497. gap: 1rem;
  1498. margin-top: -12%;
  1499. width: 100%;
  1500. height: auto;
  1501. }
  1502. .span02 {
  1503. font-size: 1.5rem;
  1504. color: white;
  1505. float: right;
  1506. margin-top: -2%;
  1507. }
  1508. .span01 {
  1509. background-image: url('@/assets/img/AiEmotion/bk01.png');
  1510. /* 使用导入的背景图片 */
  1511. background-size: 100% 100%;
  1512. /* 背景图片覆盖整个容器 */
  1513. background-repeat: no-repeat;
  1514. /* 防止背景图片重复 */
  1515. /* display: inline-block; */
  1516. /* 确保容器是块级元素 */
  1517. padding: 10px;
  1518. /* 添加内边距以显示内容 */
  1519. color: #fff;
  1520. /* 设置文字颜色以确保可读性 */
  1521. font-size: 1.5rem;
  1522. /* 增加字体大小以便更清晰显示股票名称 */
  1523. text-align: center;
  1524. /* transform: translate(-50%, -50%); */
  1525. margin-left: 0;
  1526. width: 30%;
  1527. height: auto;
  1528. }
  1529. .class01 {
  1530. width: 100%;
  1531. /* 固定容器宽度 */
  1532. min-height: 100px;
  1533. /* 设置最小高度,确保初始显示 */
  1534. height: auto;
  1535. /* 高度根据内容动态变化 */
  1536. /* 添加内边距,确保内容与边界有间距 */
  1537. box-sizing: border-box;
  1538. /* 包括内边距在宽度和高度计算中 */
  1539. background-color: #02107d;
  1540. margin: 0 auto;
  1541. /* 居中容器 */
  1542. margin-bottom: 10rem;
  1543. }
  1544. .ai-emotion-container {
  1545. display: flex;
  1546. flex-direction: column;
  1547. align-items: center;
  1548. justify-content: center;
  1549. padding: 20px;
  1550. position: relative;
  1551. }
  1552. .user-input-display {
  1553. margin-top: 20px;
  1554. display: flex;
  1555. flex-direction: column;
  1556. width: 100%;
  1557. }
  1558. .message-container {
  1559. display: flex;
  1560. margin-bottom: 10px;
  1561. }
  1562. .user-message {
  1563. background-color: #007bff;
  1564. color: #fff;
  1565. padding: 10px 15px;
  1566. border-radius: 15px;
  1567. max-width: 60%;
  1568. text-align: left;
  1569. box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
  1570. align-self: flex-end;
  1571. }
  1572. .ai-message {
  1573. background-color: #f1f1f1;
  1574. color: #333;
  1575. padding: 10px 15px;
  1576. border-radius: 15px;
  1577. max-width: 60%;
  1578. text-align: left;
  1579. box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
  1580. align-self: flex-start;
  1581. }
  1582. .input-container {
  1583. display: flex;
  1584. align-items: center;
  1585. gap: 10px;
  1586. }
  1587. .fixed-bottom {
  1588. position: fixed;
  1589. bottom: 100px;
  1590. left: 0;
  1591. width: 100%;
  1592. background-color: #f8f9fa;
  1593. padding: 10px 20px;
  1594. box-shadow: 0 -2px 5px rgba(0, 0, 0, 0.1);
  1595. }
  1596. .input-box {
  1597. padding: 10px;
  1598. font-size: 16px;
  1599. border: 1px solid #ccc;
  1600. border-radius: 5px;
  1601. width: calc(100% - 120px);
  1602. }
  1603. .send-button {
  1604. padding: 10px 20px;
  1605. font-size: 16px;
  1606. color: #fff;
  1607. background-color: #007bff;
  1608. border: none;
  1609. border-radius: 5px;
  1610. cursor: pointer;
  1611. }
  1612. .send-button:hover {
  1613. background-color: #0056b3;
  1614. }
  1615. /* 手机端适配样式 */
  1616. @media only screen and (max-width: 768px) {
  1617. .container {
  1618. padding-top: 2%;
  1619. }
  1620. .title4 {
  1621. color: white;
  1622. font-size: 20px;
  1623. font-weight: bold;
  1624. margin-left: 28%;
  1625. }
  1626. .class0603 {
  1627. min-width: 100%;
  1628. margin-top: 25%;
  1629. }
  1630. .scaled-img {
  1631. background-image: url('@/assets/img/AiEmotion/tree02.jpg');
  1632. background-size: 100% 100%;
  1633. background-position: center;
  1634. background-repeat: no-repeat;
  1635. text-align: center;
  1636. width: 95%;
  1637. margin-top: 6%;
  1638. height: 200px;
  1639. min-height: 200px;
  1640. }
  1641. .title3 {
  1642. color: white;
  1643. font-size: 20px;
  1644. font-weight: bold;
  1645. margin-left: 30%;
  1646. }
  1647. .title2 {
  1648. color: white;
  1649. font-size: 20px;
  1650. font-weight: bold;
  1651. margin-left: 30%;
  1652. }
  1653. /* 图片样式 */
  1654. .golden-wheel img {
  1655. width: 100%;
  1656. }
  1657. .class0201 img {
  1658. width: 100%;
  1659. }
  1660. .class0301 img {
  1661. width: 100%;
  1662. margin: 10px 10px;
  1663. }
  1664. .class0403 img {
  1665. width: 100%;
  1666. margin: 10px 10px;
  1667. }
  1668. .class0501 img {
  1669. width: 100%;
  1670. margin: 10px 10px;
  1671. }
  1672. .class0702 img {
  1673. width: 100%;
  1674. margin: 10px 10px;
  1675. }
  1676. .class0700 img {
  1677. width: 100%;
  1678. margin: 10px 10px;
  1679. }
  1680. .scaled-img img {
  1681. width: 30%;
  1682. height: auto;
  1683. }
  1684. .class09 img {
  1685. width: 100%;
  1686. margin: 10px 10px;
  1687. }
  1688. .img01 {
  1689. height: auto;
  1690. margin-left: 7%;
  1691. width: 25%;
  1692. margin-top: 10px;
  1693. }
  1694. .title1 {
  1695. font-size: 20px;
  1696. margin-left: 5%;
  1697. }
  1698. .class02 .span02 {
  1699. font-size: 14px;
  1700. color: white;
  1701. float: right;
  1702. margin-top: -6%;
  1703. }
  1704. .class03 {
  1705. background-image: url('@/assets/img/AiEmotion/bk03.png');
  1706. background-size: 100% 100%;
  1707. background-repeat: no-repeat;
  1708. width: 100%;
  1709. /* margin-left: -45px; */
  1710. height: auto;
  1711. }
  1712. .class03 .class003 {
  1713. padding-top: 3rem;
  1714. padding-left: 0rem;
  1715. }
  1716. .class01 {
  1717. min-height: 100px;
  1718. height: auto;
  1719. box-sizing: border-box;
  1720. background-color: #02107d;
  1721. margin-bottom: 10rem;
  1722. }
  1723. .class04 {
  1724. width: 100%;
  1725. height: auto;
  1726. min-height: 38rem;
  1727. /* min-height: 51rem; */
  1728. /* margin-left: -39px; */
  1729. /* min-height: 38rem; */
  1730. }
  1731. .class02 .container img {
  1732. width: 68%;
  1733. height: auto;
  1734. /* margin-top: 5%; */
  1735. margin-left: 0%;
  1736. }
  1737. .img02 {
  1738. width: 25%;
  1739. height: auto;
  1740. margin-left: 32%;
  1741. }
  1742. .class0401 {
  1743. padding-top: 3rem;
  1744. }
  1745. .img03,
  1746. .img04 {
  1747. width: 25%;
  1748. height: auto;
  1749. margin-left: 33%;
  1750. }
  1751. .text-container p {
  1752. font-size: 16px;
  1753. }
  1754. .lz-img {
  1755. margin-bottom: 0;
  1756. padding-top: 0;
  1757. img {
  1758. width: 30%;
  1759. height: auto;
  1760. margin-top: 5%;
  1761. }
  1762. }
  1763. .class08 {
  1764. background-size: 100% 100%;
  1765. background-repeat: no-repeat;
  1766. width: 100%;
  1767. height: auto;
  1768. min-height: 20rem;
  1769. margin: 0 auto;
  1770. }
  1771. .bk-image {
  1772. .conclusion-container {
  1773. padding: 15px;
  1774. border-radius: 8px;
  1775. margin: 8px;
  1776. .conclusion-item {
  1777. margin-bottom: 15px;
  1778. &:last-child {
  1779. margin-bottom: 0;
  1780. }
  1781. .conclusion-title {
  1782. color: #FFD700;
  1783. font-size: 16px;
  1784. font-weight: bold;
  1785. margin: 0 0 8px 0;
  1786. text-align: center;
  1787. }
  1788. .conclusion-text {
  1789. color: #ffffff;
  1790. font-size: 14px;
  1791. line-height: 1.5;
  1792. margin: 0 0 6px 0;
  1793. text-align: left;
  1794. word-wrap: break-word;
  1795. &:last-child {
  1796. margin-bottom: 0;
  1797. }
  1798. }
  1799. }
  1800. }
  1801. .conclusion-placeholder {
  1802. padding: 15px;
  1803. text-align: center;
  1804. p {
  1805. color: #999999;
  1806. font-size: 12px;
  1807. margin: 0;
  1808. }
  1809. }
  1810. }
  1811. .bk-image {
  1812. background-size: 100% 100%;
  1813. background-repeat: no-repeat;
  1814. width: 100%;
  1815. height: auto;
  1816. margin: 0 auto;
  1817. margin-top: 0px;
  1818. margin-left: 0;
  1819. .conclusion-container {
  1820. padding: 20px;
  1821. border-radius: 15px;
  1822. margin: 20px;
  1823. background: linear-gradient(135deg, rgba(0, 212, 255, 0.15) 0%, rgba(0, 100, 200, 0.15) 100%);
  1824. border: 2px solid rgba(0, 212, 255, 0.4);
  1825. box-shadow: 0 8px 25px rgba(0, 212, 255, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.1);
  1826. backdrop-filter: blur(15px);
  1827. .conclusion-item {
  1828. margin-bottom: 20px;
  1829. padding: 20px;
  1830. border-radius: 12px;
  1831. background: linear-gradient(135deg, rgba(0, 212, 255, 0.2) 0%, rgba(0, 150, 255, 0.1) 100%);
  1832. border: 1px solid rgba(0, 212, 255, 0.5);
  1833. border-left: 5px solid #00d4ff;
  1834. box-shadow: 0 4px 15px rgba(0, 212, 255, 0.2), inset 0 1px 0 rgba(255, 255, 255, 0.1);
  1835. transition: all 0.3s ease;
  1836. position: relative;
  1837. overflow: hidden;
  1838. &::before {
  1839. content: '';
  1840. position: absolute;
  1841. top: 0;
  1842. left: 0;
  1843. right: 0;
  1844. height: 2px;
  1845. background: linear-gradient(90deg, #00d4ff, #0099ff, #00d4ff);
  1846. opacity: 0.8;
  1847. }
  1848. &:last-child {
  1849. margin-bottom: 0;
  1850. }
  1851. .conclusion-title {
  1852. color: #FFD700;
  1853. font-size: 22px;
  1854. font-weight: bold;
  1855. margin: 0 0 15px 0;
  1856. text-align: center;
  1857. text-shadow: 0 2px 8px rgba(0, 212, 255, 0.5), 0 0 20px rgba(0, 212, 255, 0.3);
  1858. letter-spacing: 2px;
  1859. position: relative;
  1860. &::after {
  1861. content: '';
  1862. position: absolute;
  1863. bottom: -5px;
  1864. left: 50%;
  1865. transform: translateX(-50%);
  1866. width: 60px;
  1867. height: 2px;
  1868. background: linear-gradient(90deg, transparent, #00d4ff, transparent);
  1869. }
  1870. }
  1871. .conclusion-text {
  1872. color: #ffffff;
  1873. font-size: 20px;
  1874. line-height: 1.8;
  1875. margin: 0 0 12px 0;
  1876. text-align: left;
  1877. word-wrap: break-word;
  1878. text-shadow: 0 1px 3px rgba(0, 0, 0, 0.7);
  1879. padding-left: 15px;
  1880. position: relative;
  1881. &::before {
  1882. content: '▶';
  1883. position: absolute;
  1884. left: 0;
  1885. top: 0;
  1886. color: #00d4ff;
  1887. font-size: 12px;
  1888. opacity: 0.7;
  1889. }
  1890. &:last-child {
  1891. margin-bottom: 0;
  1892. }
  1893. }
  1894. }
  1895. }
  1896. .conclusion-placeholder {
  1897. padding: 30px;
  1898. text-align: center;
  1899. border-radius: 12px;
  1900. background: rgba(255, 255, 255, 0.05);
  1901. border: 1px dashed rgba(153, 153, 153, 0.3);
  1902. p {
  1903. color: #999999;
  1904. font-size: 16px;
  1905. margin: 0;
  1906. font-style: italic;
  1907. }
  1908. }
  1909. .disclaimer-item {
  1910. margin-top: 30px;
  1911. padding: 20px;
  1912. border-top: 1px solid rgba(153, 153, 153, 0.2);
  1913. text-align: center;
  1914. .disclaimer-text {
  1915. color: #999999;
  1916. font-size: 14px;
  1917. margin: 0;
  1918. font-style: italic;
  1919. opacity: 0.8;
  1920. letter-spacing: 1px;
  1921. }
  1922. }
  1923. }
  1924. .class05 {
  1925. background-size: 100% 100%;
  1926. background-repeat: no-repeat;
  1927. width: 100%;
  1928. height: auto;
  1929. min-height: 48rem;
  1930. }
  1931. .class06 {
  1932. background-size: 100% 100%;
  1933. background-repeat: no-repeat;
  1934. width: 100%;
  1935. height: auto;
  1936. min-height: 35rem;
  1937. margin: 0 auto;
  1938. }
  1939. .text-container {
  1940. position: relative;
  1941. top: 0;
  1942. left: 0;
  1943. color: white;
  1944. text-align: left;
  1945. padding: 5%;
  1946. }
  1947. .div00 {
  1948. display: flex;
  1949. flex-direction: column;
  1950. margin-left: 5rem;
  1951. gap: 0;
  1952. margin-top: -6rem;
  1953. width: 100%;
  1954. height: auto;
  1955. }
  1956. .class003 .div01 {
  1957. background-repeat: no-repeat;
  1958. background-size: 100% 100%;
  1959. width: 35%;
  1960. min-height: 25px;
  1961. float: left;
  1962. margin-left: 100px;
  1963. text-align: center;
  1964. margin-top: 10px;
  1965. font-size: 10px;
  1966. color: white;
  1967. }
  1968. .class003 .div02 {
  1969. background-repeat: no-repeat;
  1970. background-size: 100% 100%;
  1971. width: 35%;
  1972. min-height: 25px;
  1973. float: left;
  1974. margin-left: 100px;
  1975. text-align: center;
  1976. margin-top: 10px;
  1977. font-size: 10px;
  1978. color: white;
  1979. }
  1980. .span01 {
  1981. /* 使用导入的背景图片 */
  1982. background-image: url('@/assets/img/AiEmotion/bk01.png');
  1983. background-size: 100% 100%;
  1984. /* 背景图片覆盖整个容器 */
  1985. background-repeat: no-repeat;
  1986. /* 防止背景图片重复 */
  1987. display: inline-block;
  1988. /* 确保容器是块级元素 */
  1989. padding: 10px;
  1990. /* 添加内边距以显示内容 */
  1991. color: #fff;
  1992. /* 设置文字颜色以确保可读性 */
  1993. font-size: 14px;
  1994. /* 增加字体大小以便更清晰显示股票名称 */
  1995. text-align: center;
  1996. width: 50%;
  1997. }
  1998. .class0502,
  1999. .class0601 {
  2000. padding-top: 10%;
  2001. }
  2002. }
  2003. /* 加载提示样式 */
  2004. .loading-container {
  2005. display: flex;
  2006. justify-content: center;
  2007. align-items: center;
  2008. min-height: 60vh;
  2009. padding: 40px 20px;
  2010. }
  2011. .loading-content {
  2012. text-align: center;
  2013. background: linear-gradient(135deg, rgba(0, 212, 255, 0.15) 0%, rgba(0, 100, 200, 0.15) 100%);
  2014. border: 2px solid rgba(0, 212, 255, 0.4);
  2015. border-radius: 20px;
  2016. padding: 40px;
  2017. backdrop-filter: blur(15px);
  2018. box-shadow: 0 8px 25px rgba(0, 212, 255, 0.3);
  2019. }
  2020. .loading-spinner {
  2021. width: 60px;
  2022. height: 60px;
  2023. border: 4px solid rgba(0, 212, 255, 0.3);
  2024. border-top: 4px solid #00d4ff;
  2025. border-radius: 50%;
  2026. animation: spin 1s linear infinite;
  2027. margin: 0 auto 20px;
  2028. }
  2029. @keyframes spin {
  2030. 0% {
  2031. transform: rotate(0deg);
  2032. }
  2033. 100% {
  2034. transform: rotate(360deg);
  2035. }
  2036. }
  2037. .loading-text {
  2038. color: #00d4ff;
  2039. font-size: 18px;
  2040. font-weight: bold;
  2041. text-shadow: 0 2px 8px rgba(0, 212, 255, 0.5);
  2042. letter-spacing: 1px;
  2043. }
  2044. </style>