Explorar el Código

【增加】增加 chat 聊天列表、删除、复制到粘贴板

cherishsince hace 1 año
padre
commit
b116d82376

+ 41 - 0
src/api/ai/chat/message/index.ts

@@ -0,0 +1,41 @@
+import request from '@/config/axios'
+
+// 聊天VO
+export interface ChatMessageVO {
+  id: number // 编号
+  conversationId: string // 会话编号
+  type: string // 消息类型
+  userId: string // 用户编号
+  roleId: string // 角色编号
+  model: number // 模型标志
+  modelId: number // 模型编号
+  content: number // 聊天内容
+  tokens: number // 消耗 Token 数量
+  createTime: Date // 创建时间
+}
+
+export interface ChatMessageSendVO {
+  conversationId: string // 会话编号
+  content: number // 聊天内容
+}
+
+// AI chat 聊天
+export const ChatMessageApi = {
+
+  // 消息列表
+  messageList: async (conversationId: string) => {
+    return await request.get({ url: `/ai/chat/message/list-by-conversation-id?conversationId=${conversationId}`})
+  },
+
+  // 发送 send 消息
+  send: async (data: ChatMessageSendVO) => {
+    return await request.post({ url: `/ai/chat/message/send`, data })
+  },
+
+
+  // 发送 send 消息
+  delete: async (id: string) => {
+    return await request.delete({ url: `/ai/chat/message/delete?id=${id}` })
+  },
+
+}

+ 276 - 0
src/views/ai/chat/components/MessageList.vue

@@ -0,0 +1,276 @@
+
+<template>
+  <div class="message-container" ref="messageContainer">
+    <div class="chat-list" v-for="(item, index) in list" :key="index">
+      <!--  靠左 message  -->
+      <div class="left-message message-item" v-if="item.type === 'system'">
+        <div class="avatar" >
+          <el-avatar
+            src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"
+          />
+        </div>
+        <div class="message">
+          <div>
+            <el-text class="time">{{formatDate(item.createTime)}}</el-text>
+          </div>
+          <div class="left-text-container">
+            <el-text class="left-text">
+              {{item.content}}
+            </el-text>
+          </div>
+          <div class="left-btns">
+            <div class="btn-cus" @click="noCopy(item.content)">
+              <img class="btn-image" src="@/assets/ai/copy.svg"/>
+              <el-text class="btn-cus-text">复制</el-text>
+            </div>
+            <div class="btn-cus" style="margin-left: 20px;" @click="onDelete(item.id)">
+              <img class="btn-image" src="@/assets/ai/delete.svg" style="height: 17px;"/>
+              <el-text class="btn-cus-text">删除</el-text>
+            </div>
+          </div>
+        </div>
+      </div>
+      <!--  靠右 message  -->
+      <div class="right-message message-item" v-if="item.type === 'user'">
+        <div class="avatar">
+          <el-avatar
+            src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"
+          />
+        </div>
+        <div class="message">
+          <div>
+            <el-text class="time">{{formatDate(item.createTime)}}</el-text>
+          </div>
+          <div class="right-text-container">
+            <el-text class="right-text">
+              {{item.content}}
+            </el-text>
+          </div>
+          <div class="right-btns">
+            <div class="btn-cus"  @click="noCopy(item.content)">
+              <img class="btn-image" src="@/assets/ai/copy.svg"/>
+              <el-text class="btn-cus-text">复制</el-text>
+            </div>
+            <div class="btn-cus" style="margin-left: 20px;" @click="onDelete(item.id)">
+              <img class="btn-image" src="@/assets/ai/delete.svg" style="height: 17px;"/>
+              <el-text class="btn-cus-text">删除</el-text>
+            </div>
+          </div>
+        </div>
+
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { useClipboard } from '@vueuse/core'
+import {ChatMessageApi, ChatMessageVO, ChatMessageSendVO} from '@/api/ai/chat/message'
+import { formatDate } from '@/utils/formatTime'
+import {ApiKeyVO} from "@/api/ai/model/apiKey";
+
+// 初始化 copy 到粘贴板
+const { copy, isSupported } = useClipboard();
+/** chat message 列表 */
+defineOptions({ name: 'chatMessageList' })
+const list = ref<ChatMessageVO[]>([]) // 列表的数据
+
+// 对话id TODO @范 先写死
+const conversationId = '1781604279872581648'
+const content = '苹果是什么颜色?'
+
+/** 查询列表 */
+const messageList = async () => {
+  try {
+    // 获取列表数据
+    const res = await ChatMessageApi.messageList(conversationId)
+    list.value = res;
+    // 滚动到最下面
+    scrollToBottom();
+  } finally {
+  }
+}
+// ref
+const messageContainer: any = ref(null);
+const isScrolling = ref(false)//用于判断用户是否在滚动
+
+/** send */
+const sendMessage = async () => {
+  try {
+    const requestParams = {
+      conversationId,
+      content,
+    }
+    const messageSendVO = requestParams as unknown as ChatMessageSendVO
+    const res = await ChatMessageApi.send(messageSendVO) as unknown as ChatMessageVO
+    console.log('---', res.content)
+  } finally {
+  }
+}
+
+function scrollToBottom() {
+  nextTick(() => {
+    //注意要使用nexttick以免获取不到dom
+    console.log('isScrolling.value', isScrolling.value)
+    if (!isScrolling.value) {
+      messageContainer.value.scrollTop = messageContainer.value.scrollHeight - messageContainer.value.offsetHeight
+    }
+  })
+}
+
+function handleScroll() {
+  const scrollContainer = messageContainer.value
+  const scrollTop = scrollContainer.scrollTop
+  const scrollHeight = scrollContainer.scrollHeight
+  const offsetHeight = scrollContainer.offsetHeight
+
+  if (scrollTop + offsetHeight < scrollHeight) {
+    // 用户开始滚动并在最底部之上,取消保持在最底部的效果
+    isScrolling.value = true
+  } else {
+    // 用户停止滚动并滚动到最底部,开启保持到最底部的效果
+    isScrolling.value = false
+  }
+}
+
+function noCopy(content) {
+  copy(content)
+  ElMessage({
+    message: '复制成功!',
+    type: 'success',
+  })
+}
+
+const onDelete = async (id) => {
+  // 删除 message
+  await ChatMessageApi.delete(id)
+  ElMessage({
+    message: '删除成功!',
+    type: 'success',
+  })
+  // 重新获取 message 列表
+  await messageList();
+}
+
+/** 初始化 **/
+onMounted(async () => {
+  // 获取列表数据
+  messageList();
+
+  // scrollToBottom();
+  // await nextTick
+  // 监听滚动事件,判断用户滚动状态
+  messageContainer.value.addEventListener('scroll', handleScroll)
+
+})
+</script>
+
+<style scoped lang="scss">
+
+.message-container {
+  position: absolute;
+  top: 0;
+  bottom: 0;
+  overflow-y: scroll;
+  padding: 0 15px;
+}
+
+// 中间
+.chat-list {
+  display: flex;
+  flex-direction: column;
+  overflow-y: hidden;
+
+  .message-item {
+    margin-top: 50px;
+  }
+
+  .left-message {
+    display: flex;
+    flex-direction: row;
+  }
+
+  .right-message {
+    display: flex;
+    flex-direction: row-reverse;
+    justify-content: flex-start;
+  }
+
+  .avatar {
+    //height: 170px;
+    //width: 170px;
+  }
+
+  .message {
+    display: flex;
+    flex-direction: column;
+    text-align: left;
+    margin: 0 15px;
+
+    .time {
+      text-align: left;
+      line-height: 30px;
+    }
+
+    .left-text-container {
+      display: flex;
+      flex-direction: column;
+      overflow-wrap: break-word;
+      background-color: #e4e4e4;
+      box-shadow: 0 0 0 1px #e4e4e4;
+      border-radius: 10px;
+      padding: 10px 10px 5px 10px;
+
+      .left-text {
+        color: #393939;
+      }
+    }
+
+    .right-text-container {
+      display: flex;
+      flex-direction: column;
+      overflow-wrap: break-word;
+      background-color: #267fff;
+      color: #FFF;
+      box-shadow: 0 0 0 1px #267fff;
+      border-radius: 10px;
+      padding: 10px;
+
+      .right-text {
+        color: #FFF;
+      }
+    }
+
+    .left-btns, .right-btns {
+      display: flex;
+      flex-direction: row;
+      margin-top: 8px;
+    }
+  }
+
+  // 复制、删除按钮
+  .btn-cus {
+    display: flex;
+    background-color: transparent;
+    align-items: center;
+
+    .btn-image {
+      height: 20px;
+      margin-right: 5px;
+    }
+
+    .btn-cus-text {
+      color: #757575;
+    }
+  }
+
+  .btn-cus:hover {
+    cursor: pointer;
+  }
+
+  .btn-cus:focus {
+    background-color: #8c939d;
+  }
+}
+
+</style>

+ 15 - 165
src/views/ai/chat/index.vue

@@ -81,73 +81,10 @@
           </el-button>
         </div>
       </el-header>
-      <el-main>
-
-        <div class="chat-list">
-          <!--  靠左 message  -->
-          <div class="left-message message-item">
-            <div class="avatar">
-              <el-avatar
-                src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"
-              />
-            </div>
-            <div class="message">
-              <div>
-                <el-text class="time">2024-05-10 22:38</el-text>
-              </div>
-              <div class="left-text-container">
-                <el-text class="left-text">
-                  如果您想获取某个网页或程序的截图,可以使用浏览器自带的截图功能,或者使用第三方截图工具,如Snipping
-                  Tool、FastStone Capture等。如果您想将屏幕上的某个区域截取下来,可以使用键盘上的“Prt
-                  Sc”键(或“Print Screen”键)来获取整个屏幕的截图,并将其粘贴到图像编辑软件中进行编辑和保存。
-                  如果您需要更具体的帮助,例如如何使用特定的截图工具或如何编辑截图,请提供更多详细信息,我将尽力为您提供帮助。
-                </el-text>
-
-              </div>
-              <div class="left-btns">
-                <div class="btn-cus">
-                  <img class="btn-image" src="@/assets/ai/copy.svg"/>
-                  <el-text class="btn-cus-text">复制</el-text>
-                </div>
-                <div class="btn-cus" style="margin-left: 20px;">
-                  <img class="btn-image" src="@/assets/ai/delete.svg" style="height: 17px;"/>
-                  <el-text class="btn-cus-text">删除</el-text>
-                </div>
-              </div>
-            </div>
-          </div>
-          <!--  靠右 message  -->
-          <div class="right-message message-item">
-            <div class="avatar">
-              <el-avatar
-                src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"
-              />
-            </div>
-            <div class="message">
-              <div>
-                <el-text class="time">2024-05-10 22:38</el-text>
-              </div>
-              <div class="right-text-container">
-                <el-text class="right-text">
-                  今天天气
-                </el-text>
-              </div>
-              <div class="right-btns">
-                <div class="btn-cus">
-                  <img class="btn-image" src="@/assets/ai/copy.svg"/>
-                  <el-text class="btn-cus-text">复制</el-text>
-                </div>
-                <div class="btn-cus" style="margin-left: 20px;">
-                  <img class="btn-image" src="@/assets/ai/delete.svg" style="height: 17px;"/>
-                  <el-text class="btn-cus-text">删除</el-text>
-                </div>
-              </div>
-            </div>
-
-          </div>
-        </div>
-
 
+      <!--  main    -->
+      <el-main class="main-container">
+        <MessageList />
       </el-main>
       <el-footer class="footer-container">
         <textarea placeholder="问我任何问题...(Shift+Enter 换行,按下 Enter 发送)"
@@ -161,7 +98,10 @@
     </el-container>
   </el-container>
 </template>
+
 <script setup lang="ts">
+import MessageList from './components/MessageList.vue'
+
 const conversationList = [
   {
     id: 1,
@@ -199,6 +139,10 @@ const deleteConversationTitle = (conversation) => {
 const searchConversation = () => {
   // TODO 芋艿:待实现
 }
+
+/** 初始化 **/
+onMounted(() => {
+})
 </script>
 <style lang="scss" scoped>
 
@@ -331,107 +275,13 @@ const searchConversation = () => {
   }
 }
 
-// 中间
-.chat-list {
-  display: flex;
-  flex-direction: column;
-
-  .message-item {
-    margin-top: 50px;
-  }
-
-  .left-message {
-    display: flex;
-    flex-direction: row;
-
-  }
-
-  .right-message {
-    display: flex;
-    flex-direction: row-reverse;
-    justify-content: flex-start;
-  }
-
-  .avatar {
-    //height: 170px;
-    //width: 170px;
-  }
-
-  .message {
-    display: flex;
-    flex-direction: column;
-    text-align: left;
-    margin: 0 15px;
-
-    .time {
-      text-align: left;
-      line-height: 30px;
-    }
-
-    .left-text-container {
-      display: flex;
-      flex-direction: column;
-      overflow-wrap: break-word;
-      background-color: #e4e4e4;
-      box-shadow: 0 0 0 1px #e4e4e4;
-      border-radius: 10px;
-      padding: 10px 10px 5px 10px;
-
-      .left-text {
-        color: #393939;
-      }
-    }
-
-    .right-text-container {
-      display: flex;
-      flex-direction: column;
-      overflow-wrap: break-word;
-      background-color: #267fff;
-      color: #FFF;
-      box-shadow: 0 0 0 1px #267fff;
-      border-radius: 10px;
-      padding: 10px;
-
-      .right-text {
-        color: #FFF;
-      }
-    }
-
-    .left-btns, .right-btns {
-      display: flex;
-      flex-direction: row;
-      margin-top: 8px;
-    }
-
-
-  }
-
-  // 复制、删除按钮
-  .btn-cus {
-    display: flex;
-    background-color: transparent;
-    align-items: center;
-
-    .btn-image {
-      height: 20px;
-      margin-right: 5px;
-    }
-
-    .btn-cus-text {
-      color: #757575;
-    }
-  }
-
-  .btn-cus:hover {
-    cursor: pointer;
-  }
-
-  .btn-cus:focus {
-    background-color: #8c939d;
-  }
+// main 容器
+.main-container {
+  margin: 0;
+  padding: 0;
+  position: relative;
 }
 
-
 // 底部
 .footer-container {
   display: flex;