wujingjing
2024-10-24 9089a59a073fd252cec25b45c735dc726f3ac3cf
src/components/chat/Chat.vue
@@ -1,13 +1,17 @@
<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" v-loading="chatListLoading" :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 }"
                  v-for="(item, index) of computedMessageList"
                  :key="index"
                  :key="`${item.historyId}_${item.role}`"
               >
                  <img
                     class="rounded-full size-12 flex-0"
@@ -163,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';
@@ -323,14 +328,9 @@
let currentLLMId = null;
const getAnswerById = async (historyId: string) => {
   return await GetHistoryAnswer({
      history_id: historyId,
   });
};
const sendChatMessage = async (content: ChatContent = messageContent.value, cb?: any, isCallExtParams?: any) => {
   if (!content?.values || isTalking.value ||chatListLoading.value) return;
   if (!content?.values || isTalking.value || chatListLoading.value) return;
   const isNewChat = messageList.value.length === 0;
   if (isNewChat) {
      if (activeSampleId.value) {
@@ -364,7 +364,7 @@
      } else {
         resMsgContent = await questionAi(content.values);
      }
      nextUserMsgEndIndex.value++;
      if (isNewChat) {
         const firstResCb = getRoomConfig(currentRouteId, 'firstResCb');
         firstResCb?.(resMsgContent);
@@ -376,8 +376,10 @@
      assistantItem.historyId = questionRes.history_id;
      assistantItem.sectionAId = finalCalcSectionAId;
      appendLastMessageContent(resMsgContent);
      // 收到恢复,继续滚
      scrollToBottom();
      setTimeout(() => {
         // 收到回复,继续滚
         scrollToBottom();
      }, 300);
   } catch (error: any) {
      // appendLastMessageContent({
      //    type: AnswerType.Text,
@@ -396,69 +398,28 @@
      messageList.value.at(-1).content = content;
   }
};
const { loadRangeData, onChatListScroll, moreIsLoading, nextUserMsgEndIndex } = useScrollLoad({
   container: chatListDom,
   historyGroupId: currentRouteId,
   messageList,
   parseAnswerContent: parseContent,
});
// 一次性加载最近条数限制
const LOAD_CHAT_LIMIT = 10;
// 所有用户提问历史记录
let userMsgHistory = [];
// 下次加载用户提问索引位置
let nextUserMsgEndIndex = 0;
const chatListLoading = ref(false);
const { scrollToBottom } = useScrollToBottom({
   chatListDom: chatListDom,
});
onMounted(async () => {
   const res = await QueryHistoryDetail({
      history_group_id: currentRouteId,
   });
   userMsgHistory = res.details ?? [];
   nextUserMsgEndIndex = userMsgHistory.length;
   // 截取倒数 LOAD_CHAT_LIMIT 条用户消息
   const currentUserMsgList = userMsgHistory.slice(nextUserMsgEndIndex - LOAD_CHAT_LIMIT, nextUserMsgEndIndex);
   messageList.value = [];
   // 加载初始数据
   chatListLoading.value = true;
   const tmpMessageList = currentUserMsgList.map((item) => {
      return {
         historyId: item.history_id,
         role: RoleEnum.user,
         content: {
            type: AnswerType.Text,
            values: item.question,
         },
      } as ChatMessage;
   });
   const sectionAIdMap = new Map();
   try {
      chatListLoading.value = true;
      const resList = await Promise.all(
         (currentUserMsgList ?? []).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 = tmpMessageList[insertIndex - 1];
         userMsg.content.values = item?.answer?.question ?? userMsg.content.values;
         tmpMessageList.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++;
      });
   } finally {
   await loadRangeData().finally(() => {
      chatListLoading.value = false;
   }
   messageList.value = tmpMessageList;
   if (tmpMessageList.length === 0) {
   });
   if (messageList.value.length === 0) {
      messageContent.value = {
         type: AnswerType.Text,
         values: activeChatRoom.value.title,
@@ -469,14 +430,13 @@
      setTimeout(() => {
         // 初始状态滚一下
         scrollToBottom();
         setTimeout(() => {
            chatListDom.value.addEventListener('scroll', onChatListScroll);
         }, 300);
      }, 300);
   }
});
const { scrollToBottom } = useScrollToBottom({
   chatListDom: chatListDom,
});
//#region ====================== 关联查询 ======================
const relativeQueryClick = async (val) => {
   sendChatMessage(
@@ -524,10 +484,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>