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 | 876 +++++++++++++++++++++++++++++++++++++++++----------------- 1 files changed, 621 insertions(+), 255 deletions(-) diff --git a/src/components/chat/Chat.vue b/src/components/chat/Chat.vue index 7fd8daf..ff4ec3f 100644 --- a/src/components/chat/Chat.vue +++ b/src/components/chat/Chat.vue @@ -1,81 +1,154 @@ <template> - <div class="flex flex-col h-full"> - <div class="h-full flex flex-col items-center overflow-y-auto"> - <div ref="chatListDom" class="h-full w-[100ch]"> - <div class="group flex px-4 py-4 hover:bg-slate-100 rounded-lg" v-for="(item, index) of computedMessageList" :key="index"> - <img class="rounded-full size-12 mr-4" :src="roleImageMap[item.role]" alt="" srcset="" /> + <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 class="flex"> - <div class="relative" v-if="item.content?.values"> - <div class="text-sm rounded-[6px] p-4 leading-relaxed max-w-[100ch] bg-white"> - <component class="max-w-[100ch]" :is="answerTypeMapCom[item.content.type]" :data="item.content.values" /> - </div> - - <div v-if="item.role === RoleEnum.assistant" class="absolute flex items-center right-0 mr-2 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 ywicon icon-copy cursor-pointer hover:text-[#0284ff] hover:!text-[18px]" @click="copyClick(item)" /> - </div> - <div class="flex items-center justify-center size-[15px]"> - <i - :class="{ 'text-[#0284ff]': item.state === AnswerState.Like }" - class="p-2 ywicon icon-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 ywicon icon-buzan cursor-pointer hover:text-[#0284ff] !text-[13px] hover:!text-[15px]" - @click="unLikeClick(item)" - /> - </div> - </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> - - <Loding v-else :process="process" /> </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-gray-100 flex justify-center"> - <PlayBar :isTalking="isTalking" v-model="messageContent.values" @sendClick="sendChatMessage" /> - </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> + + <!-- 鎶藉眽 --> + <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 { computed, nextTick, ref, watch } from 'vue'; -import useClipboard from 'vue-clipboard3'; -import Loding from './components/Loding.vue'; +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 { AnswerType, RoleEnum, answerTypeMapCom, roleImageMap, type ChatMessage, AnswerState } from './model/types'; -import { GetHistoryAnswer, QueryHistoryDetail, QuestionAi, SetHistoryAnswerState, getQuestionProcess } 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 router from '/@/router'; -import { activeChatRoom, activeLLMId, activeRoomId, activeSampleId, activeSectionAId } from '/@/stores/chatRoom'; -import { v4 as uuidv4 } from 'uuid'; +import CustomDrawer from '/@/components/drawer/CustomDrawer.vue'; +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 chatListDom = ref<HTMLDivElement>(); +const currentRoute = router.currentRoute; +const currentRouteId = currentRoute.value.query.id as string; +activeRoomId.value = currentRouteId; const messageList = ref<ChatMessage[]>([]); const computedMessageList = computed(() => { - return messageList.value.filter((v) => v.role !== RoleEnum.system); + 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: '鍙戠敓閿欒锛�', + values: '瑙f瀽澶辫触锛�', }; + if (res.type) { + res.answer_type = res.type; + } + const curExtraContent = parseExtraContent(res); + switch (res.answer_type) { case AnswerType.RecordSet: content = { @@ -89,6 +162,12 @@ values: res.values ?? res.answer, }; break; + case AnswerType.Script: + content = { + type: AnswerType.Script, + values: res, + }; + break; case AnswerType.Knowledge: content = { @@ -97,96 +176,267 @@ }; 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: + content = { + type: AnswerType.Url, + values: res.url, + }; + break; + case AnswerType.Map: + content = { + type: AnswerType.Map, + values: res.values, }; break; default: content = { type: AnswerType.Text, - values: '鍙戠敓閿欒锛�', + values: '瑙f瀽澶辫触锛�', }; break; } + if (!extraContent) { + content = { + ...content, + ...curExtraContent, + }; + } else { + content = { + ...content, + ...extraContent, + }; + } + return content; }; -//#region ====================== 鏌ヨ杩涘害 ====================== -let processId = ''; -const QUERY_PROCESS_INTERVAL = 1000; -const process = ref(''); -let processTimer = null; -let finishProcess = true; - -const queryProcessApi = async () => { - const res = await getQuestionProcess({ - process_id: processId, - }).catch((err) => { - process.value = err; - }); - - process.value = res.process; - finishProcess = true; -}; - -const queryProcess = () => { - processTimer = setInterval(() => { - if (!finishProcess) return; - finishProcess = false; - queryProcessApi(); - }, QUERY_PROCESS_INTERVAL); -}; - -const clearQueryProcess = () => { - process.value = ''; - clearInterval(processTimer); -}; - -//#endregion let questionRes = null; +let position = null; +const preQuestion = ref(null); + +let lastAxiosSource: CancelTokenSource = null; const questionAi = async (text) => { - if (!currentSectionId) { - ElMessage.warning('鍙戦�佸け璐ワ紝鏈‘瀹氬簲鐢ㄥ満鏅紒'); + let judgeParams = null; + if (!preQuestion.value) { + judgeParams = {}; + } else { + judgeParams = { + prev_question: preQuestion.value, + }; } - processId = uuidv4(); + const params = { - process_id: processId, question: text, - // FIXME: 鏆傛椂杩欐牱 - section_a_id: currentSectionId, - history_group_id: activeRoomId.value, + 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); - clearQueryProcess(); - questionRes = res; - // const res = { - // json_ok: true, - // question: '鏄ㄦ棩浜斾竴骞垮満鍘嬪姏', - // answer_type: 'recordset', - // values: { - // names: ['yesterday', 'max_pressure'], - // values: [ - // ['2024-06-28 00:00:00', 24.378], - // ['2024-06-29 00:00:00', 24.276], - // ], - // type: 'records', - // title: '鏄ㄦ棩浜斾竴骞垮満(D_GW_04)鐨勬渶澶у帇鍔涘��', - // }, - // }; + let lastTimestamp = new Date().getTime(); + questionRes = {}; + let lastIsResult = false; + const resultP = new Promise((resolve, reject) => { + const currentSource = axios.CancelToken.source(); + lastAxiosSource = currentSource; - const content = parseContent(res); + 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; + }); + }); + }); + + await resultP; + const content = parseContent(questionRes, true); return content; }; @@ -196,24 +446,61 @@ values: '', }); -const scrollToBottom = () => { - if (!chatListDom.value) return; - chatListDom.value.lastElementChild?.scrollIntoView(); -}; -let currentSectionId = null; -let currentSampleId = null; +let currentSampleId = ''; 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 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 (!messageContent.value?.values) return; - if (activeChatRoom.value.isInitial) { - activeChatRoom.value.title = messageContent.value.values; - activeChatRoom.value.isInitial = false; + if (!checkCanSend(content)) { + return; + } + const isNewChat = messageList.value.length === 0; + if (isNewChat) { if (activeSampleId.value) { currentSampleId = activeSampleId.value; } @@ -221,155 +508,234 @@ if (activeLLMId.value) { currentLLMId = activeLLMId.value; } - - if (activeSectionAId.value) { - currentSectionId = activeSectionAId.value; - } } + 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); - - let resMsgContent: ChatContent = null; resMsgContent = await questionAi(content.values); - userItem.historyId = questionRes.history_id; - assistantItem.historyId = questionRes.history_id; - appendLastMessageContent(resMsgContent); - } catch (error: any) { - appendLastMessageContent({ - type: AnswerType.Text, - values: '鍙戠敓閿欒锛�', - }); - } finally { - isTalking.value = false; - } -}; -const appendLastMessageContent = (content: ChatContent) => { - if (messageList.value.at(-1)) { - messageList.value.at(-1).content = content; - } + + updateLoadIndex(); + + userItem.historyId = questionRes?.history_id; + userItem.content.values = questionRes?.question ?? userItem.content.values; + 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) {} }; -watch( - () => activeRoomId.value, - async (val) => { - if (!val) { - router.replace({ - name: 'Home', - }); - return; - } - if (activeChatRoom.value.isInitial) { - messageContent.value = { - type: AnswerType.Text, - values: activeChatRoom.value.title, - }; - sendChatMessage(); - } else { - const res = await QueryHistoryDetail({ - history_group_id: activeRoomId.value, - }); - messageList.value = (res.details ?? []).map((item) => { - return { - historyId: item.history_id, - role: RoleEnum.user, - content: { - type: AnswerType.Text, - values: item.question, - }, - } as ChatMessage; - }); - currentSectionId = res?.details?.[0]?.section_a_id; - currentSampleId = res?.details?.[0]?.sample_id; - const resList = await Promise.all((res.details ?? []).map((item) => getAnswerById(item.history_id))); - let i = 0; +const sendClick = () => { + sendChatMessage(messageContent.value); +}; - resList.map((item, index) => { - const insertIndex = index + 1 + i; - messageList.value.splice(insertIndex, 0, { - historyId: item.answer.history_id, - role: RoleEnum.assistant, - content: parseContent(item.answer), - state: item.answer_state, - }); - i++; - }); - } - }, - { - immediate: true, - } -); - -let forbidScroll = false; -watch( +const { loadRangeData, onChatListScroll, moreIsLoading, updateLoadIndex } = useScrollLoad({ + container: chatListDom, + historyGroupId: currentRouteId, messageList, - () => { - if (forbidScroll) return; - nextTick(() => scrollToBottom()); - }, - { - deep: true, + 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, + }; -//#region ====================== 鑱婂ぉ鍐呭鎿嶄綔 ====================== + 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 { toClipboard } = useClipboard(); + conclusion: msgValue.conclusion ?? [], + isChecked: false, + }; + messageList.value = [userMsg, assistantMsg]; +}; -const copyClick = (item) => { - const type = item.content.type; - let text = ''; - if (type === AnswerType.Knowledge) { - text = item.content.values?.map((item) => item.answer).join('\n\n') ?? ''; +onMounted(async () => { + messageList.value = []; + chatListLoading.value = true; + if (isSharePage.value) { + await loadShareData().finally(() => { + chatListLoading.value = false; + }); } else { - text = item.content.values; + await loadRangeData().finally(() => { + chatListLoading.value = false; + }); } - ElMessage.success('澶嶅埗鎴愬姛'); - toClipboard(text); + setTimeout(() => { + emitter.emit('updateHeaderTitle', activeChatRoom.value?.title ?? ''); + }, 300); + + if (messageList.value.length === 0) { + 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 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 }); }; -const likeClick = async (item) => { - const toSetState = item.state === AnswerState.Like ? AnswerState.Null : AnswerState.Like; - const res = await SetHistoryAnswerState({ - history_id: item.historyId, - answer_state: toSetState, - }); - item.state = toSetState; - forbidScroll = true; - nextTick(() => { - forbidScroll = false; - }); +//#region ====================== 渚ц竟鏍廳rawer ====================== +const drawerIsShow = ref(false); + +const updateChatInput = (content) => { + messageContent.value.values = content; }; +//#endregion +//#region ====================== 鐢ㄦ埛璇㈤棶鐨勯棶棰樿缃负甯哥敤璇� ====================== +const setCommonQuestionInfo = ref({}); -const unLikeClick = async (item) => { - const toSetState = item.state === AnswerState.Unlike ? AnswerState.Null : AnswerState.Unlike; - const res = await SetHistoryAnswerState({ - history_id: item.historyId, - answer_state: toSetState, - }); - item.state = toSetState; +//鐢ㄦ埛闂璁剧疆涓哄父鐢ㄨ +const setCommonQuestionClick = (item) => { + setCommonQuestionInfo.value = item; +}; +//#endregion - forbidScroll = true; - nextTick(() => { - forbidScroll = false; - }); +//#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