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.

1367 lines
35 KiB

3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
  1. <template>
  2. <div
  3. v-if="!isMobile"
  4. class="history-record-container"
  5. :class="{
  6. collapsed: !isCollapsed,
  7. }"
  8. >
  9. <!-- 收起状态的展开按钮和图标 -->
  10. <div v-if="isCollapsed" class="collapsed-container">
  11. <img
  12. class="collapsed-icon"
  13. src="https://d31zlh4on95l9h.cloudfront.net/images/985a70c1fb0296e4b2b9e05d2dfdc22c.png"
  14. alt="icon"
  15. />
  16. <img
  17. class="god-icon"
  18. src="https://d31zlh4on95l9h.cloudfront.net/images/759eabdc1a040be4583a358c0a7a6bf2.png"
  19. alt="icon"
  20. title="AI小财神"
  21. />
  22. <img
  23. class="collapsed-toggle-btn"
  24. @click="openHistory"
  25. src="https://d31zlh4on95l9h.cloudfront.net/images/cba6816917b00287565f3bed84ced7dd.png"
  26. alt="icon"
  27. title="打开边栏"
  28. />
  29. </div>
  30. <div v-if="isCollapsed" class="collapsed-bottom-container">
  31. <div
  32. class="collapsed-bottom-btn"
  33. @click="handleAnnouncementClick"
  34. title="公告"
  35. >
  36. <img
  37. class="collapsed-bottom-announcement"
  38. src="https://d31zlh4on95l9h.cloudfront.net/images/3f7a1cb3dd6c5f9d4163fab4d26b4f4f.png"
  39. alt="icon"
  40. />
  41. </div>
  42. <div
  43. class="collapsed-bottom-btn"
  44. @click="handleFeedbackClick"
  45. title="用户反馈"
  46. >
  47. <img
  48. class="collapsed-bottom-feedback"
  49. src="https://d31zlh4on95l9h.cloudfront.net/images/90c933dba4b36edae3775f9fd34a2e6c.png"
  50. alt="icon"
  51. />
  52. </div>
  53. </div>
  54. <!-- 历史记录内容 -->
  55. <div class="history-content" v-if="!isCollapsed">
  56. <div class="head-container">
  57. <!-- 标题 -->
  58. <div class="history-actions">
  59. <img
  60. src="/src/assets/img/homePage/logo.png"
  61. alt="Logo"
  62. class="logo-img"
  63. />
  64. </div>
  65. <!-- 折叠/展开按钮 -->
  66. <img
  67. class="toggle-btn"
  68. @click="closeHistory"
  69. src="https://d31zlh4on95l9h.cloudfront.net/images/cba6816917b00287565f3bed84ced7dd.png"
  70. alt="icon"
  71. title="收起边栏"
  72. />
  73. </div>
  74. <!-- 历史记录列表 -->
  75. <div class="history-list">
  76. <!-- 空状态 -->
  77. <div v-if="historyRecords.length === 0" class="empty-state">
  78. <div class="empty-icon">
  79. <el-icon class="documentDelete"><DocumentDelete /></el-icon>
  80. </div>
  81. <p class="empty-text">暂无历史记录</p>
  82. </div>
  83. <div v-else v-for="history in categoryHistory" :key="history.name">
  84. <div class="categoryName" v-if="history.list.length != 0">
  85. {{ history.name }}
  86. </div>
  87. <div
  88. v-for="record in history.list"
  89. :key="record.id"
  90. class="history-item"
  91. :class="{ active: selectedRecordId === record.id }"
  92. >
  93. <div class="record-content" @click="selectRecord(record)">
  94. <div class="record-img">
  95. <img
  96. v-if="marketList[record.stockMarket]"
  97. :src="marketList[record.stockMarket]"
  98. :alt="record.stockMarket"
  99. />
  100. <img
  101. v-else
  102. src="https://d31zlh4on95l9h.cloudfront.net/images/9a431843b182c64a05fa3c8f6772b8a4.png"
  103. alt="record.stockMarket"
  104. />
  105. </div>
  106. <div class="record-msg">
  107. <div class="record-text">
  108. <span class="stock-name">{{ record.stockName }}</span>
  109. <span class="stock-code">({{ record.stockCode }})</span>
  110. <div v-if="history.name === '置顶'">
  111. <svg
  112. t="1755227529729"
  113. class="top-icon"
  114. viewBox="320 280 380 460"
  115. version="1.1"
  116. xmlns="http://www.w3.org/2000/svg"
  117. p-id="7392"
  118. >
  119. <path
  120. d="M351.085714 292.571429h321.828572v29.257142H351.085714v-29.257142z m175.542857 125.805714l146.285715 146.285714-20.48 20.48-125.805715-125.805714V731.428571h-29.257142v-272.091428L371.565714 585.142857l-20.48-20.48 146.285715-146.285714h29.257142z"
  121. fill="#FFFFFF"
  122. p-id="7393"
  123. ></path>
  124. </svg>
  125. </div>
  126. </div>
  127. <div class="record-time">
  128. {{ moment(record.createdTime).format("YYYY-MM-DD HH:mm:ss") }}
  129. </div>
  130. </div>
  131. </div>
  132. <div class="record-actions">
  133. <el-popover
  134. class="box-item"
  135. placement="right-start"
  136. trigger="click"
  137. >
  138. <template #reference>
  139. <el-icon class="more-btn"><MoreFilled /></el-icon>
  140. </template>
  141. <div class="popover-content">
  142. <div class="popover-item">
  143. <img
  144. class="popover-icon"
  145. src="https://d31zlh4on95l9h.cloudfront.net/images/9ad3617c94955bcb76e1b11db70bb80b.png"
  146. alt=""
  147. />
  148. 数据更新时间{{ moment(record.date).format("D/M/YYYY") }}
  149. </div>
  150. <div
  151. v-if="record.isTop"
  152. class="popover-item popover-btn"
  153. @click="changeTopStatus(record.isTop, record.id)"
  154. >
  155. <img
  156. class="popover-icon"
  157. src="https://d31zlh4on95l9h.cloudfront.net/images/a458305d8275734cc96bf6cad29864bf.png"
  158. alt=""
  159. />
  160. 取消置顶
  161. </div>
  162. <div
  163. v-else
  164. class="popover-item popover-btn"
  165. @click="changeTopStatus(record.isTop, record.id)"
  166. >
  167. <img
  168. class="popover-icon"
  169. src="https://d31zlh4on95l9h.cloudfront.net/images/a458305d8275734cc96bf6cad29864bf.png"
  170. alt=""
  171. />
  172. 置顶
  173. </div>
  174. <div
  175. class="popover-item popover-btn"
  176. @click="deleteRecord(record.id)"
  177. >
  178. <img
  179. class="popover-icon"
  180. src="https://d31zlh4on95l9h.cloudfront.net/images/027718d41523375a69e9cac927601cf8.png"
  181. alt=""
  182. />
  183. 删除
  184. </div>
  185. </div>
  186. </el-popover>
  187. </div>
  188. </div>
  189. </div>
  190. </div>
  191. <div class="bottom-container">
  192. <div class="bottom-btn" @click="handleAnnouncementClick">
  193. <img
  194. class="bottom-announcement"
  195. src="https://d31zlh4on95l9h.cloudfront.net/images/3f7a1cb3dd6c5f9d4163fab4d26b4f4f.png"
  196. alt="icon"
  197. />
  198. <div style="color: aliceblue">公告</div>
  199. </div>
  200. <div class="bottom-btn" @click="handleFeedbackClick">
  201. <img
  202. class="bottom-feedback"
  203. src="https://d31zlh4on95l9h.cloudfront.net/images/90c933dba4b36edae3775f9fd34a2e6c.png"
  204. alt="icon"
  205. />
  206. <div style="color: aliceblue">用户反馈</div>
  207. </div>
  208. </div>
  209. </div>
  210. </div>
  211. <div
  212. v-else
  213. class="mobile-history-record-container"
  214. :class="{
  215. mobileCollapsed: !isCollapsed,
  216. }"
  217. >
  218. <!-- 历史记录内容 -->
  219. <div class="history-content" v-if="!isCollapsed">
  220. <div class="mobile-head-container">
  221. <!-- 折叠/展开按钮 -->
  222. <img
  223. class="mobile-toggle-btn"
  224. @click="closeHistory"
  225. src="https://d31zlh4on95l9h.cloudfront.net/images/37fe3d79a8a700f6c674c9f0e7af066b.png"
  226. alt="icon"
  227. />
  228. <!-- 标题 -->
  229. <div class="mobile-history-actions">
  230. <img
  231. src="/src/assets/img/homePage/logo.png"
  232. alt="Logo"
  233. class="logo-img"
  234. />
  235. </div>
  236. </div>
  237. <!-- 历史记录列表 -->
  238. <div class="history-list">
  239. <!-- 空状态 -->
  240. <div v-if="historyRecords.length === 0" class="empty-state">
  241. <div class="empty-icon">
  242. <el-icon class="documentDelete"><DocumentDelete /></el-icon>
  243. </div>
  244. <p class="empty-text">暂无历史记录</p>
  245. </div>
  246. <div v-else v-for="history in categoryHistory" :key="history.name">
  247. <div class="categoryName" v-if="history.list.length != 0">
  248. {{ history.name }}
  249. </div>
  250. <div
  251. v-for="record in history.list"
  252. :key="record.id"
  253. class="history-item"
  254. :class="{ active: selectedRecordId === record.id }"
  255. >
  256. <div class="record-content" @click="selectRecord(record)">
  257. <div class="record-img">
  258. <img
  259. v-if="marketList[record.stockMarket]"
  260. :src="marketList[record.stockMarket]"
  261. :alt="record.stockMarket"
  262. />
  263. <img
  264. v-else
  265. src="https://d31zlh4on95l9h.cloudfront.net/images/9a431843b182c64a05fa3c8f6772b8a4.png"
  266. alt="record.stockMarket"
  267. />
  268. </div>
  269. <div class="record-msg">
  270. <div class="record-text">
  271. <span class="stock-name">{{ record.stockName }}</span>
  272. <span class="stock-code">({{ record.stockCode }})</span>
  273. <div v-if="history.name === '置顶'">
  274. <svg
  275. t="1755227529729"
  276. class="top-icon"
  277. viewBox="320 280 380 460"
  278. version="1.1"
  279. xmlns="http://www.w3.org/2000/svg"
  280. p-id="7392"
  281. >
  282. <path
  283. d="M351.085714 292.571429h321.828572v29.257142H351.085714v-29.257142z m175.542857 125.805714l146.285715 146.285714-20.48 20.48-125.805715-125.805714V731.428571h-29.257142v-272.091428L371.565714 585.142857l-20.48-20.48 146.285715-146.285714h29.257142z"
  284. fill="#FFFFFF"
  285. p-id="7393"
  286. ></path>
  287. </svg>
  288. </div>
  289. </div>
  290. <div class="record-time">
  291. {{ moment(record.createdTime).format("YYYY-MM-DD HH:mm:ss") }}
  292. </div>
  293. </div>
  294. </div>
  295. <div class="record-actions">
  296. <el-popover
  297. class="box-item"
  298. placement="right-start"
  299. trigger="click"
  300. >
  301. <template #reference>
  302. <el-icon class="more-btn"><MoreFilled /></el-icon>
  303. </template>
  304. <div class="popover-content">
  305. <div class="popover-item">
  306. <img
  307. class="popover-icon"
  308. src="https://d31zlh4on95l9h.cloudfront.net/images/9ad3617c94955bcb76e1b11db70bb80b.png"
  309. alt=""
  310. />
  311. 数据更新时间{{ moment(record.date).format("D/M/YYYY") }}
  312. </div>
  313. <div
  314. v-if="record.isTop"
  315. class="popover-item popover-btn"
  316. @click="changeTopStatus(record.isTop, record.id)"
  317. >
  318. <img
  319. class="popover-icon"
  320. src="https://d31zlh4on95l9h.cloudfront.net/images/a458305d8275734cc96bf6cad29864bf.png"
  321. alt=""
  322. />
  323. 取消置顶
  324. </div>
  325. <div
  326. v-else
  327. class="popover-item popover-btn"
  328. @click="changeTopStatus(record.isTop, record.id)"
  329. >
  330. <img
  331. class="popover-icon"
  332. src="https://d31zlh4on95l9h.cloudfront.net/images/a458305d8275734cc96bf6cad29864bf.png"
  333. alt=""
  334. />
  335. 置顶
  336. </div>
  337. <div
  338. class="popover-item popover-btn"
  339. @click="deleteRecord(record.id)"
  340. >
  341. <img
  342. class="popover-icon"
  343. src="https://d31zlh4on95l9h.cloudfront.net/images/027718d41523375a69e9cac927601cf8.png"
  344. alt=""
  345. />
  346. 删除
  347. </div>
  348. </div>
  349. </el-popover>
  350. </div>
  351. </div>
  352. </div>
  353. </div>
  354. <div class="mobile-bottom-container">
  355. <div
  356. class="mobile-bottom-btn"
  357. @click="handleAnnouncementClick"
  358. title="公告"
  359. >
  360. <img
  361. class="mobile-bottom-announcement"
  362. src="https://d31zlh4on95l9h.cloudfront.net/images/3f7a1cb3dd6c5f9d4163fab4d26b4f4f.png"
  363. alt="icon"
  364. />
  365. <div class="mobile-bottom-text">公告</div>
  366. </div>
  367. <div
  368. class="mobile-bottom-btn"
  369. @click="handleFeedbackClick"
  370. title="用户反馈"
  371. >
  372. <img
  373. class="mobile-bottom-feedback"
  374. src="https://d31zlh4on95l9h.cloudfront.net/images/90c933dba4b36edae3775f9fd34a2e6c.png"
  375. alt="icon"
  376. />
  377. <div class="mobile-bottom-text">用户反馈</div>
  378. </div>
  379. </div>
  380. </div>
  381. </div>
  382. <el-dialog
  383. v-model="deleteDialogVisible"
  384. title="永久删除记录"
  385. :width="computedDialogWidth"
  386. align-center
  387. >
  388. <span>删除后该记录将不可恢复确认删除吗</span>
  389. <template #footer>
  390. <div class="dialog-footer">
  391. <el-button @click="closeDeleteDialog()">取消</el-button>
  392. <el-button type="primary" @click="deleteRecordConfirm()">
  393. 删除
  394. </el-button>
  395. </div>
  396. </template>
  397. </el-dialog>
  398. </template>
  399. <script setup>
  400. import { ref, computed, onMounted, watch } from "vue";
  401. import {
  402. getHistoryListAPI,
  403. changeTopAPI,
  404. deleteRecordAPI,
  405. clickRecordAPI,
  406. } from "../../api/AIxiaocaishen";
  407. import moment from "moment";
  408. import { ElMessage } from "element-plus";
  409. import { useChatStore } from "../../store/chat";
  410. const chatStore = useChatStore();
  411. import { useDataStore } from "@/store/dataList.js";
  412. const dataStore = useDataStore();
  413. import { useRouter } from "vue-router";
  414. const router = useRouter();
  415. // Props
  416. const props = defineProps({
  417. currentType: {
  418. type: String,
  419. default: "AIchat", // 'AIchat' 或 'AiEmotion'
  420. },
  421. });
  422. const isMobile = ref(null);
  423. // Emits
  424. const emit = defineEmits([
  425. "selectRecord",
  426. "recordAdded",
  427. "startNewChat",
  428. "showAnnouncement",
  429. "showFeedback",
  430. ]);
  431. // 响应式数据
  432. const marketList = ref({
  433. cn: "https://d31zlh4on95l9h.cloudfront.net/images/c685daa929d80a03c26841dfa783cc3c.png",
  434. usa: "https://d31zlh4on95l9h.cloudfront.net/images/bccbc3058f327f72aa158fa0852dce19.png",
  435. hk: "https://d31zlh4on95l9h.cloudfront.net/images/ab050afe6867e9f961561f665ed12d10.png",
  436. sg: "https://d31zlh4on95l9h.cloudfront.net/images/90c5ce1edef2235a100e3ee0ad3cac92.png",
  437. vi: "https://d31zlh4on95l9h.cloudfront.net/images/59404c85889abd57dfd15040099edc1a.png",
  438. th: "https://d31zlh4on95l9h.cloudfront.net/images/31f5433264cf1f84cf550995fa16d86e.png",
  439. can: "https://d31zlh4on95l9h.cloudfront.net/images/26382451bfa08e6a419a2190b799dae5.png",
  440. my: "https://d31zlh4on95l9h.cloudfront.net/images/7efa8487a1317ed17eacc77b58e0a26d.png",
  441. });
  442. const isCollapsed = ref(true);
  443. const selectedRecordId = ref(null);
  444. const delObj = ref({});
  445. const deleteDialogVisible = ref(false);
  446. const dialogWidth = ref("500px"); // 对话框动态宽度
  447. // 计算属性:根据移动端状态动态设置对话框宽度
  448. const computedDialogWidth = computed(() => {
  449. if (isMobile.value) {
  450. return "70%"; // 移动端使用百分比宽度
  451. }
  452. return dialogWidth.value; // 桌面端使用固定宽度
  453. });
  454. const openDeleteDialog = () => {
  455. deleteDialogVisible.value = true;
  456. };
  457. const closeDeleteDialog = () => {
  458. delObj.value = {};
  459. deleteDialogVisible.value = false;
  460. };
  461. const historyRecords = ref([]);
  462. const categoryHistory = ref([]);
  463. let chatFirstFlag = true;
  464. let emotionTirstFlag = true;
  465. // 添加缓存机制
  466. let cachedCategoryHistory = null;
  467. let lastDataHash = null;
  468. const getHistoryList = async (params) => {
  469. try {
  470. const result = await getHistoryListAPI(params);
  471. historyRecords.value = result.data;
  472. // 检查数据是否有变化,使用简单的哈希值比较
  473. const currentDataHash = JSON.stringify(
  474. result.data.map((r) => ({
  475. id: r.id,
  476. isTop: r.isTop,
  477. createdTime: r.createdTime,
  478. }))
  479. );
  480. if (cachedCategoryHistory && lastDataHash === currentDataHash) {
  481. // 使用缓存的分类结果
  482. categoryHistory.value = cachedCategoryHistory;
  483. return;
  484. }
  485. let remainingRecords = result.data; // 复制原数组
  486. // console.log(
  487. // "params",
  488. // params,
  489. // "result",
  490. // result.data,
  491. // "chatFirstFlag",
  492. // chatFirstFlag,
  493. // "emotionTirstFlag",
  494. // emotionTirstFlag
  495. // );
  496. if (chatFirstFlag && params.model == 1 && result.data.length != 0) {
  497. if (!isMobile.value) {
  498. chatStore.aiChatCall = true;
  499. }
  500. chatFirstFlag = false;
  501. }
  502. if (emotionTirstFlag && params.model == 2 && result.data.length != 0) {
  503. if (!isMobile.value) {
  504. chatStore.aiEmotionCall = true;
  505. }
  506. emotionTirstFlag = false;
  507. }
  508. // 只在首次加载时根据chatStore状态设置折叠状态,避免搜索后自动弹出历史记录
  509. if (isMobile.value) {
  510. console.log("移动设备默认关闭");
  511. isCollapsed.value = true;
  512. } else {
  513. console.log("pc设备进行判断");
  514. if (props.currentType == "AIchat") {
  515. isCollapsed.value = !chatStore.aiChatCall;
  516. console.log("夺宝奇兵页面", isCollapsed.value);
  517. } else if (props.currentType == "AiEmotion") {
  518. isCollapsed.value = !chatStore.aiEmotionCall;
  519. console.log("情绪大模型页面", isCollapsed.value);
  520. } else if (props.currentType == "deepNine") {
  521. isCollapsed.value = !chatStore.deepNineCall;
  522. console.log("深度九大模型页面", isCollapsed.value);
  523. }
  524. }
  525. // 非首次调用时保持当前折叠状态不变
  526. // 优化:一次遍历完成所有分类,避免重复计算日期
  527. const today = moment().format("YYYY-MM-DD");
  528. const threeDaysAgo = moment().subtract(3, "days").startOf("day");
  529. const yesterday = moment().subtract(1, "days").endOf("day");
  530. const sevenDaysAgo = moment().subtract(7, "days").startOf("day");
  531. const thirtyDaysAgo = moment().subtract(30, "days").startOf("day");
  532. const topList = [];
  533. const todayList = [];
  534. const recent3DaysList = [];
  535. const recent7DaysList = [];
  536. const recent30DaysList = [];
  537. remainingRecords.forEach((record) => {
  538. if (record.isTop === 1) {
  539. topList.push(record);
  540. return;
  541. }
  542. const recordDate = moment(record.createdTime);
  543. const recordDateStr = recordDate.format("YYYY-MM-DD");
  544. if (recordDateStr === today) {
  545. todayList.push(record);
  546. } else if (
  547. recordDate.isAfter(threeDaysAgo) &&
  548. recordDate.isBefore(yesterday)
  549. ) {
  550. recent3DaysList.push(record);
  551. } else if (recordDate.isAfter(sevenDaysAgo)) {
  552. recent7DaysList.push(record);
  553. } else if (recordDate.isAfter(thirtyDaysAgo)) {
  554. recent30DaysList.push(record);
  555. }
  556. });
  557. historyRecords.value = result.data;
  558. categoryHistory.value = [
  559. {
  560. name: "置顶",
  561. list: topList,
  562. },
  563. {
  564. name: "今日",
  565. list: todayList,
  566. },
  567. {
  568. name: "近3日",
  569. list: recent3DaysList,
  570. },
  571. {
  572. name: "近7日",
  573. list: recent7DaysList,
  574. },
  575. {
  576. name: "近30日",
  577. list: recent30DaysList,
  578. },
  579. ];
  580. // 更新缓存
  581. cachedCategoryHistory = categoryHistory.value;
  582. lastDataHash = currentDataHash;
  583. // console.log("historyRecords", historyRecords.value);
  584. // console.log("categoryHistory", categoryHistory.value);
  585. } catch (e) {
  586. console.error("获取历史记录出错", e);
  587. // 确保在出错时historyRecords和categoryHistory仍然是数组
  588. historyRecords.value = [];
  589. categoryHistory.value = [];
  590. }
  591. };
  592. const changeTop = async (param) => {
  593. try {
  594. await changeTopAPI(param);
  595. } catch (e) {
  596. console.error("置顶或取消置顶失败", e);
  597. }
  598. };
  599. const changeTopStatus = async (isTop, id) => {
  600. try {
  601. // 立即关闭popover,避免闪现
  602. const popoverElement = document.querySelector(".el-popover");
  603. if (popoverElement) {
  604. popoverElement.style.display = "none";
  605. }
  606. if (isTop == 0 && categoryHistory.value[0].list.length >= 3) {
  607. console.log("超过置顶上线");
  608. ElMessage.warning("最多置顶三条内容,已达上限!");
  609. return;
  610. }
  611. await changeTop({
  612. model:
  613. props.currentType == "AIchat"
  614. ? 1
  615. : props.currentType == "AiEmotion"
  616. ? 2
  617. : 3,
  618. recordId: id,
  619. isTop: isTop == 1 ? 0 : 1,
  620. });
  621. await getHistoryList({
  622. model:
  623. props.currentType == "AIchat"
  624. ? 1
  625. : props.currentType == "AiEmotion"
  626. ? 2
  627. : 3,
  628. token: localStorage.getItem("localToken"),
  629. });
  630. } catch (error) {
  631. console.error("操作失败:", error);
  632. }
  633. };
  634. // 方法
  635. const toggleCollapse = () => {
  636. isCollapsed.value = !isCollapsed.value;
  637. // 保存折叠状态到本地存储
  638. localStorage.setItem("historyRecordCollapsed", isCollapsed.value);
  639. };
  640. const backToSelectModel = () => {
  641. router.push("/Selectmodel");
  642. };
  643. const openHistory = () => {
  644. // getHistoryList({
  645. // model: props.currentType == "AIchat" ? 1 : 2,
  646. // token: localStorage.getItem("localToken"),
  647. // });
  648. isCollapsed.value = false;
  649. if (props.currentType == "AIchat") {
  650. chatStore.aiChatCall = true;
  651. } else if (props.currentType == "AiEmotion") {
  652. chatStore.aiEmotionCall = true;
  653. } else if (props.currentType == "deepNine") {
  654. chatStore.deepNineCall = true;
  655. }
  656. };
  657. const closeHistory = () => {
  658. isCollapsed.value = true;
  659. if (props.currentType == "AIchat") {
  660. chatStore.aiChatCall = false;
  661. } else if (props.currentType == "AiEmotion") {
  662. chatStore.aiEmotionCall = false;
  663. } else if (props.currentType == "deepNine") {
  664. chatStore.deepNineCall = false;
  665. }
  666. };
  667. const historyData = ref({});
  668. const selectRecord = async (record) => {
  669. if (
  670. (props.currentType == "AIchat" || props.currentType == "deepNine") &&
  671. chatStore.firstAPICall
  672. ) {
  673. ElMessage.warning("正在获取回复中,请稍后");
  674. return;
  675. }
  676. try {
  677. selectedRecordId.value = record.id;
  678. const result = await clickRecordAPI({
  679. model:
  680. props.currentType == "AIchat"
  681. ? 1
  682. : props.currentType == "AiEmotion"
  683. ? 2
  684. : 3,
  685. parentId: record.parentId,
  686. recordId: record.id,
  687. });
  688. if (result && result.data) {
  689. if (isMobile.value) {
  690. // 如果手机,收起历史记录
  691. isCollapsed.value = true;
  692. if (props.currentType == "AIchat") {
  693. chatStore.aiChatCall = false;
  694. } else if (props.currentType == "AiEmotion") {
  695. chatStore.aiEmotionCall = false;
  696. } else if (props.currentType == "deepNine") {
  697. chatStore.deepNineCall = false;
  698. }
  699. }
  700. dataStore.isFeedback = false;
  701. historyData.value = result.data;
  702. chatStore.dbqbClickRecord = historyData.value;
  703. deepNineStore.dbqbClickRecord=historyData.value
  704. // 构造股票数据对象,保持与现有结构一致
  705. const stockData = {
  706. queryText: result.data.keyword, // 使用记录中的keyword字段作为查询文本
  707. stockInfo: {
  708. name: result.data.stockData?.stockName || record.stockName || "",
  709. code: record.stockCode || "",
  710. market: record.stockMarket || "cn",
  711. },
  712. apiData: result.data.stockData || {}, // 图表数据
  713. conclusionData: result.data.wokeFlowData?.One || {}, // 场景应用的结论和音频
  714. timestamp: record.createdTime || new Date().toISOString(), // 使用历史记录的createdTime字段
  715. };
  716. // 通过emit将数据传递给父组件
  717. emit("selectRecord", stockData);
  718. console.log("历史记录数据已发送给父组件:", stockData);
  719. } else {
  720. console.error("历史记录数据格式不正确:", result);
  721. }
  722. } catch (e) {
  723. console.error("获取历史记录数据失败", e);
  724. }
  725. };
  726. const deleteRecord = (id) => {
  727. delObj.value.id = id;
  728. // 立即关闭popover,避免闪现
  729. nextTick(() => {
  730. // 尝试多种方式关闭popover
  731. const popoverElement = document.querySelector(".el-popover");
  732. if (popoverElement) {
  733. popoverElement.style.display = "none";
  734. }
  735. // 触发body点击事件来关闭popover
  736. document.body.click();
  737. });
  738. openDeleteDialog();
  739. };
  740. const deleteRecordConfirm = async () => {
  741. try {
  742. const result = await deleteRecordAPI({
  743. model:
  744. props.currentType == "AIchat"
  745. ? 1
  746. : props.currentType == "AiEmotion"
  747. ? 2
  748. : 3,
  749. recordId: delObj.value.id,
  750. });
  751. console.log(result.msg);
  752. closeDeleteDialog();
  753. await getHistoryList({
  754. model:
  755. props.currentType == "AIchat"
  756. ? 1
  757. : props.currentType == "AiEmotion"
  758. ? 2
  759. : 3,
  760. token: localStorage.getItem("localToken"),
  761. });
  762. } catch (e) {
  763. console.error("删除失败", e);
  764. }
  765. };
  766. // 处理公告按钮点击
  767. const handleAnnouncementClick = () => {
  768. if (props.currentType == "AIchat" && chatStore.firstAPICall) {
  769. ElMessage.warning("正在获取回复中,请稍后");
  770. return;
  771. }
  772. emit("showAnnouncement");
  773. };
  774. // 处理用户反馈按钮点击
  775. const handleFeedbackClick = () => {
  776. if (props.currentType == "AIchat" && chatStore.firstAPICall) {
  777. ElMessage.warning("正在获取回复中,请稍后");
  778. return;
  779. }
  780. emit("showFeedback");
  781. };
  782. // 监听夺宝奇兵和情绪大模型的搜索记录状态
  783. watch(
  784. () => chatStore.searchRecord,
  785. (newVal) => {
  786. if (chatStore.searchRecord) {
  787. getHistoryList({
  788. model:
  789. props.currentType == "AIchat"
  790. ? 1
  791. : props.currentType == "AiEmotion"
  792. ? 2
  793. : 3,
  794. token: localStorage.getItem("localToken"),
  795. });
  796. chatStore.searchRecord = false;
  797. }
  798. }
  799. );
  800. // 监听深度九大模型的搜索记录状态
  801. import { useDeepNineStore } from "@/store/deepNine.js";
  802. const deepNineStore = useDeepNineStore();
  803. watch(
  804. () => deepNineStore.searchRecord,
  805. (newVal) => {
  806. if (deepNineStore.searchRecord) {
  807. getHistoryList({
  808. model:
  809. props.currentType == "deepNine"
  810. ? 3
  811. : props.currentType == "AIchat"
  812. ? 1
  813. : 2,
  814. token: localStorage.getItem("localToken"),
  815. });
  816. deepNineStore.searchRecord = false;
  817. }
  818. }
  819. );
  820. // 暴露方法和状态给父组件
  821. defineExpose({
  822. isCollapsed,
  823. toggleCollapse,
  824. getHistoryList,
  825. selectedRecordId,
  826. });
  827. // 生命周期
  828. onMounted(() => {
  829. const userAgent = navigator.userAgent;
  830. isMobile.value =
  831. /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
  832. userAgent
  833. );
  834. let model =
  835. props.currentType == "AIchat"
  836. ? 1
  837. : props.currentType == "AiEmotion"
  838. ? 2
  839. : 3;
  840. getHistoryList({
  841. model: model,
  842. token: localStorage.getItem("localToken"),
  843. });
  844. });
  845. </script>
  846. <style scoped>
  847. .history-record-container {
  848. min-width: 40px;
  849. width: 3%;
  850. position: fixed;
  851. left: 0;
  852. top: 0;
  853. bottom: 0;
  854. background: rgba(0, 0, 0, 0.5);
  855. border-right: 1px solid rgba(255, 255, 255, 0.1);
  856. backdrop-filter: blur(10px);
  857. z-index: 1000;
  858. transition: width 0.2s ease-out;
  859. display: flex;
  860. flex-direction: column;
  861. align-items: center;
  862. /* justify-content: center; */
  863. }
  864. .mobile-history-record-container {
  865. width: 0px;
  866. position: fixed;
  867. left: 0;
  868. top: 0;
  869. bottom: 0;
  870. background: rgba(0, 0, 0, 0.5);
  871. border-right: 1px solid rgba(255, 255, 255, 0.1);
  872. backdrop-filter: blur(10px);
  873. z-index: 1000;
  874. transition: width 0.2s ease-out;
  875. display: flex;
  876. flex-direction: column;
  877. align-items: center;
  878. }
  879. .collapsed {
  880. width: 300px;
  881. }
  882. .mobileCollapsed {
  883. /* max-width: 400px */
  884. width: 80vw;
  885. }
  886. .toggle-btn {
  887. width: 32px;
  888. height: 32px;
  889. transform: rotate(180deg);
  890. border-radius: 6px;
  891. color: white;
  892. cursor: pointer;
  893. display: flex;
  894. align-items: center;
  895. justify-content: center;
  896. transition: background-color 0.2s ease-out, border-color 0.2s ease-out;
  897. z-index: 10;
  898. }
  899. .mobile-toggle-btn {
  900. width: 10%;
  901. height: auto;
  902. border-radius: 6px;
  903. color: white;
  904. cursor: pointer;
  905. display: flex;
  906. align-items: center;
  907. justify-content: center;
  908. transition: background-color 0.2s ease-out, border-color 0.2s ease-out;
  909. z-index: 10;
  910. }
  911. .toggle-btn:hover {
  912. background: rgba(255, 255, 255, 0.2);
  913. border-color: rgba(255, 255, 255, 0.3);
  914. }
  915. .collapsed-container {
  916. width: 100%;
  917. margin-top: 60px;
  918. display: flex;
  919. flex-direction: column;
  920. align-items: center;
  921. gap: 20px;
  922. z-index: 1000;
  923. }
  924. .collapsed-icon {
  925. width: 80%;
  926. height: auto;
  927. object-fit: contain;
  928. }
  929. .collapsed-toggle-btn {
  930. width: 80%;
  931. height: auto;
  932. border-radius: 6px;
  933. display: flex;
  934. align-items: center;
  935. justify-content: center;
  936. cursor: pointer;
  937. transition: all 0.3s ease;
  938. color: white;
  939. }
  940. .collapsed-toggle-btn:hover {
  941. background: rgba(255, 255, 255, 0.2);
  942. border-color: rgba(255, 255, 255, 0.3);
  943. }
  944. .god-icon {
  945. width: 100%;
  946. }
  947. .collapsed-bottom-container {
  948. width: 100%;
  949. height: 16%;
  950. margin-top: auto;
  951. background-color: rgba(106, 0, 255, 0.2);
  952. display: flex;
  953. flex-direction: column;
  954. align-items: center;
  955. justify-content: center;
  956. gap: 30px;
  957. z-index: 1000;
  958. }
  959. .collapsed-bottom-btn {
  960. width: 100%;
  961. /* height: 50%; */
  962. display: flex;
  963. justify-content: center;
  964. cursor: pointer;
  965. /* align-items: center; */
  966. }
  967. .collapsed-bottom-feedback {
  968. width: 60%;
  969. height: auto;
  970. }
  971. .collapsed-bottom-feedback:hover {
  972. transform: scale(1.1);
  973. }
  974. .collapsed-bottom-announcement {
  975. width: 60%;
  976. height: auto;
  977. }
  978. .collapsed-bottom-announcement:hover {
  979. transform: scale(1.1);
  980. }
  981. .history-content {
  982. flex: 1;
  983. display: flex;
  984. flex-direction: column;
  985. width: 100%;
  986. /* padding: 20px; */
  987. overflow: hidden;
  988. min-height: 0;
  989. }
  990. .head-container {
  991. margin-top: 20%;
  992. margin-bottom: 10px;
  993. width: 100%;
  994. display: flex;
  995. align-items: center;
  996. justify-content: center;
  997. }
  998. .mobile-head-container {
  999. padding: 5px 0px 5px 20px;
  1000. /* margin-left: 20px; */
  1001. /* width: 100%; */
  1002. display: flex;
  1003. align-items: center;
  1004. border-bottom: 2px solid #414141;
  1005. /* justify-content: center; */
  1006. }
  1007. .history-header {
  1008. display: flex;
  1009. justify-content: space-between;
  1010. align-items: center;
  1011. margin-bottom: 20px;
  1012. padding-bottom: 15px;
  1013. border-bottom: 1px solid rgba(255, 255, 255, 0.1);
  1014. }
  1015. .history-actions {
  1016. display: flex;
  1017. justify-content: center;
  1018. align-items: center;
  1019. }
  1020. .mobile-history-actions {
  1021. margin-left: auto;
  1022. display: flex;
  1023. justify-content: center;
  1024. align-items: center;
  1025. }
  1026. .logo-img {
  1027. height: auto;
  1028. width: 70%;
  1029. object-fit: contain;
  1030. }
  1031. .history-list {
  1032. flex: 1;
  1033. overflow-y: auto;
  1034. scrollbar-width: thin;
  1035. scrollbar-color: rgba(255, 255, 255, 0.3) transparent;
  1036. }
  1037. .history-list::-webkit-scrollbar {
  1038. width: 6px;
  1039. }
  1040. .history-list::-webkit-scrollbar-track {
  1041. background: transparent;
  1042. }
  1043. .history-list::-webkit-scrollbar-thumb {
  1044. background: rgba(255, 255, 255, 0.3);
  1045. border-radius: 3px;
  1046. }
  1047. .history-list::-webkit-scrollbar-thumb:hover {
  1048. background: rgba(255, 255, 255, 0.5);
  1049. }
  1050. .categoryName {
  1051. color: white;
  1052. padding: 12px;
  1053. }
  1054. .history-item {
  1055. background: rgba(255, 255, 255, 0.05);
  1056. border-radius: 8px;
  1057. margin-bottom: 8px;
  1058. padding: 12px;
  1059. cursor: pointer;
  1060. transition: all 0.2s ease;
  1061. border: 1px solid transparent;
  1062. display: flex;
  1063. justify-content: center;
  1064. align-items: center;
  1065. }
  1066. .history-item:hover {
  1067. background: rgba(255, 255, 255, 0.1);
  1068. }
  1069. .history-item.active {
  1070. background: rgba(255, 255, 255, 0.5);
  1071. }
  1072. .record-content {
  1073. display: flex;
  1074. width: 100%;
  1075. }
  1076. .record-img {
  1077. display: flex;
  1078. align-items: center;
  1079. justify-content: center;
  1080. width: 20%;
  1081. }
  1082. .record-msg {
  1083. width: 80%;
  1084. }
  1085. .record-text {
  1086. color: white;
  1087. font-size: 13px;
  1088. line-height: 1.4;
  1089. margin-bottom: 6px;
  1090. overflow: hidden;
  1091. text-overflow: ellipsis;
  1092. -webkit-line-clamp: 2;
  1093. -webkit-box-orient: vertical;
  1094. display: flex;
  1095. }
  1096. .top-icon {
  1097. margin-left: 5px;
  1098. color: white;
  1099. height: auto;
  1100. width: 15px;
  1101. }
  1102. .stock-name {
  1103. font-weight: 500;
  1104. margin-right: 4px;
  1105. }
  1106. .stock-code {
  1107. color: rgba(255, 255, 255, 0.7);
  1108. font-size: 12px;
  1109. font-weight: 400;
  1110. }
  1111. .record-time {
  1112. color: rgba(255, 255, 255, 0.6);
  1113. font-size: 11px;
  1114. }
  1115. .record-actions {
  1116. height: 100%;
  1117. /* margin-left: 8px; */
  1118. transition: opacity 0.2s ease;
  1119. }
  1120. .more-btn {
  1121. background: rgba(231, 76, 60, 0);
  1122. border: none;
  1123. border-radius: 4px;
  1124. color: white;
  1125. padding: 4px;
  1126. cursor: pointer;
  1127. transition: all 0.2s ease;
  1128. display: flex;
  1129. align-items: center;
  1130. justify-content: center;
  1131. }
  1132. .more-btn:hover {
  1133. background: rgba(255, 255, 255, 0.3);
  1134. transform: scale(1.1);
  1135. }
  1136. .popover-content {
  1137. display: flex;
  1138. flex-direction: column;
  1139. }
  1140. .popover-item {
  1141. display: flex;
  1142. align-items: center;
  1143. padding: 10px;
  1144. /* justify-content: center; */
  1145. }
  1146. .popover-btn {
  1147. cursor: pointer;
  1148. }
  1149. .popover-btn:hover {
  1150. background: rgba(0, 0, 0, 0.1);
  1151. }
  1152. .popover-icon {
  1153. margin-right: 5px;
  1154. }
  1155. .empty-state {
  1156. display: flex;
  1157. flex-direction: column;
  1158. align-items: center;
  1159. justify-content: center;
  1160. padding: 40px 20px;
  1161. text-align: center;
  1162. }
  1163. .empty-icon {
  1164. margin-bottom: 16px;
  1165. opacity: 0.5;
  1166. }
  1167. .documentDelete {
  1168. color: white;
  1169. font-size: 5rem;
  1170. }
  1171. .empty-text {
  1172. color: rgba(255, 255, 255, 0.6);
  1173. font-size: 14px;
  1174. margin: 0;
  1175. }
  1176. .bottom-container {
  1177. width: 100%;
  1178. height: 16%;
  1179. margin-top: auto;
  1180. background-color: rgba(106, 0, 255, 0.2);
  1181. display: flex;
  1182. justify-content: space-between;
  1183. align-items: center;
  1184. }
  1185. .mobile-bottom-container {
  1186. border-top: 2px solid #414141;
  1187. width: 100%;
  1188. height: 16%;
  1189. margin-top: auto;
  1190. background-color: rgba(106, 0, 255, 0.2);
  1191. display: flex;
  1192. flex-direction: column;
  1193. justify-content: space-between;
  1194. align-items: center;
  1195. }
  1196. .bottom-btn {
  1197. width: 50%;
  1198. display: flex;
  1199. flex-direction: column;
  1200. justify-content: center;
  1201. align-items: center;
  1202. cursor: pointer;
  1203. }
  1204. .bottom-btn:hover {
  1205. transform: scale(1.2);
  1206. }
  1207. .mobile-bottom-btn {
  1208. width: 100%;
  1209. height: 50%;
  1210. display: flex;
  1211. align-items: center;
  1212. }
  1213. .bottom-feedback {
  1214. width: 35%;
  1215. height: auto;
  1216. }
  1217. .mobile-bottom-feedback {
  1218. margin-left: 20px;
  1219. height: 60%;
  1220. width: auto;
  1221. }
  1222. /* .bottom-feedback:hover {
  1223. transform: scale(1.2);
  1224. } */
  1225. .bottom-announcement {
  1226. width: 35%;
  1227. height: auto;
  1228. }
  1229. .mobile-bottom-announcement {
  1230. margin-left: 20px;
  1231. height: 60%;
  1232. width: auto;
  1233. }
  1234. .mobile-bottom-text {
  1235. color: white;
  1236. margin-left: 10px;
  1237. font-size: 1.1rem;
  1238. }
  1239. /* .bottom-announcement:hover {
  1240. transform: scale(1.2);
  1241. } */
  1242. /* 移动端适配 */
  1243. @media (max-width: 768px) {
  1244. .history-content {
  1245. /* padding: 15px; */
  1246. }
  1247. }
  1248. </style>
  1249. <style>
  1250. .el-popover {
  1251. width: auto !important;
  1252. padding: 0 !important;
  1253. }
  1254. </style>