bottomSider2.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. <template>
  2. <div>
  3. <div class="trigger-button">
  4. <div class="icon" @click="openMenu"></div>
  5. </div>
  6. <div v-if="dialogVisible" class="dialog-overlay">
  7. <!-- 左侧折叠区域 -->
  8. <div class="left-side" :class="{ collapsed: isFold }" id="leftSide">
  9. <div
  10. class="addBtn"
  11. :style="{
  12. backgroundImage: `url(${isFold ? '/src/assets/images/vent/home/add.svg' : ''})`,
  13. backgroundColor: isFold ? '' : '#2cb6ff',
  14. width: isFold ? '20px' : 'auto',
  15. }"
  16. >
  17. <span
  18. class="btn-text-bg"
  19. :style="{
  20. backgroundImage: `url(${!isFold ? '/src/assets/images/vent/home/addB.svg' : ''})`,
  21. }"
  22. ></span>
  23. <span v-if="!isFold" class="btn-text" @click="addNew">添加新对话</span>
  24. </div>
  25. <div class="divider0"></div>
  26. <div
  27. v-if="isFold"
  28. class="historyBtn"
  29. :style="{ backgroundImage: `url(${isFold ? '/src/assets/images/vent/home/history.svg' : '/src/assets/images/vent/home/history.svg'})` }"
  30. @click="addNew"
  31. ></div>
  32. <div v-else class="historyBtn1">
  33. <span
  34. class="btn-text-bg"
  35. :style="{
  36. backgroundImage: `url(${!isFold ? '/src/assets/images/vent/home/history.svg' : ''})`,
  37. }"
  38. ></span>
  39. <span v-if="!isFold" class="btn-text">历史对话</span>
  40. <a-list style="width: 100px" :split="false">
  41. <a-list-item style="padding: 5px 0 0 30px; color: #5e7081; font-size: 12px; white-space: nowrap; text-overflow: ellipsis"
  42. >历史数据1</a-list-item
  43. >
  44. <a-list-item style="padding: 5px 0 0 30px; color: #5e7081; font-size: 12px; white-space: nowrap; text-overflow: ellipsis"
  45. >历史数据12</a-list-item
  46. >
  47. <a-list-item style="padding: 5px 0 0 30px; color: #5e7081; font-size: 12px; white-space: nowrap; text-overflow: ellipsis"
  48. >历史数据123</a-list-item
  49. >
  50. </a-list>
  51. </div>
  52. <div
  53. class="foldBtn"
  54. :style="{ backgroundImage: `url(${isFold ? '/src/assets/images/vent/home/Fold.svg' : '/src/assets/images/vent/home/unfold.svg'})` }"
  55. @click="fold"
  56. ></div>
  57. </div>
  58. <!-- 右侧对话框 -->
  59. <div class="right-side">
  60. <div class="input-content">
  61. <!-- 对话区域 -->
  62. <div class="dialog-area">
  63. <div v-for="message in sortedMessages" :key="message.id" :class="['message-item', message.type]">
  64. <!-- 用户提问样式 -->
  65. <div v-if="message.type === 'user'" class="ask-message">
  66. <span>{{ message.content }}</span>
  67. </div>
  68. <!-- 系统回答样式 -->
  69. <div v-else class="system-message">
  70. <div class="answerIcon"></div>
  71. <div class="answer-message">
  72. <div>
  73. <span>{{ message.content }}</span>
  74. </div>
  75. </div>
  76. </div>
  77. </div>
  78. </div>
  79. <!-- 文本输入区域 -->
  80. <div v-if="spinning" class="thinking-area">
  81. <span style="color: #fff">思考中···</span>
  82. <a-spin :spinning="spinning"></a-spin>
  83. </div>
  84. <div class="input-area">
  85. <textarea v-model="inputText" placeholder="请输入你的问题"> </textarea>
  86. <!-- 底部操作栏 -->
  87. <div class="action-bar">
  88. <!-- 文件上传按钮 -->
  89. <label class="upload-btn">
  90. <div class="send-file" />
  91. <span class="divider"> | </span>
  92. <div class="send-img" />
  93. </label>
  94. <!-- 发送按钮 -->
  95. <div class="send-btn" @click="handleSend"></div>
  96. </div>
  97. </div>
  98. </div>
  99. </div>
  100. </div>
  101. </div>
  102. </template>
  103. <script lang="ts" setup>
  104. import { ref, onMounted, nextTick, computed } from 'vue';
  105. // 响应式变量声明
  106. const dialogVisible = ref(false);
  107. const isFold = ref(false); // 是否折叠
  108. const inputText = ref(''); // 输入框内容
  109. // const messages = ref([]); // 消息列表
  110. const spinning = ref(false); // 加载状态
  111. const systemMessage = ref(''); // 系统返回信息
  112. const session_id = ref(''); // 会话id
  113. const hasCreated = ref(false); // 标志位,防止重复调用create接口
  114. type MessageItem = {
  115. id: string; // 唯一标识(可用时间戳生成)
  116. type: 'user' | 'system';
  117. content: string;
  118. timestamp: number; // 排序依据
  119. };
  120. const messageList = ref<MessageItem[]>([]);
  121. const sortedMessages = computed(() => {
  122. return messageList.value.sort((a, b) => a.timestamp - b.timestamp);
  123. });
  124. // const scrollToBottom = () => {
  125. // nextTick(() => {
  126. // const container = document.querySelector('.dialog-area');
  127. // container.scrollTop = container.scrollHeight;
  128. // });
  129. // };
  130. const openMenu = () => {
  131. dialogVisible.value = !dialogVisible.value;
  132. if (dialogVisible.value) {
  133. hasCreated.value = true;
  134. addNew();
  135. }
  136. };
  137. const fold = () => {
  138. isFold.value = !isFold.value;
  139. };
  140. async function addNew() {
  141. let response = await fetch('http://182.92.126.35:6005/sessions/create', {
  142. method: 'post',
  143. headers: {
  144. 'Content-Type': 'application/json',
  145. },
  146. });
  147. const data = await response.json();
  148. session_id.value = data.session_id;
  149. }
  150. //获取消息列表
  151. async function handleSend() {
  152. spinning.value = true;
  153. // 添加用户消息
  154. messageList.value.push({
  155. id: `user_${Date.now()}`,
  156. type: 'user',
  157. content: inputText.value,
  158. timestamp: Date.now(),
  159. });
  160. // 调用接口获取答案
  161. // const answer = await fetchAnswerFromAPI(question);
  162. const params = {
  163. chat_session_id: session_id.value,
  164. prompt: inputText.value,
  165. ref_file_ids: [],
  166. thinking_enabled: false,
  167. };
  168. inputText.value = ''; // 清空输入框
  169. //将用户输入的内容发送到后端
  170. let response = await fetch('http://182.92.126.35:6005/chat', {
  171. method: 'POST',
  172. headers: {
  173. 'Content-Type': 'application/json',
  174. },
  175. body: JSON.stringify(params),
  176. });
  177. const data = await response.json();
  178. const assistantReply = data.reply; // 获取助手回复
  179. systemMessage.value = assistantReply;
  180. // 添加系统回答
  181. messageList.value.push({
  182. id: `system_${Date.now()}`,
  183. type: 'system',
  184. content: systemMessage.value,
  185. timestamp: Date.now(),
  186. });
  187. spinning.value = false;
  188. }
  189. // async function handleSend() {
  190. // spinning.value = true;
  191. // userMessage.value.push({
  192. // msg: inputText.value, // 消息内容
  193. // });
  194. // inputText.value = ''; // 清空输入框
  195. // const params = {
  196. // messages: [{ role: 'user', content: inputText.value }],
  197. // };
  198. // //将用户输入的内容发送到后端
  199. // let response = await fetch('http://182.92.126.35:6005/chat', {
  200. // method: 'POST',
  201. // headers: {
  202. // 'Content-Type': 'application/json',
  203. // },
  204. // body: JSON.stringify(params),
  205. // });
  206. // const data = await response.json();
  207. // spinning.value = false;
  208. // const assistantReply = data.reply; // 获取助手回复
  209. // systemMessage.value = assistantReply;
  210. // }
  211. // 初始化按钮定位
  212. onMounted(() => {});
  213. </script>
  214. <style lang="less" scoped>
  215. @keyframes menuShow {
  216. 0% {
  217. width: 0;
  218. height: 0;
  219. }
  220. 100% {
  221. width: 480px;
  222. height: 100vh;
  223. }
  224. }
  225. .trigger-button {
  226. position: fixed;
  227. bottom: 10px;
  228. right: 10px;
  229. z-index: 1000000;
  230. .icon {
  231. width: 60px;
  232. height: 60px;
  233. position: relative;
  234. background-image: url('/@/assets/images/vent/home/wakeBtn.png');
  235. background-position: center;
  236. background-size: 100% 100%;
  237. }
  238. }
  239. .dialog-overlay {
  240. width: 32%;
  241. height: 55%;
  242. z-index: 999;
  243. display: flex;
  244. position: fixed;
  245. right: 90px;
  246. bottom: 20px;
  247. box-shadow: 0 0 3px 3px #1074c1;
  248. background-color: #09172c;
  249. }
  250. .left-side {
  251. background: #0c2842;
  252. transition: width 0.5s ease; /* 平滑过渡动画 */
  253. width: 120px; /* 展开时宽度 */
  254. position: relative; /* 用于按钮定位 */
  255. }
  256. .left-side.collapsed {
  257. width: 40px; /* 折叠时宽度 */
  258. }
  259. .addBtn {
  260. height: 30px;
  261. position: absolute;
  262. background-size: 100% 100%;
  263. background-position: center;
  264. padding: 2px;
  265. right: 10px;
  266. top: 10px;
  267. left: 10px;
  268. align-items: center;
  269. border-radius: 3px;
  270. cursor: pointer;
  271. }
  272. .btn-text-bg {
  273. width: 14px;
  274. height: 14px;
  275. position: absolute;
  276. background-size: 100% 100%;
  277. right: 10px;
  278. top: 9px;
  279. left: 10px;
  280. bottom: 10px;
  281. }
  282. .btn-text {
  283. margin-left: 3px;
  284. font-size: 12px;
  285. color: #fff;
  286. white-space: nowrap;
  287. margin-left: 30px;
  288. line-height: 26px;
  289. }
  290. .historyBtn {
  291. width: 20px;
  292. height: 20px;
  293. position: absolute;
  294. background-size: 100% 100%;
  295. background-position: center;
  296. padding: 2px;
  297. right: 10px;
  298. top: 100px;
  299. }
  300. .historyBtn1 {
  301. width: 20px;
  302. height: 20px;
  303. position: absolute;
  304. background-size: 100% 100%;
  305. background-position: center;
  306. left: 3px;
  307. top: 80px;
  308. }
  309. .divider0 {
  310. border-bottom: 1px solid #1074c1;
  311. width: auto;
  312. margin: 0 10px;
  313. height: 13%;
  314. display: block;
  315. background: transparent;
  316. }
  317. .foldBtn {
  318. width: 20px;
  319. height: 20px;
  320. position: absolute;
  321. background-size: 100% 100%;
  322. background-position: center;
  323. padding: 2px;
  324. right: 10px;
  325. bottom: 10px;
  326. cursor: pointer;
  327. }
  328. .right-side {
  329. flex: 1; /* 占据剩余空间 */
  330. background: #09172c;
  331. }
  332. .input-content {
  333. display: flex;
  334. flex-direction: column;
  335. justify-content: flex-end; /* 内容底部对齐 */
  336. height: 100%;
  337. padding: 20px; /* 统一内边距 */
  338. }
  339. .ask-message {
  340. align-self: flex-end;
  341. float: right;
  342. max-width: 70%;
  343. padding: 10px;
  344. margin: 10px;
  345. border-radius: 5px;
  346. color: #fff;
  347. background: #0c2842;
  348. align-self: flex-end; /* 右侧对齐‌:ml-citation{ref="2" data="citationList"} */
  349. }
  350. .answer {
  351. display: flex;
  352. flex-direction: row;
  353. }
  354. .answerIcon {
  355. margin-top: 10px;
  356. width: 35px;
  357. height: 35px;
  358. background-image: url('/@/assets/images/vent/home/answerIcon.svg');
  359. background-size: 100% 100%;
  360. }
  361. .answer-message {
  362. float: left;
  363. max-width: 80%;
  364. padding: 10px;
  365. margin: 10px;
  366. border-radius: 5px;
  367. color: #fff;
  368. background: #0c2842;
  369. }
  370. /** 系统返回信息**/
  371. .system-message {
  372. display: flex;
  373. flex-direction: row;
  374. align-self: flex-start;
  375. max-width: 90%;
  376. padding: 12px;
  377. display: flex;
  378. }
  379. .answerIcon {
  380. margin-top: 10px;
  381. width: 35px;
  382. height: 35px;
  383. background-image: url('/@/assets/images/vent/home/answerIcon.svg');
  384. background-size: 100% 100%;
  385. }
  386. .answer-message {
  387. float: left;
  388. max-width: 80%;
  389. padding: 10px;
  390. margin: 10px;
  391. border-radius: 5px;
  392. color: #fff;
  393. background: #0c2842;
  394. }
  395. .dialog-area {
  396. flex: 1; /* 占据剩余空间 */
  397. gap: 50px; /* 消息块间隔统一控制 */
  398. overflow-y: auto; /* 垂直滚动条 */
  399. margin-bottom: 10px;
  400. }
  401. .loading-wrapper,
  402. .content-wrapper {
  403. min-height: 40px; /* 避免高度塌陷 */
  404. }
  405. .message-item.user {
  406. margin-bottom: 30px;
  407. }
  408. .input-area {
  409. background-color: #043256 !important;
  410. display: flex;
  411. flex-direction: column;
  412. gap: 10px;
  413. }
  414. textarea {
  415. background-color: #043256 !important;
  416. width: 100%;
  417. height: 40px;
  418. border: none;
  419. resize: none;
  420. outline: none;
  421. overflow: hidden;
  422. padding: 10px; /* 统一内边距 */
  423. color: #fff;
  424. }
  425. .action-bar {
  426. display: flex;
  427. align-items: center;
  428. gap: 12px;
  429. }
  430. .upload-btn {
  431. display: flex;
  432. cursor: pointer;
  433. padding: 6px 12px;
  434. }
  435. .divider {
  436. color: #ccc;
  437. font-weight: 300;
  438. margin: 0 10px;
  439. }
  440. .send-file {
  441. width: 20px;
  442. height: 20px;
  443. background-image: url('/@/assets/images/vent/home/sendFile.svg');
  444. background-size: 100% 100%;
  445. border-radius: 4px;
  446. cursor: pointer;
  447. }
  448. .send-img {
  449. width: 20px;
  450. height: 20px;
  451. background-image: url('/@/assets/images/vent/home/sendImg.svg');
  452. background-size: 100% 100%;
  453. border-radius: 4px;
  454. cursor: pointer;
  455. }
  456. .send-btn {
  457. width: 20px;
  458. height: 20px;
  459. margin-left: auto;
  460. margin-right: 10px;
  461. background-color: #1074c1;
  462. background-image: url('/@/assets/images/vent/home/send.svg');
  463. background-position: center;
  464. background-size: 100% 100%;
  465. border-radius: 2px;
  466. cursor: pointer;
  467. }
  468. </style>