From 1d0eec6da86e3e9bcbf002d1a22142586fe023a3 Mon Sep 17 00:00:00 2001 From: wujingjing <gersonwu@qq.com> Date: 星期二, 31 十二月 2024 11:34:15 +0800 Subject: [PATCH] 拆分组件 assistantMsg.vue --- src/components/chat/Chat.vue | 927 +++++++++++++++++++++++++++++++++++---------------------- 1 files changed, 575 insertions(+), 352 deletions(-) diff --git a/src/components/chat/Chat.vue b/src/components/chat/Chat.vue index 010fb3f..ff4ec3f 100644 --- a/src/components/chat/Chat.vue +++ b/src/components/chat/Chat.vue @@ -1,200 +1,153 @@ <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 - 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" - > - <img - class="rounded-full size-12 flex-0" - :class="{ 'mr-4': item.role === RoleEnum.assistant, 'ml-4': item.role === RoleEnum.user }" - :src="roleImageMap[item.role]" - alt="" - srcset="" - /> - <div class="flex-auto flex" :class="{ 'justify-end': item.role === RoleEnum.user }"> - <div class="inline-flex flex-col" :class="{ 'w-full': item.role === RoleEnum.assistant }"> - <div class="w-full" v-if="item.content?.values"> - <div - class="text-sm rounded-[6px] p-4 leading-relaxed" - :style="{ backgroundColor: item.role === RoleEnum.user ? 'rgb(197 224 255)' : 'white' }" - > - <div v-if="item.content.errCode === ErrorCode.Message" class="flex-column w-full"> - <p class="text-red-500"> - {{ item.content.errMsg }} - </p> - <div class="mt-5 flex items-center" v-if="showFixQuestion(item)"> - <div class="text-gray-600 flex-0"> - {{ item.content.origin.err_json.fix_question.title + '锛�' }} - </div> - <div class="ml-1 space-x-2 inline-flex flex-wrap"> - <div - v-for="fixItem in item.content.origin.err_json.fix_question?.values" - :key="fixItem" - class="bg-gray-200 p-3 hover:bg-[#c5e0ff] hover:text-[#1c86ff] cursor-pointer rounded-lg" - @click="fixQuestionClick(fixItem, item.content.origin)" - > - {{ fixItem.title }} - </div> - </div> - </div> - </div> - <template v-else> - <component :is="answerTypeMapCom[item.content.type]" :data="item.content.values" :originData="item" /> + <ChatContainer + :loading="chatListLoading" + :more-is-loading="moreIsLoading" + :is-share-page="isSharePage" + :chat-width="chatWidth" + ref="containerRef" + > + <!-- 娑堟伅鍒楄〃 --> + <template #message-list> + <template v-if="computedMessageList?.length > 0"> + <div v-for="(item, msgIndex) of computedMessageList" :key="`${item.historyId}_${item.role}`"> + <UserMsg + :msg="item" + @shareClick="shareClick" + @setCommonQuestion="setCommonQuestionClick" + v-if="item.role === RoleEnum.user" + ></UserMsg> - <div - v-if="item.role === RoleEnum.assistant && item.content.origin?.ext_call_list" - class="flex font-bold items-center mt-6" - > - <div class="flex-0 mb-auto -mr-4">鍏宠仈鍔熻兘锛�</div> - <div class="space-x-5 flex flex-wrap"> - <div - v-for="callItem in item.content.origin?.ext_call_list" - :key="callItem.call_ext_id" - @click="relativeQueryClick(callItem)" - class="cursor-pointer hover:underline first-of-type:ml-5" - > - {{ callItem.question }} - </div> - </div> - </div> - </template> - </div> - - <!-- 鎿嶄綔 --> - <div v-if="item.role === RoleEnum.assistant" class="absolute flex items-center right-0 mr-4 mt-2 space-x-2"> - <div - class="flex items-center justify-center size-[15px]" - v-if="item.content?.type === AnswerType.Text || item.content?.type === AnswerType.Knowledge" - > - <i - class="p-2 ywifont ywicon-copy cursor-pointer hover:text-[#0284ff] hover:!text-[18px]" - @click="copyClick(item)" - /> - </div> - <template v-if="item.content.errCode !== ErrorCode.Message"> - <div class="flex items-center justify-center size-[15px]"> - <i - :class="{ 'text-[#0284ff]': item.state === AnswerState.Like }" - class="p-2 ywifont ywicon-dianzan cursor-pointer hover:text-[#0284ff] font-medium hover:!text-[18px]" - @click="likeClick(item)" - /> - </div> - <div class="flex items-center justify-center size-[15px]"> - <i - :class="{ 'text-[#0284ff]': item.state === AnswerState.Unlike }" - class="p-2 ywifont ywicon-buzan cursor-pointer hover:text-[#0284ff] !text-[13px] hover:!text-[15px]" - @click="unLikeClick(item)" - /> - </div> - </template> - - <div class="flex items-center justify-center size-[15px] relative"> - <i - class="p-2 ywifont ywicon-wentifankui cursor-pointer hover:text-[#0284ff] !text-[13px] hover:!text-[15px]" - @click=" - ($event) => - feedbackClick( - $event, - item, - computedMessageList - .filter((v) => v.role === RoleEnum.assistant) - .findIndex((v) => v.historyId === item.historyId) - ) - " - /> - <FeedbackPanel - v-show="feedbackIsShow && currentFeedbackMapItem === item" - ref="feedbackPanelRef" - v-model:isShow="feedbackIsShow" - v-model:content="feedbackContent" - :chatItem="currentFeedbackMapItem" - :position="feedbackPosition" - /> - </div> - </div> - </div> - - <Loding v-if="isTalking && index === computedMessageList.length - 1" class="w-fit" :process="process" /> - </div> - </div> - </div> - <div v-if="showAskMore" class="ml-4 mt-5 text-sm"> - <div class="text-gray-600 mb-5">浣犲彲浠ョ户缁棶鎴戯細</div> - <div class="space-y-2 inline-flex flex-col"> - <div - v-for="item in computedMessageList.at(-1).content.askMoreList" - :key="item.history_id" - class="bg-white p-3 hover:bg-[#c5e0ff] hover:text-[#1c86ff] cursor-pointer rounded-lg" - @click="askMoreClick(item)" - > - {{ item.question }} - </div> + <AssistantMsg + v-else + :msg="item" + :msgList="computedMessageList" + :isLast="msgIndex === computedMessageList.length - 1" + @sendChatMessage="sendChatMessage" + @shareMsg="shareClick" + :isTalking="isTalking" + /> + </div> + <div v-if="showAskMore" class="ml-4 mt-5 pb-10"> + <div class="text-gray-600 mb-5">浣犲彲浠ョ户缁棶鎴戯細</div> + <div class="space-y-2 inline-flex flex-col"> + <div + v-for="item in computedMessageList.at(-1).content.askMoreList" + :key="item.history_id" + class="bg-white p-3 hover:bg-[#c5e0ff] hover:text-[#1c86ff] cursor-pointer rounded-lg" + @click="askMoreClick(item)" + > + {{ item.question }} </div> </div> </div> - </div> + </template> + <el-empty v-else-if="isSharePage && !chatListLoading" :image-size="200"> + <template #description> + <span class="text-[15px]">鍒嗕韩鐨勫璇濅笉瀛樺湪鎴栧凡澶辨晥</span> + </template> + </el-empty> + </template> - <div class="sticky bottom-0 w-full p-6 pb-8 bg-[rgb(247,248,250)] flex justify-center"> - <PlayBar - v-model:voicePageIsShow="voicePageIsShow" - :isTalking="isTalking" - :isHome="false" - v-model="messageContent.values" - @sendClick="sendClick" - :style="{ width: chatWidth }" - ></PlayBar> - </div> - </div> + <!-- 杈撳叆鍖哄煙 --> + <template #input-area> + <PlayBar + v-model:voicePageIsShow="voicePageIsShow" + :isTalking="isTalking" + :isHome="false" + v-model="messageContent.values" + @sendClick="sendClick" + @showUpChatClick="showUpChatClick" + @stopGenClick="stopGenClick" + @showDownChatClick="showDownChatClick" + :style="{ width: chatWidth }" + :setCommonQuestionInfo="setCommonQuestionInfo" + /> + </template> - <CustomDrawer v-model:isShow="drawerIsShow" @updateChatInput="updateChatInput" /> - </div> + <!-- 鎶藉眽 --> + <template #drawer> + <CustomDrawer v-model:isShow="drawerIsShow" @updateChatInput="updateChatInput" /> + <ShareLinkDlg ref="shareLinkDlgRef" /> + </template> + </ChatContainer> </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'; -import FeedbackPanel from './components/FeedbackPanel.vue'; -import Loding from './components/Loding.vue'; -import { useAssistantContentOpt } from './hooks/useAssistantContentOpt'; -import { useQueryProcess } from './hooks/useQueryProcess'; -import { useScrollToBottom } from './hooks/useScrollToBottom'; +import type { CancelTokenSource } from 'axios'; +import axios from 'axios'; +import { orderBy } from 'lodash-es'; +import moment from 'moment'; +import { computed, onActivated, onMounted, ref } from 'vue'; +import { loadAmisSource } from '../amis/load'; +import { convertProcessItem, convertProcessToStep, formatShowTimeYear, useScrollLoad } from './hooks/useScrollLoad'; 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 { AnswerState, AnswerType, RoleEnum, type ChatMessage } from './model/types'; +import { getShareChatJsonByPost, questionStreamByPost } from '/@/api/ai/chat'; import PlayBar from '/@/components/chat/components/playBar/PlayBar.vue'; import CustomDrawer from '/@/components/drawer/CustomDrawer.vue'; -import router from '/@/router'; -import { activeChatRoom, activeLLMId, activeSampleId, activeSectionAId, getRoomConfig, roomConfig } from '/@/stores/chatRoom'; -import { ErrorCode } from '/@/utils/request'; +import { Logger } from '/@/model/logger/Logger'; +import { ElMessage } from 'element-plus'; +import AssistantMsg from './assistant/index.vue'; +import ChatContainer from './components/ChatContainer.vue'; +import ShareLinkDlg from './components/shareLink/index.vue'; +import UserMsg from './user/index.vue'; +import router from '/@/router'; +import { + activeChatRoom, + activeGroupType, + activeLLMId, + activeRoomId, + activeSampleId, + isSharePage, + roomConfig, +} from '/@/stores/chatRoom'; +import emitter from '/@/utils/mitt'; +import { useCompRef } from '/@/utils/types'; +import { toMyFixed } from '/@/utils/util'; +const containerRef = useCompRef(ChatContainer); +const chatListDom = computed(() => containerRef.value?.chatListDom); const chatWidth = '75%'; const voicePageIsShow = ref(false); let isTalking = ref(false); + let messageContent = ref<ChatContent>({ type: AnswerType.Text, values: '', }); const currentRoute = router.currentRoute; const currentRouteId = currentRoute.value.query.id as string; -const chatListDom = ref<HTMLDivElement>(); +activeRoomId.value = currentRouteId; const messageList = ref<ChatMessage[]>([]); const computedMessageList = computed(() => { return messageList.value.filter((v) => !!v); }); -const parseContent = (res) => { +const parseExtraContent = (res) => { + if (!res) return {}; + const askMoreList = orderBy(res.context_history, [(item) => Number(item.radio)], ['desc']); + const errCode = res?.err_code; + const errMsg = res?.json_msg; + const origin = res; + + return { + askMoreList, + errCode, + errMsg, + origin, + }; +}; + +const parseContent = (res, reportIsShow = false, extraContent?) => { if (!res) return null; let content: ChatContent = { type: AnswerType.Text, values: '瑙f瀽澶辫触锛�', }; + if (res.type) { + res.answer_type = res.type; + } + const curExtraContent = parseExtraContent(res); switch (res.answer_type) { case AnswerType.RecordSet: @@ -209,6 +162,12 @@ values: res.values ?? res.answer, }; break; + case AnswerType.Script: + content = { + type: AnswerType.Script, + values: res, + }; + break; case AnswerType.Knowledge: content = { @@ -217,10 +176,22 @@ }; break; + case AnswerType.Report: + content = { + type: AnswerType.Report, + values: (res?.reports ?? []).map((item) => ({ + content: parseContent(item, reportIsShow, { origin: item, conclusion: item.conclusion ?? [] }), + })), + }; + break; + case AnswerType.Summary: content = { type: AnswerType.Summary, - values: res.summary, + values: res.summary?.map((item) => { + item.reportIsShow = reportIsShow; + return item; + }), }; break; case AnswerType.Url: @@ -242,75 +213,230 @@ }; break; } - content.askMoreList = _.orderBy(res.context_history, [(item) => Number(item.radio)], ['desc']); - content.errCode = res?.err_code; - content.errMsg = res?.json_msg; - content.origin = res; + if (!extraContent) { + content = { + ...content, + ...curExtraContent, + }; + } else { + content = { + ...content, + ...extraContent, + }; + } + return content; }; -const { clearQueryProcess, process, processId, queryProcess } = useQueryProcess(); -const DEFAULT_SECTION_A_ID = 'knowledge_base'; - let questionRes = null; +let position = null; +const preQuestion = ref(null); -let finalCalcSectionAId = null; +let lastAxiosSource: CancelTokenSource = null; const questionAi = async (text) => { - processId.value = uuidv4(); let judgeParams = null; if (!preQuestion.value) { - // const aiContent = computedMessageList.value.filter((item) => item.role === RoleEnum.assistant); - // const lastQuestion = aiContent[aiContent.length - 2]?.content?.origin?.question; - // judgeParams = lastQuestion - // ? { - // prev_question: lastQuestion, - // } - // : {}; - // 姝e父鍥炵瓟鏆傛椂涓嶉噰鐢� judgeParams = {}; } else { judgeParams = { prev_question: preQuestion.value, }; } - let currentSectionAId = ''; - if (activeSectionAId.value) { - currentSectionAId = activeSectionAId.value; - activeSectionAId.value = ''; - } else { - const lastSectionAItem = _.findLast( - computedMessageList.value as any, - (item) => item.role === RoleEnum.assistant && !!item.sectionAId - ); - currentSectionAId = lastSectionAItem?.sectionAId ?? DEFAULT_SECTION_A_ID; - } - finalCalcSectionAId = currentSectionAId; const params = { - process_id: processId.value, question: text, - // FIXME: 鏆傛椂杩欐牱 - section_a_id: currentSectionAId, history_group_id: currentRouteId, raw_mode: roomConfig.value?.[currentRouteId]?.isAnswerByLLM ?? false, ...judgeParams, } as any; + + if (position) { + const longitude = position.coords.longitude; + const latitude = position.coords.latitude; + params.cur_pos = [longitude, latitude].join(','); + } + + if (activeGroupType.value) { + params.group_type = activeGroupType.value; + } if (currentSampleId) { params.sample_id = currentSampleId; currentSampleId = ''; } - // if (currentLLMId) { - // params.llm_id = currentLLMId; - // } - clearQueryProcess(); - queryProcess(); - const res = await QuestionAi(params).finally(() => { - clearQueryProcess(); + let lastTimestamp = new Date().getTime(); + questionRes = {}; + let lastIsResult = false; + const resultP = new Promise((resolve, reject) => { + const currentSource = axios.CancelToken.source(); + lastAxiosSource = currentSource; + + const getResReport = () => { + const resReport = { + answer_type: AnswerType.Report, + reports: [], + }; + return resReport; + }; + const checkReportEmpty = () => { + const isEmpty = !questionRes?.reports || questionRes?.reports?.length === 0; + + return isEmpty; + }; + questionStreamByPost( + params, + (chunkRes) => { + Logger.info('chunk response锛歕n\n' + JSON.stringify(chunkRes)); + + if (chunkRes.mode === 'result') { + lastIsResult = true; + const res = chunkRes.value; + + if (checkReportEmpty()) { + const resReport = getResReport(); + resReport.reports.push(res); + questionRes = resReport; + resolve(resReport); + } else { + const lastMsg = computedMessageList.value.at(-1); + + // 宸茬粡瑙f瀽杩囦竴娆� reports + lastMsg.content.values.push({ + content: parseContent(res, true, { + origin: res, + }), + }); + } + return; + // chunkRes.value = '鍑嗗鏁版嵁鍒嗘瀽'; + } + + if (chunkRes.mode === 'summary') { + const lastMsg = computedMessageList.value.at(-1); + const extraContent = parseExtraContent(chunkRes.value); + const isReportEmpty = checkReportEmpty(); + // 娌℃湁缁忚繃 result 鎶ュ憡杩樻病鍒濆鍖� + if (isReportEmpty) { + const resReport = getResReport(); + questionRes = resReport; + } + // 姝ゅ璇濆凡缁忓姞鍏ュ埌瀵硅瘽鍒楄〃 + if (lastMsg.content?.values && extraContent) { + for (const key in extraContent) { + if (Object.prototype.hasOwnProperty.call(extraContent, key)) { + const value = extraContent[key]; + if (!lastMsg.content[key] || (Array.isArray(lastMsg.content[key]) && lastMsg.content[key].length === 0)) { + lastMsg.content[key] = value; + } + } + } + + lastMsg.historyId = chunkRes.value.history_id; + const userMsg = computedMessageList.value.at(-2); + userMsg.historyId = chunkRes.value.history_id; + userMsg.content.values = chunkRes.value.question; + } + + if (Object.keys(questionRes).length === 0) { + questionRes = chunkRes.value; + } + + // 姝ゅ璇濊繕鏈姞鍏ュ埌瀵硅瘽鍒楄〃 + if (!lastMsg.content?.values && questionRes) { + questionRes = { + ...questionRes, + ...chunkRes.value, + }; + } + + if (isReportEmpty) { + resolve(questionRes); + } + // computedMessageList.value[computedMessageList.value.length - 1] = finalMsg; + scrollToBottom(); + // chunkRes.value = '浣犲彲浠ョ户缁棶鎴�'; + return; + } + + if (chunkRes.mode === 'conclusion') { + const lastReport = computedMessageList.value.at(-1)?.content?.values?.at(-1); + if (lastReport) { + lastReport.conclusion = chunkRes.value; + chunkRes.value = '鍒嗘瀽缁撴潫'; + } + } + + if (chunkRes.mode === 'question') { + const lastGroup = computedMessageList.value.at(-1).stepGroup.at(-1); + const stepList = lastGroup?.value ?? []; + const lastStepItem = stepList.at(-1); + if (!lastStepItem.subStep) { + lastStepItem.subStep = []; + } + lastStepItem.subStep.push({ + type: chunkRes.value.type, + data: chunkRes.value, + }); + scrollToBottom(); + return; + } + // 鏆傛椂涓嶈�冭檻澶氫釜 report鎯呭喌 + + // if (lastIsResult && chunkRes.mode !== 'finish') { + // // 寮�濮嬪鍔犳柊鐨� stepGroup + // computedMessageList.value.at(-1).stepGroup.push({ + // value: [], + // isShow: true, + // }); + // lastIsResult = false; + // } + const lastGroup = computedMessageList.value.at(-1).stepGroup.at(-1); + const stepList = lastGroup?.value ?? []; + const currentTimeStamp = new Date().getTime(); + const ms = toMyFixed(currentTimeStamp - lastTimestamp, 2) + ' ms'; + if (chunkRes.mode === 'finish') { + stepList.at(-1).ms = ms; + isTalking.value = false; + + return; + } + + if (stepList?.length >= 1) { + stepList.at(-1).ms = ms; + } else { + const stepGroup = computedMessageList.value.at(-1).stepGroup; + if (stepGroup.length > 1) { + const lastStepList = stepGroup.at(-2).value; + lastStepList.at(-1).ms = ms; + } + } + lastTimestamp = currentTimeStamp; + const stepItem = convertProcessItem(chunkRes); + + stepList.push(stepItem); + // 寮哄埗瑙﹀彂鏇存柊 + + scrollToBottom(); + }, + { + cancelToken: currentSource.token, + } + ) + .catch((err) => { + throw err; + }) + .finally(() => { + isTalking.value = false; + // 鏀惰捣鎵�鏈� stepGroup + computedMessageList.value.at(-1).stepGroup.forEach((item) => { + item.isShow = false; + }); + }); }); - questionRes = res; - const content = parseContent(res); + + await resultP; + const content = parseContent(questionRes, true); return content; }; @@ -324,14 +450,55 @@ let currentLLMId = null; -const getAnswerById = async (historyId: string) => { - return await GetHistoryAnswer({ - history_id: historyId, - }); +const stopGenClick = () => { + lastAxiosSource?.cancel(); + isTalking.value = false; + chatListLoading.value = false; + + computedMessageList.value.at(-1).isStopMsg = true; }; -const sendChatMessage = async (content: ChatContent = messageContent.value, cb?: any, isCallExtParams?: any) => { - if (!content?.values || isTalking.value) return; +const checkCanSend = (content: ChatContent = messageContent.value) => { + if (!content?.values) { + return false; + } + if (isTalking.value || chatListLoading.value) { + ElMessage.warning('ai 姝e湪鍥炲涓紝璇风◢鍚庡皾璇曟彁闂�'); + return false; + } + return true; +}; + +const addChatItem = (content: ChatContent) => { + isTalking.value = true; + const userItem: ChatMessage = { role: RoleEnum.user, content, isChecked: false } as any; + const assistantItem: ChatMessage = { + role: RoleEnum.assistant, + content: { + type: AnswerType.Report, + }, + state: AnswerState.Null, + stepGroup: [ + { + value: [], + isShow: true, + }, + ], + isStopMsg: false, + isChecked: false, + } as any; + messageList.value.push(userItem); + clearMessageContent(); + + messageList.value.push(assistantItem); + scrollToBottom(); + return [userItem, assistantItem]; +}; + +const sendChatMessage = async (content: ChatContent = messageContent.value) => { + if (!checkCanSend(content)) { + return; + } const isNewChat = messageList.value.length === 0; if (isNewChat) { if (activeSampleId.value) { @@ -345,160 +512,171 @@ let resMsgContent: ChatContent = null; try { - isTalking.value = true; - const userItem: ChatMessage = { role: RoleEnum.user, content } as any; - const assistantItem: ChatMessage = { role: RoleEnum.assistant, content: null, state: AnswerState.Null } as any; - // 鍙戦�佸綋鍓� - messageList.value.push(userItem); - // 娓呯┖杈撳叆妗� - clearMessageContent(); + const [userItem, assistantItem] = addChatItem(content); - // 鍑虹幇鍥炲锛岀疆绌哄嚭鐜扮瓑寰呭姩鐢� - messageList.value.push(assistantItem); - if (isCallExtParams) { - const extRes = await extCallQuery(isCallExtParams); - questionRes = extRes; - resMsgContent = parseContent(extRes); - } else { - resMsgContent = await questionAi(content.values); - } + resMsgContent = await questionAi(content.values); - if (isNewChat) { - const firstResCb = getRoomConfig(currentRouteId, 'firstResCb'); - firstResCb?.(resMsgContent); - } else { - cb?.(resMsgContent); - } - userItem.historyId = questionRes.history_id; + updateLoadIndex(); + + userItem.historyId = questionRes?.history_id; userItem.content.values = questionRes?.question ?? userItem.content.values; - assistantItem.historyId = questionRes.history_id; - assistantItem.sectionAId = finalCalcSectionAId; - appendLastMessageContent(resMsgContent); - } catch (error: any) { - // appendLastMessageContent({ - // type: AnswerType.Text, - // values: '鍙戠敓閿欒锛�', - // }); - } finally { - isTalking.value = false; + assistantItem.historyId = questionRes?.history_id; + const currentTime = formatShowTimeYear(moment().format('YYYY-MM-DD HH:mm:ss')); + assistantItem.createTime = currentTime; + assistantItem.content = resMsgContent; + setTimeout(() => { + // 鏀跺埌鍥炲锛岀户缁粴 + scrollToBottom(); + }, 300); + } catch (error: any) {} +}; + +const sendClick = () => { + sendChatMessage(messageContent.value); +}; + +const { loadRangeData, onChatListScroll, moreIsLoading, updateLoadIndex } = useScrollLoad({ + container: chatListDom, + historyGroupId: currentRouteId, + messageList, + parseAnswerContent: parseContent, +}); + +const chatListLoading = ref(true); + +onActivated(() => { + emitter.emit('updateHeaderTitle', activeChatRoom.value?.title ?? ''); +}); + +const initNewChat = () => { + messageContent.value = { + type: AnswerType.Text, + values: activeChatRoom.value?.title, + }; + sendChatMessage(); +}; +const scrollToBottom = () => { + containerRef.value?.scrollToBottom(); +}; + +const initHistoryChat = () => { + // 鍒濆鐘舵�佹粴涓�涓� + scrollToBottom(); + + setTimeout(() => { + chatListDom.value.addEventListener('scroll', onChatListScroll); + }, 300); +}; + +/** + * 鍔犺浇鍒嗕韩鏁版嵁 + */ +const loadShareData = async () => { + const res = await getShareChatJsonByPost({ + share_id: router.currentRoute.value.query.id as string, + }); + + const msgValue = res?.values; + if (!msgValue) { + messageList.value = []; + return; } -}; + const userMsg: ChatMessage = { + historyId: msgValue.history_id, + role: RoleEnum.user, + content: { + type: AnswerType.Text, + values: msgValue.question, + }, + isChecked: false, + }; -const sendClick = (cb) => { - sendChatMessage(messageContent.value, cb); -}; -const appendLastMessageContent = (content: ChatContent) => { - if (messageList.value.at(-1)) { - messageList.value.at(-1).content = content; - } -}; + const assistantMsg: ChatMessage = { + historyId: msgValue.history_id, + role: RoleEnum.assistant, + content: parseContent(msgValue), + stepGroup: (msgValue?.reports ?? []).map((item) => ({ + value: convertProcessToStep(item?.exec_process), + isShow: false, + })), + isStopMsg: false, -// 涓�娆℃�у姞杞芥渶杩戞潯鏁伴檺鍒� -const LOAD_CHAT_LIMIT = 10; - -// 鎵�鏈夌敤鎴锋彁闂巻鍙茶褰� -let userMsgHistory = []; -// 涓嬫鍔犺浇鐢ㄦ埛鎻愰棶绱㈠紩浣嶇疆 -let nextUserMsgEndIndex = 0; + conclusion: msgValue.conclusion ?? [], + isChecked: false, + }; + messageList.value = [userMsg, assistantMsg]; +}; 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 = currentUserMsgList.map((item) => { - return { - historyId: item.history_id, - role: RoleEnum.user, - content: { - type: AnswerType.Text, - values: item.question, - }, - } as ChatMessage; - }); - const sectionAIdMap = new Map(); - - // 鑾峰彇缁撴灉鎻掑叆鍒扮敤鎴锋彁闂箣鍚� - currentUserMsgList.map((item) => { - sectionAIdMap.set(item.history_id, item.section_a_id); - getAnswerById(item.history_id).then((aiRobot) => { - // 鐢ㄦ埛鎻愰棶绱㈠紩 - const userMsgIndex = messageList.value.findIndex((subItem) => subItem.historyId === item.history_id); - const userMsg = messageList.value[userMsgIndex]; - // values 鍙栧洖绛斾箣鍚庢渶缁堢殑 question - userMsg.content.values = aiRobot.answer?.question ?? userMsg.content.values; - messageList.value.splice( - userMsgIndex + 1, - 0, - aiRobot.answer === null - ? null - : { - historyId: aiRobot.answer?.history_id, - role: RoleEnum.assistant, - content: parseContent(aiRobot.answer), - state: aiRobot.answer_state, - sectionAId: sectionAIdMap.get(aiRobot.answer.history_id), - } - ); + messageList.value = []; + chatListLoading.value = true; + if (isSharePage.value) { + await loadShareData().finally(() => { + chatListLoading.value = false; }); - }); + } else { + await loadRangeData().finally(() => { + chatListLoading.value = false; + }); + } + setTimeout(() => { + emitter.emit('updateHeaderTitle', activeChatRoom.value?.title ?? ''); + }, 300); if (messageList.value.length === 0) { - messageContent.value = { - type: AnswerType.Text, - values: activeChatRoom.value.title, - }; - - sendChatMessage(); - } -}); - -const { forbidScroll } = useScrollToBottom({ - chatListDom: chatListDom, - displayMessageList: computedMessageList, -}); - -//#region ====================== 鍏宠仈鏌ヨ ====================== -const relativeQueryClick = async (val) => { - sendChatMessage( - { - type: AnswerType.Text, - values: val.question, - }, - undefined, - { - history_group_id: currentRouteId, - question: val.question, - call_ext_id: val.call_ext_id, - call_ext_args: val.agrs ? JSON.stringify(val.agrs) : null, + initNewChat(); + } else { + if (!isSharePage.value) { + setTimeout(() => { + initHistoryChat(); + }, 300); } - ); + } + loadAmisSource(); +}); + +//#region ====================== 鍏夋爣杈撳叆涓婁笅绠ご鏄剧ず鍘嗗彶娑堟伅 ====================== +const currentIndex = ref(null); +const history_data = computed(() => { + return computedMessageList.value.filter((item) => item.role === RoleEnum.user); +}); +//鏄剧ず涓婁竴鏉℃秷鎭� +const showUpChatClick = () => { + if (computedMessageList.value.length === 0) return; + if (currentIndex.value == 0) { + messageContent.value.values = history_data.value[currentIndex.value].content.values; + return; + } else { + currentIndex.value = (currentIndex.value + history_data.value.length - 1) % history_data.value.length; + } + messageContent.value.values = history_data.value[currentIndex.value].content.values; +}; +//鏄剧ず涓嬩竴鏉℃秷鎭� +const showDownChatClick = () => { + if (computedMessageList.value.length === 0) return; + if (currentIndex.value == history_data.value.length - 1) { + messageContent.value.values = history_data.value[currentIndex.value].content.values; + return; + } + if (currentIndex.value === null) { + currentIndex.value = 0; + } else { + currentIndex.value = (currentIndex.value + 1) % history_data.value.length; + } + messageContent.value.values = history_data.value[currentIndex.value].content.values; }; //#endregion - -const { - copyClick, - likeClick, - unLikeClick, - feedbackPosition, - feedbackIsShow, - feedbackContent, - feedbackPanelRef, - currentFeedbackMapItem, - feedbackClick, - askMoreClick, - fixQuestionClick, - preQuestion, - showFixQuestion, - showAskMore, -} = useAssistantContentOpt({ - forbidScroll, - sendChatMessage, - displayMessageList: computedMessageList, +const showAskMore = computed(() => { + if (!computedMessageList.value || computedMessageList.value.length === 0) return false; + const last = computedMessageList.value.at(-1); + const isShow = last?.role === RoleEnum.assistant && last?.content?.values && last.content?.askMoreList?.length > 0; + const result = isShow && !isSharePage.value; + return result; }); +const askMoreClick = (item) => { + if (!item.question) return; + sendChatMessage({ type: AnswerType.Text, values: item.question }); +}; //#region ====================== 渚ц竟鏍廳rawer ====================== const drawerIsShow = ref(false); @@ -507,12 +685,57 @@ messageContent.value.values = content; }; //#endregion +//#region ====================== 鐢ㄦ埛璇㈤棶鐨勯棶棰樿缃负甯哥敤璇� ====================== +const setCommonQuestionInfo = ref({}); + +//鐢ㄦ埛闂璁剧疆涓哄父鐢ㄨ +const setCommonQuestionClick = (item) => { + setCommonQuestionInfo.value = item; +}; +//#endregion + +//#region ====================== 鍒嗕韩 ====================== + +const shareLinkDlgRef = useCompRef(ShareLinkDlg); + +const shareClick = async (item: ChatMessage) => { + shareLinkDlgRef.value.openShare(item); +}; +//#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); + } + } +} + +:deep(.el-step__icon.is-text) { + --radius-size: 24px; + width: var(--radius-size); + height: var((--radius-size)); +} + +:deep(.el-step__icon-inner) { + font-size: 16px !important; +} +:deep(.el-step__description) { + min-height: 20px; +} + +:deep(.el-step:last-of-type .el-step__description) { + // display: none; +} </style> -- Gitblit v1.9.3