From 1a20b21d285bc008f6a45fad132bc808a377f853 Mon Sep 17 00:00:00 2001 From: wujingjing <gersonwu@qq.com> Date: 星期日, 19 一月 2025 11:21:31 +0800 Subject: [PATCH] 对话修改 --- src/components/chat/smallChat/index.vue | 163 +++++++++++++++++++++++++----------------------------- 1 files changed, 76 insertions(+), 87 deletions(-) diff --git a/src/components/chat/smallChat/index.vue b/src/components/chat/smallChat/index.vue index e40d41a..10bb079 100644 --- a/src/components/chat/smallChat/index.vue +++ b/src/components/chat/smallChat/index.vue @@ -1,18 +1,9 @@ <template> - <div class="relative"> - <el-tooltip v-if="!isOpen" content="浣犲ソ锛屾垜鏄疉I鍔╃悊锛屽彲浠ヨВ绛旈棶棰樸�佹帹鑽愯В鍐虫柟妗堢瓑" placement="right"> - <div - class="flex flex-col items-center gap-1 cursor-pointer bg-white rounded-lg shadow-lg p-1 absolute bottom-4 right-4 opacity-80" - @click="openChat" - > - <img :src="assistantPic" class="size-10" alt="AI澶村儚" /> - <span class="text-lg text-center w-5">AI鍔╂墜</span> - </div> - </el-tooltip> - <div v-else class="bg-white rounded-lg shadow-lg flex flex-col w-[400px] h-[600px] absolute bottom-4 right-4"> + <div ref="chatContainerRef" :style="chatContainerStyle" class="opacity-90 small-chat-container" @mousedown="startDrag"> + <div class="bg-white rounded-lg shadow-lg flex flex-col w-[400px] max-h-[600px] absolute bottom-4 right-4"> <!-- 澶撮儴 --> - <div class="h-12 flex items-center justify-between px-4 border-b"> - <div class="text-base font-medium">WI姘村姟鏅鸿兘鍔╃悊</div> + <div ref="chatHeaderRef" :style="handleStyle" class="small-chat-header h-12 flex items-center justify-between px-4 border-b"> + <div class="text-lg font-medium">WI姘村姟鏅鸿兘鍔╃悊</div> <div class="flex items-center gap-2"> <!-- <el-icon class="cursor-pointer text-gray-400 hover:text-gray-600"> <Refresh /> @@ -23,14 +14,14 @@ <el-icon class="cursor-pointer text-gray-400 hover:text-gray-600"> <Star /> </el-icon> --> - <el-icon class="cursor-pointer text-gray-400 hover:text-gray-600" @click="closeChat"> + <!-- <el-icon class="cursor-pointer text-gray-400 hover:text-gray-600" @click="closeChat"> <Close /> - </el-icon> + </el-icon> --> </div> </div> <!-- 鍐呭鍖� --> - <div class="flex-1 overflow-y-auto p-4"> + <div class="flex-1 overflow-y-auto p-4" ref="chatContentRef" v-show="showHistory"> <div v-if="isInit"> <!-- 娆㈣繋璇� --> <div class="flex flex-col items-center gap-4 mt-8"> @@ -48,10 +39,6 @@ > {{ item }} </div> - <!-- <div class="cursor-pointer hover:bg-gray-50 p-3 rounded-lg border border-solid border-gray-400"> - 闃块噷浜戜骇鍝佹�庝箞璐拱,閲嶇偣鏈夊摢浜涘姛鑳�? - </div> - <div class="cursor-pointer hover:bg-gray-50 p-3 rounded-lg border border-solid border-gray-400">AI 鍔╃悊鑳戒负鎴戝仛浠�涔�?</div> --> </div> </div> <div v-else class="flex flex-col gap-4"> @@ -83,108 +70,113 @@ </div> <!-- 搴曢儴杈撳叆妗� --> - <div class="p-4 border-t"> - <ChatInput v-model="inputText" @sendClick="sendClick" /> + <div class="p-2 border-t"> + <ChatInput v-model="inputText" @sendClick="sendClick" @toggleHistory="toggleHistory" :showHistory="showHistory" /> </div> </div> </div> </template> <script setup lang="ts" name="smallChat"> -import { computed, onMounted, ref } from 'vue'; +import { computed, nextTick, onMounted, ref } from 'vue'; import ChatInput from './ChatInput.vue'; import type { ChatMessage } from './types'; import { AssistantContent } from './types'; -import { agentStreamByPost, question_stream_reply } from '/@/api/ai/chat'; +import { agentStreamByPost } from '/@/api/ai/chat'; import { Logger } from '/@/model/logger/Logger'; import type { OLMap } from '/@/model/map/OLMap'; import assistantPic from '/static/images/role/assistant-200x192.png'; import userPic from '/static/images/role/user-200x206.png'; +import { useDrag } from '/@/hooks/useDrag'; const props = defineProps<{ olMap?: OLMap; }>(); const isOpen = ref(false); const inputText = ref(''); +/** @description 瀵硅瘽瀹屾垚鍓嶆槸鍚︽椂鍒濆鐘舵�� */ +let lastIsInit = true; const closeChat = () => { isOpen.value = false; }; +const chatHeaderRef = ref<HTMLDivElement>(null); + +const chatContainerRef = ref<HTMLDivElement>(null); + +const { startDrag, style: chatContainerStyle, handleStyle } = useDrag({ + handle: chatHeaderRef, +}); + const historyMessages = ref<ChatMessage[]>([]); const isInit = computed(() => historyMessages.value.length === 0); -const initQuestionList = ref(null); +const initQuestionList = ref(['鏀惧ぇ', '缂╁皬']); +const chatContentRef = ref<HTMLDivElement>(null); + const getLastAssistantMessage = () => { const last = historyMessages.value[historyMessages.value.length - 1]; const result = last.role === 'assistant' ? last : null; return result as ChatMessage<AssistantContent>; }; -let streamIsOpen = false; -const startStream = () => { + +const startStream = (question: string) => { + if (lastIsInit) { + showHistory.value = false; + } + let haveMapOperate = false; agentStreamByPost( { + question: question, agent_id: 'a_019471cdb0667a83956b76ac97283f1c', }, (chunkRes) => { - if (!streamIsOpen) { - streamIsOpen = true; - } Logger.info('agent stream response锛歕n\n' + JSON.stringify(chunkRes)); - if (chunkRes.mode === 'question') { - if (chunkRes.type === 'json') { - if (!initQuestionList.value) { - initQuestionList.value = chunkRes.value?.options ?? []; - } - activeQuestionChunk = chunkRes; - } - } + if (chunkRes.mode === 'map') { + haveMapOperate = true; if (chunkRes.type === 'string') { const jsonData = JSON.parse(chunkRes.value); handleMapCommand(jsonData); } } - if (chunkRes.mode === 'finish') { - isOpen.value = false; - streamIsOpen = false; - const last = getLastAssistantMessage(); - if (last) { - last.content.value = '宸查��鍑�'; - last.content.isLoading = false; + if (!haveMapOperate) { + refreshAssistantMessage({ value: `鏈瘑鍒埌鎿嶄綔锛�${question}` }); } } } - ); + ).catch((error) => { + Logger.error('agent stream error锛歕n\n' + error); + refreshAssistantMessage(); + }); +}; + +const refreshAssistantMessage = (content: Partial<AssistantContent> = { value: 'AI鍥炵瓟澶辫触' }) => { + const last = getLastAssistantMessage(); + if (last) { + last.content.value = content.value; + last.content.isLoading = content.isLoading; + } }; const handleMapCommand = (command: any) => { if (!command) return; - const last = getLastAssistantMessage(); - if (last) { - last.content.value = `宸叉墽琛屾搷浣�: ${command.operate}`; - switch (command.operate) { - case '鏀惧ぇ': - props.olMap.zoomIn(); - break; + switch (command.operate) { + case '鏀惧ぇ': + props.olMap.zoomIn(); + break; - case '缂╁皬': - props.olMap.zoomOut(); - break; - } - last.content.isLoading = false; + case '缂╁皬': + props.olMap.zoomOut(); + break; } + refreshAssistantMessage({ value: `宸叉墽琛屾搷浣�: ${command.operate}` }); }; const scrollToBottom = () => { - const chatContainer = document.querySelector('.chat-container'); - if (chatContainer) { - chatContainer.scrollTop = chatContainer.scrollHeight; + if (chatContentRef.value) { + chatContentRef.value.scrollTop = chatContentRef.value.scrollHeight; } }; -const openChat = () => { - isOpen.value = true; - if (!streamIsOpen) { - startStream(); - } -}; + const applyMessage = () => { const time = Date.now().toString(); const userMessageId = `user-${time}`; @@ -206,44 +198,41 @@ }, }; historyMessages.value.push(assistantMessage); + + nextTick(() => { + scrollToBottom(); + }); const userReactive = historyMessages.value.find((item) => item.id === userMessageId); const assistantReactive = historyMessages.value.find((item) => item.id === assistantMessageId); return [userReactive, assistantReactive]; }; -const resetActive = () => { - activeQuestionChunk = null; -}; - -const refreshMessageList = () => { - historyMessages.value = historyMessages.value.concat([]); -}; const sendClick = async () => { - if (!activeQuestionChunk) return; if (!inputText.value.trim()) return; + lastIsInit = isInit.value; const lastMessage = historyMessages.value[historyMessages.value.length - 1]; if (lastMessage && lastMessage.role === 'assistant' && (lastMessage.content as AssistantContent).isLoading) return; const [userMessage, assistantMessage] = applyMessage(); const question = inputText.value; inputText.value = ''; - - const assistantContent = assistantMessage.content as AssistantContent; - const res = await question_stream_reply({ - select: question, - reply_id: activeQuestionChunk.value.reply_id, - }).catch(() => { - assistantContent.isLoading = false; - assistantContent.value = 'AI鍥炵瓟澶辫触'; - }); - resetActive(); + + startStream(question); }; const handleQuestionClick = (item: string) => { inputText.value = item; sendClick(); }; -const isFinish = ref(false); -let activeQuestionChunk = null; + +//#region ====================== 鍘嗗彶鏄剧ず鎺у埗 ====================== +const showHistory = ref(true); +const toggleHistory = () => { + showHistory.value = !showHistory.value; + nextTick(() => { + scrollToBottom(); + }); +}; +//#endregion onMounted(() => {}); </script> -- Gitblit v1.9.3