From c1b9baf8b645e16006ce57d22f8da770d2429bc4 Mon Sep 17 00:00:00 2001 From: cherishsince Date: Sun, 12 May 2024 22:22:34 +0800 Subject: [PATCH] =?UTF-8?q?=E3=80=90=E5=A2=9E=E5=8A=A0=E3=80=91stream=20?= =?UTF-8?q?=E8=81=8A=E5=A4=A9=E8=BF=9B=E8=A1=8C=E4=B8=AD=E5=88=A0=E9=99=A4?= =?UTF-8?q?=EF=BC=8C=E8=B0=83=E7=94=A8=20stream=20=E6=8E=A7=E5=88=B6?= =?UTF-8?q?=E5=99=A8=E4=B8=AD=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/ai/chat/index.vue | 163 +++++++++++++++++++----------------- 1 file changed, 88 insertions(+), 75 deletions(-) diff --git a/src/views/ai/chat/index.vue b/src/views/ai/chat/index.vue index 914b8c30..3c74d927 100644 --- a/src/views/ai/chat/index.vue +++ b/src/views/ai/chat/index.vue @@ -147,11 +147,13 @@ - -
- - 发送 -
+
+ +
+ + 发送 +
+
@@ -162,11 +164,23 @@ import {ChatMessageApi, ChatMessageSendVO, ChatMessageVO} from "@/api/ai/chat/me import {formatDate} from "@/utils/formatTime"; import {useClipboard} from '@vueuse/core' +// 初始化 copy 到粘贴板 +const { copy } = useClipboard(); + const searchName = ref('') // 查询的内容 const conversationId = ref('1781604279872581648') // 对话id const conversationInProgress = ref() // 对话进行中 +const conversationInAbortController = ref() // 对话进行中 abort 控制器(控制 stream 对话) + const prompt = ref() // prompt -const promptRes = ref() // prompt res + +// 判断 消息列表 滚动的位置(用于判断是否需要滚动到消息最下方) +const messageContainer: any = ref(null); +const isScrolling = ref(false)//用于判断用户是否在滚动 + +/** chat message 列表 */ +defineOptions({ name: 'chatMessageList' }) +const list = ref([]) // 列表的数据 const changeConversation = (conversation) => { console.log(conversation) @@ -190,82 +204,71 @@ const searchConversation = () => { /** send */ const onSend = async () => { + const content = prompt.value; + // 清空输入框 + prompt.value = '' const requestParams = { conversationId: conversationId.value, - content: prompt.value, + content: content, } as unknown as ChatMessageSendVO // 添加 message const userMessage = await ChatMessageApi.add(requestParams) as unknown as ChatMessageVO; list.value.push(userMessage) // 滚动到住下面 scrollToBottom(); - // + // stream await doSendStream(userMessage) + // } const doSendStream = async (userMessage: ChatMessageVO) => { // 创建AbortController实例,以便中止请求 - const ctrl = new AbortController() - // 发送 event stream - let isFirstMessage = true - ChatMessageApi.sendStream(userMessage.id, ctrl,(message) => { - console.log('message', message) - const data = JSON.parse(message.data) as unknown as ChatMessageVO - // 如果没有内容结束链接 - if (data.content === '') { - ctrl.abort() - } - // 首次返回需要添加一个 message 到页面,后面的都是更新 - if (isFirstMessage) { - isFirstMessage = false; - list.value.push(data) - } else { - const lastMessage = list.value[list.value.length - 1]; - lastMessage.content = lastMessage.content + data.content - list.value[list.value - 1] = lastMessage - } - // 滚动到最下面 - scrollToBottom(); - }, (error) => { - console.log('error', error) - }, () => { - console.log('close') - }) + conversationInAbortController.value = new AbortController() + // 标记对话进行中 + conversationInProgress.value = true + try { + // 发送 event stream + let isFirstMessage = true + ChatMessageApi.sendStream(userMessage.id, conversationInAbortController.value,(message) => { + console.log('message', message) + const data = JSON.parse(message.data) as unknown as ChatMessageVO + // 如果没有内容结束链接 + if (data.content === '') { + // 标记对话结束 + conversationInProgress.value = false; + // 结束 stream 对话 + conversationInAbortController.value.abort() + } + // 首次返回需要添加一个 message 到页面,后面的都是更新 + if (isFirstMessage) { + isFirstMessage = false; + list.value.push(data) + } else { + const lastMessage = list.value[list.value.length - 1]; + lastMessage.content = lastMessage.content + data.content + list.value[list.value - 1] = lastMessage + } + // 滚动到最下面 + scrollToBottom(); + }, (error) => { + console.log('error', error) + // 标记对话结束 + conversationInProgress.value = false; + // 结束 stream 对话 + conversationInAbortController.value.abort() + }, () => { + console.log('close') + // 标记对话结束 + conversationInProgress.value = false; + // 结束 stream 对话 + conversationInAbortController.value.abort() + }) + } finally { + + } - // // 创建一个正在进行中的message - // const chatMessage = { - // id: null, // 编号 - // conversationId: conversationId.value, // 会话编号 - // type: 'system', // 消息类型 - // userId: null, // 用户编号 - // roleId: null, // 角色编号 - // model: null, // 模型标志 - // modelId: null, // 模型编号 - // content: '加载中...', // 聊天内容 - // tokens: null, // 消耗 Token 数量 - // createTime: new Date(), // 创建时间 - // } as unknown as ChatMessageVO - // list.value.push(chatMessage) - // // 滚动到最下面 - // scrollToBottom(); } -/** Prompt */ -const onPromptInput = async (e) => { - console.log(e.data) - // prompt.value = e.data -} - - -// 初始化 copy 到粘贴板 -const { copy, isSupported } = useClipboard(); -/** chat message 列表 */ -defineOptions({ name: 'chatMessageList' }) -const list = ref([]) // 列表的数据 - -// 对话id TODO @范 先写死 -const content = '苹果是什么颜色?' - /** 查询列表 */ const messageList = async () => { try { @@ -277,9 +280,7 @@ const messageList = async () => { } finally { } } -// ref -const messageContainer: any = ref(null); -const isScrolling = ref(false)//用于判断用户是否在滚动 + function scrollToBottom() { nextTick(() => { @@ -321,6 +322,10 @@ const onDelete = async (id) => { message: '删除成功!', type: 'success', }) + // tip:如果 stream 进行中的 message,就需要调用 controller 结束 + if (conversationInAbortController.value) { + conversationInAbortController.value.abort() + } // 重新获取 message 列表 await messageList(); } @@ -525,8 +530,8 @@ onMounted(async () => { display: flex; flex-direction: column; overflow-wrap: break-word; - background-color: #e4e4e4; - box-shadow: 0 0 0 1px #e4e4e4; + background-color: rgba(228, 228, 228, 0.80); + box-shadow: 0 0 0 1px rgba(228, 228, 228, 0.80); border-radius: 10px; padding: 10px 10px 5px 10px; @@ -587,10 +592,18 @@ onMounted(async () => { display: flex; flex-direction: column; height: auto; - border: 1px solid #e3e3e3; - border-radius: 10px; - margin: 20px 20px; - padding: 9px 10px; + margin: 0; + padding: 0; + + .prompt-from { + display: flex; + flex-direction: column; + height: auto; + border: 1px solid #e3e3e3; + border-radius: 10px; + margin: 20px 20px; + padding: 9px 10px; + } .prompt-input { height: 80px;