Преглед на файлове

【代码优化】AI:聊天对话 index.vue 代码梳理 40%(message 部分)

YunaiV преди 11 месеца
родител
ревизия
1a6afa3263

+ 1 - 0
src/api/ai/chat/conversation/index.ts

@@ -12,6 +12,7 @@ export interface ChatConversationVO {
   temperature: number // 温度参数
   maxTokens: number // 单条回复的最大 Token 数量
   maxContexts: number // 上下文的最大 Message 数量
+  createTime?: Date // 创建时间
   // 额外字段
   systemMessage?: string // 角色设定
   modelName?: string // 模型名字

+ 44 - 83
src/views/ai/chat/index/components/message/MessageList.vue

@@ -1,7 +1,7 @@
 <template>
-  <div ref="messageContainer" style="height: 100%; overflow-y: auto; position: relative">
+  <div ref="messageContainer" class="h-100% overflow-y relative">
     <div class="chat-list" v-for="(item, index) in list" :key="index">
-      <!--  靠左 message  -->
+      <!-- 靠左 message:system、assistant 类型 -->
       <div class="left-message message-item" v-if="item.type !== 'user'">
         <div class="avatar">
           <el-avatar :src="roleAvatar" />
@@ -14,16 +14,16 @@
             <MarkdownView class="left-text" :content="item.content" />
           </div>
           <div class="left-btns">
-            <el-button class="btn-cus" link @click="noCopy(item.content)">
+            <el-button class="btn-cus" link @click="copyContent(item.content)">
               <img class="btn-image" src="@/assets/ai/copy.svg" />
             </el-button>
             <el-button v-if="item.id > 0" class="btn-cus" link @click="onDelete(item.id)">
-              <img class="btn-image" src="@/assets/ai/delete.svg" style="height: 17px" />
+              <img class="btn-image h-17px" src="@/assets/ai/delete.svg" />
             </el-button>
           </div>
         </div>
       </div>
-      <!--  靠右 message  -->
+      <!-- 靠右 message:user 类型 -->
       <div class="right-message message-item" v-if="item.type === 'user'">
         <div class="avatar">
           <el-avatar :src="userAvatar" />
@@ -36,15 +36,11 @@
             <div class="right-text">{{ item.content }}</div>
           </div>
           <div class="right-btns">
-            <el-button class="btn-cus" link @click="noCopy(item.content)">
+            <el-button class="btn-cus" link @click="copyContent(item.content)">
               <img class="btn-image" src="@/assets/ai/copy.svg" />
             </el-button>
             <el-button class="btn-cus" link @click="onDelete(item.id)">
-              <img
-                class="btn-image"
-                src="@/assets/ai/delete.svg"
-                style="height: 17px; margin-right: 12px"
-              />
+              <img class="btn-image h-17px mr-12px" src="@/assets/ai/delete.svg" />
             </el-button>
             <el-button class="btn-cus" link @click="onRefresh(item)">
               <el-icon size="17"><RefreshRight /></el-icon>
@@ -63,23 +59,25 @@
   </div>
 </template>
 <script setup lang="ts">
+import { PropType } from 'vue'
 import { formatDate } from '@/utils/formatTime'
 import MarkdownView from '@/components/MarkdownView/index.vue'
-import { ChatMessageApi, ChatMessageVO } from '@/api/ai/chat/message'
 import { useClipboard } from '@vueuse/core'
-import { PropType } from 'vue'
 import { ArrowDownBold, Edit, RefreshRight } from '@element-plus/icons-vue'
+import { ChatMessageApi, ChatMessageVO } from '@/api/ai/chat/message'
 import { ChatConversationVO } from '@/api/ai/chat/conversation'
 import { useUserStore } from '@/store/modules/user'
 import userAvatarDefaultImg from '@/assets/imgs/avatar.gif'
 import roleAvatarDefaultImg from '@/assets/ai/gpt.svg'
 
+const message = useMessage() // 消息弹窗
 const { copy } = useClipboard() // 初始化 copy 到粘贴板
-// 判断 消息列表 滚动的位置(用于判断是否需要滚动到消息最下方)
+const userStore = useUserStore()
+
+// 判断“消息列表”滚动的位置(用于判断是否需要滚动到消息最下方)
 const messageContainer: any = ref(null)
 const isScrolling = ref(false) //用于判断用户是否在滚动
 
-const userStore = useUserStore()
 const userAvatar = computed(() => userStore.user.avatar ?? userAvatarDefaultImg)
 const roleAvatar = computed(() => props.conversation.roleAvatar ?? roleAvatarDefaultImg)
 
@@ -95,12 +93,16 @@ const props = defineProps({
   }
 })
 
+const { list } = toRefs(props) // 消息列表
+
+const emits = defineEmits(['onDeleteSuccess', 'onRefresh', 'onEdit']) // 定义 emits
+
 // ============ 处理对话滚动 ==============
 
+/** 滚动到底部 */
 const scrollToBottom = async (isIgnore?: boolean) => {
+  // 注意要使用 nextTick 以免获取不到dom
   await nextTick(() => {
-    // TODO @fan:中文写作习惯,中英文之间要有空格;另外,nextick 哈,idea 如果有绿色波兰线,可以关注下
-    //注意要使用nexttick以免获取不到dom
     if (isIgnore || !isScrolling.value) {
       messageContainer.value.scrollTop =
         messageContainer.value.scrollHeight - messageContainer.value.offsetHeight
@@ -122,75 +124,48 @@ function handleScroll() {
   }
 }
 
-/**
- * 复制
- */
-const noCopy = async (content) => {
-  copy(content)
-  ElMessage({
-    message: '复制成功!',
-    type: 'success'
-  })
+/** 回到底部 */
+const handleGoBottom = async () => {
+  const scrollContainer = messageContainer.value
+  scrollContainer.scrollTop = scrollContainer.scrollHeight
+}
+
+/** 回到顶部 */
+const handlerGoTop = async () => {
+  const scrollContainer = messageContainer.value
+  scrollContainer.scrollTop = 0
+}
+
+defineExpose({ scrollToBottom, handlerGoTop }) // 提供方法给 parent 调用
+
+// ============ 处理消息操作 ==============
+
+/** 复制 */
+const copyContent = async (content) => {
+  await copy(content)
+  message.success('复制成功!')
 }
 
-/**
- * 删除
- */
+/** 删除 */
 const onDelete = async (id) => {
   // 删除 message
   await ChatMessageApi.deleteChatMessage(id)
-  ElMessage({
-    message: '删除成功!',
-    type: 'success'
-  })
+  message.success('删除成功!')
   // 回调
   emits('onDeleteSuccess')
 }
 
-/**
- * 刷新
- */
+/** 刷新 */
 const onRefresh = async (message: ChatMessageVO) => {
   emits('onRefresh', message)
 }
 
-/**
- * 编辑
- */
+/** 编辑 */
 const onEdit = async (message: ChatMessageVO) => {
   emits('onEdit', message)
 }
 
-/**
- * 回到底部
- */
-const handleGoBottom = async () => {
-  const scrollContainer = messageContainer.value
-  scrollContainer.scrollTop = scrollContainer.scrollHeight
-}
-
-/**
- * 回到顶部
- */
-const handlerGoTop = async () => {
-  const scrollContainer = messageContainer.value
-  scrollContainer.scrollTop = 0
-}
-
-// 监听 list
-// TODO @fan:这个木有,是不是删除啦
-const { list, conversationId } = toRefs(props)
-watch(list, async (newValue, oldValue) => {
-  console.log('watch list', list)
-})
-
-// 提供方法给 parent 调用
-defineExpose({ scrollToBottom, handlerGoTop })
-
-// 定义 emits
-const emits = defineEmits(['onDeleteSuccess', 'onRefresh', 'onEdit'])
-
-// onMounted
+/** 初始化 */
 onMounted(async () => {
   messageContainer.value.addEventListener('scroll', handleScroll)
 })
@@ -199,15 +174,7 @@ onMounted(async () => {
 <style scoped lang="scss">
 .message-container {
   position: relative;
-  //top: 0;
-  //bottom: 0;
-  //left: 0;
-  //right: 0;
-  //width: 100%;
-  //height: 100%;
   overflow-y: scroll;
-  //padding: 0 15px;
-  //z-index: -1;
 }
 
 // 中间
@@ -231,11 +198,6 @@ onMounted(async () => {
     justify-content: flex-start;
   }
 
-  .avatar {
-    //height: 170px;
-    //width: 170px;
-  }
-
   .message {
     display: flex;
     flex-direction: column;
@@ -272,7 +234,6 @@ onMounted(async () => {
         color: #fff;
         display: inline;
         background-color: #267fff;
-        color: #fff;
         box-shadow: 0 0 0 1px #267fff;
         border-radius: 10px;
         padding: 10px;

+ 15 - 11
src/views/ai/chat/index/components/message/MessageListEmpty.vue

@@ -1,31 +1,35 @@
+<!-- 消息列表为空时,展示 prompt 列表 -->
 <template>
   <div class="chat-empty">
-
-    <!--  title  -->
+    <!-- title -->
     <div class="center-container">
-      <div class="title">芋 AI</div>
+      <div class="title">芋 AI</div>
       <div class="role-list">
-        <div class="role-item" v-for="prompt in promptList" :key="prompt.prompt" @click="handlerPromptClick(prompt)">
-          {{prompt.prompt}}
+        <div
+          class="role-item"
+          v-for="prompt in promptList"
+          :key="prompt.prompt"
+          @click="handlerPromptClick(prompt)"
+        >
+          {{ prompt.prompt }}
         </div>
       </div>
     </div>
   </div>
 </template>
 <script setup lang="ts">
-
-const promptList = ref<any[]>() // 角色列表
-promptList.value = [
+const promptList = [
   {
-    "prompt": "今天气怎么样?",
+    prompt: '今天气怎么样?'
   },
   {
-    "prompt": "写一首好听的诗歌?",
+    prompt: '写一首好听的诗歌?'
   }
-]
+] // prompt 列表
 
 const emits = defineEmits(['onPrompt'])
 
+/** 选中 prompt 点击 */
 const handlerPromptClick = async ({ prompt }) => {
   emits('onPrompt', prompt)
 }

+ 1 - 4
src/views/ai/chat/index/components/message/MessageNewConversation.vue

@@ -9,13 +9,10 @@
     </div>
   </div>
 </template>
-
 <script setup lang="ts">
 const emits = defineEmits(['onNewConversation'])
 
-/**
- * 新建 conversation 聊天对话
- */
+/** 新建 conversation 聊天对话 */
 const handlerNewChat = () => {
   emits('onNewConversation')
 }