wujingjing
2024-10-10 83cba4ea5493bb1488cd31e570de6ec458f56be0
src/components/chat/Chat.vue
@@ -1,8 +1,12 @@
<template>
   <div class="flex h-full">
      <div class="flex flex-col h-full flex-auto">
         <div class="h-full flex flex-col items-center overflow-y-auto">
            <div ref="chatListDom" class="h-full" :style="{ width: chatWidth }">
         <div ref="chatListDom" class="relative h-full flex flex-col items-center overflow-y-auto">
            <span
               class="more-loading absolute text-blue-400 left-[50%] translate-x-[-50%] cursor-pointer w-10"
               v-loading="moreIsLoading"
            ></span>
            <div class="h-full" v-loading="chatListLoading" :style="{ width: chatWidth }">
               <div
                  class="group flex px-4 py-6 hover:bg-slate-100 rounded-lg relative"
                  :class="{ 'flex-row-reverse': item.role === RoleEnum.user }"
@@ -156,7 +160,6 @@
</template>
<script setup lang="ts">
import { ElMessage } from 'element-plus';
import _ from 'lodash';
import { v4 as uuidv4 } from 'uuid';
import { computed, onMounted, ref } from 'vue';
@@ -164,10 +167,11 @@
import Loding from './components/Loding.vue';
import { useAssistantContentOpt } from './hooks/useAssistantContentOpt';
import { useQueryProcess } from './hooks/useQueryProcess';
import { useScrollLoad } from './hooks/useScrollLoad';
import { useScrollToBottom } from './hooks/useScrollToBottom';
import type { ChatContent } from './model/types';
import { AnswerState, AnswerType, RoleEnum, answerTypeMapCom, roleImageMap, type ChatMessage } from './model/types';
import { GetHistoryAnswer, QueryHistoryDetail, QuestionAi, extCallQuery } from '/@/api/ai/chat';
import { GetHistoryAnswer, QuestionAi, extCallQuery } from '/@/api/ai/chat';
import PlayBar from '/@/components/chat/components/playBar/PlayBar.vue';
import CustomDrawer from '/@/components/drawer/CustomDrawer.vue';
import router from '/@/router';
@@ -331,7 +335,7 @@
};
const sendChatMessage = async (content: ChatContent = messageContent.value, cb?: any, isCallExtParams?: any) => {
   if (!content?.values || isTalking.value) return;
   if (!content?.values || isTalking.value || chatListLoading.value) return;
   const isNewChat = messageList.value.length === 0;
   if (isNewChat) {
      if (activeSampleId.value) {
@@ -355,6 +359,9 @@
      // 出现回复,置空出现等待动画
      messageList.value.push(assistantItem);
      // 滚动至当前发送消息
      scrollToBottom();
      if (isCallExtParams) {
         const extRes = await extCallQuery(isCallExtParams);
         questionRes = extRes;
@@ -362,7 +369,7 @@
      } else {
         resMsgContent = await questionAi(content.values);
      }
      nextUserMsgEndIndex.value++;
      if (isNewChat) {
         const firstResCb = getRoomConfig(currentRouteId, 'firstResCb');
         firstResCb?.(resMsgContent);
@@ -374,6 +381,10 @@
      assistantItem.historyId = questionRes.history_id;
      assistantItem.sectionAId = finalCalcSectionAId;
      appendLastMessageContent(resMsgContent);
      setTimeout(() => {
         // 收到回复,继续滚
         scrollToBottom();
      }, 300);
   } catch (error: any) {
      // appendLastMessageContent({
      //    type: AnswerType.Text,
@@ -392,51 +403,27 @@
      messageList.value.at(-1).content = content;
   }
};
const { loadRangeData, onChatListScroll, moreIsLoading, nextUserMsgEndIndex } = useScrollLoad({
   container: chatListDom,
   historyGroupId: currentRouteId,
   messageList,
   parseAnswerContent: parseContent,
});
const chatListLoading = ref(false);
const { scrollToBottom } = useScrollToBottom({
   chatListDom: chatListDom,
});
onMounted(async () => {
   const res = await QueryHistoryDetail({
      history_group_id: currentRouteId,
   });
   messageList.value = [];
   // 加载初始数据
   chatListLoading.value = true;
   messageList.value = (res.details ?? []).map((item) => {
      return {
         historyId: item.history_id,
         role: RoleEnum.user,
         content: {
            type: AnswerType.Text,
            values: item.question,
         },
      } as ChatMessage;
   await loadRangeData().finally(() => {
      chatListLoading.value = false;
   });
   const sectionAIdMap = new Map();
   const resList = await Promise.all(
      (res.details ?? []).map((item) => {
         sectionAIdMap.set(item.history_id, item.section_a_id);
         return getAnswerById(item.history_id);
      })
   );
   let i = 0;
   resList.map((item, index) => {
      const insertIndex = index + 1 + i;
      const userMsg = messageList.value[insertIndex - 1];
      userMsg.content.values = item?.answer?.question ?? userMsg.content.values;
      messageList.value.splice(
         insertIndex,
         0,
         item.answer === null
            ? null
            : {
                  historyId: item.answer?.history_id,
                  role: RoleEnum.assistant,
                  content: parseContent(item.answer),
                  state: item.answer_state,
                  sectionAId: sectionAIdMap.get(item.answer.history_id),
              }
      );
      i++;
   });
   if (messageList.value.length === 0) {
      messageContent.value = {
         type: AnswerType.Text,
@@ -444,14 +431,17 @@
      };
      sendChatMessage();
   } else {
      setTimeout(() => {
         // 初始状态滚一下
         scrollToBottom();
         setTimeout(() => {
            chatListDom.value.addEventListener('scroll', onChatListScroll);
         }, 300);
      }, 300);
   }
});
const { forbidScroll } = useScrollToBottom({
   chatListDom: chatListDom,
   displayMessageList: computedMessageList,
});
//#region ====================== 关联查询 ======================
const relativeQueryClick = async (val) => {
   sendChatMessage(
@@ -486,7 +476,6 @@
   showFixQuestion,
   showAskMore,
} = useAssistantContentOpt({
   forbidScroll,
   sendChatMessage,
   displayMessageList: computedMessageList,
});
@@ -500,10 +489,21 @@
//#endregion
</script>
<style scoped>
<style scoped lang="scss">
pre {
   font-family: -apple-system, 'Noto Sans', 'Helvetica Neue', Helvetica, 'Nimbus Sans L', Arial, 'Liberation Sans', 'PingFang SC',
      'Hiragino Sans GB', 'Noto Sans CJK SC', 'Source Han Sans SC', 'Source Han Sans CN', 'Microsoft YaHei', 'Wenquanyi Micro Hei',
      'WenQuanYi Zen Hei', 'ST Heiti', SimHei, 'WenQuanYi Zen Hei Sharp', sans-serif;
}
.more-loading {
   :deep(.el-loading-spinner) {
      --loading-size: 35px;
      margin-top: 0;
      .circular {
         width: var(--loading-size);
         height: var(--loading-size);
      }
   }
}
</style>