SSE流式响应完成
This commit is contained in:
@@ -2,26 +2,29 @@
|
||||
<div class="h-screen max-w-3xl mx-auto relative">
|
||||
<!-- 聊天记录区域 -->
|
||||
<div class="overflow-y-auto pb-24 pt-4 px-4">
|
||||
<!-- 遍历聊天记录 -->
|
||||
<template v-for="(chat, index) in chatList" :key="index">
|
||||
<!-- 用户提问消息(靠右) -->
|
||||
<div class="flex justify-end mb-4">
|
||||
<div v-if="chat.role === 'user'" class="flex justify-end mb-4">
|
||||
<div class="quesiton-container">
|
||||
<p>你是谁</p>
|
||||
<p>{{ chat.content }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 大模型回复消息(靠左) -->
|
||||
<div class="flex mb-4">
|
||||
<div v-else class="flex mb-4">
|
||||
<!-- 头像 -->
|
||||
<div class="flex-shrink-0 mr-3">
|
||||
<div class="shrink-0 mr-3">
|
||||
<div class="w-8 h-8 rounded-full flex items-center justify-center border border-gray-200">
|
||||
<SvgIcon name="deepseek-logo" customCss="w-5 h-5"></SvgIcon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 回复的内容 -->
|
||||
<div class="p-1 max-w-[80%] mb-2 grow">
|
||||
<p>我是DeepSeek Chat,由深度求索公司开发的智能AI助手!✨ 我可以帮你解答各种问题,无论是学习、工作,还是日常生活中的小困惑,都可以找我聊聊。有什么我可以帮你的吗?😊</p>
|
||||
<div class="p-1 max-w-[80%] mb-2">
|
||||
<p>{{ chat.content }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<!-- 提问输入框 -->
|
||||
<div class="absolute bottom-0 left-0 w-full mb-5">
|
||||
@@ -34,6 +37,7 @@
|
||||
<!-- 发送按钮 -->
|
||||
<div class="flex justify-end">
|
||||
<button
|
||||
@click="sendMessage"
|
||||
class="flex items-center justify-center bg-[#4d6bfe] rounded-full w-8 h-8 border border-[#4d6bfe] hover:bg-[#3b5bef] transition-colors">
|
||||
<SvgIcon name="up-arrow" customCss="w-5 h-5 text-white"></SvgIcon>
|
||||
</button>
|
||||
@@ -46,12 +50,110 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { onBeforeUnmount, ref } from 'vue'
|
||||
|
||||
interface ChatMessage {
|
||||
role: 'user' | 'assistant'
|
||||
content: string
|
||||
}
|
||||
|
||||
// 输入的消息
|
||||
const message = ref('')
|
||||
const message = ref<string>('')
|
||||
|
||||
// textarea引用
|
||||
const textareaRef = ref<HTMLElement | null>(null);
|
||||
const textareaRef = ref<HTMLTextAreaElement | null>(null);
|
||||
|
||||
// 当前 SSE 连接实例
|
||||
let eventSource: EventSource | null = null
|
||||
|
||||
// 聊天记录 (给个默认的问候语)
|
||||
const chatList = ref<ChatMessage[]>([
|
||||
{ role: 'assistant', content: '我是小维智能 AI 助手!✨ 我可以帮你解答各种问题,无论是学习、工作,还是日常生活中的小困惑,都可以找我聊聊。有什么我可以帮你的吗?😊' }
|
||||
])
|
||||
|
||||
// 发送消息
|
||||
const sendMessage = async (): Promise<void> => {
|
||||
// 校验发送的消息不能为空
|
||||
if (!message.value.trim()) return
|
||||
|
||||
// 将用户发送的消息添加到 chatList 聊天列表中
|
||||
const userMessage = message.value.trim()
|
||||
chatList.value.push({ role: 'user', content: userMessage })
|
||||
|
||||
// 点击发送按钮后,清空输入框
|
||||
message.value = ''
|
||||
// 将输入框的高度重置
|
||||
if (textareaRef.value) {
|
||||
textareaRef.value.style.height = 'auto'
|
||||
}
|
||||
|
||||
// 发送新的消息前关闭旧的 SSE 连接
|
||||
closeSSE()
|
||||
|
||||
// 添加一个占位的回复消息
|
||||
chatList.value.push({ role: 'assistant', content: '' })
|
||||
|
||||
const updateAssistantMessage = (content: string) => {
|
||||
const lastIndex = chatList.value.length - 1
|
||||
if (lastIndex < 0) return
|
||||
const lastMessage = chatList.value[lastIndex]
|
||||
if (lastMessage?.role !== 'assistant') return
|
||||
lastMessage.content = content
|
||||
}
|
||||
|
||||
try {
|
||||
// 建立 SSE 连接
|
||||
eventSource = new EventSource(`http://localhost:8080/v5/ai/generateStream?message=${encodeURIComponent(userMessage)}`)
|
||||
|
||||
// 响应的回答
|
||||
let responseText = ''
|
||||
|
||||
// 处理消息事件
|
||||
eventSource.onmessage = (event) => {
|
||||
console.log('接收到数据: ', event.data)
|
||||
if (event.data) { // 若响应数据不为空
|
||||
// 持续追加流式回答
|
||||
responseText += event.data;
|
||||
|
||||
// 更新最后一条消息
|
||||
updateAssistantMessage(responseText)
|
||||
}
|
||||
}
|
||||
|
||||
// 处理错误
|
||||
eventSource.onerror = (error) => {
|
||||
// 通常 SSE 在完成传输后会触发一次 error 事件,这是正常的
|
||||
if (error.eventPhase === EventSource.CLOSED) {
|
||||
console.log('SSE正常关闭')
|
||||
} else {
|
||||
// 提示用户 “请求出错”
|
||||
updateAssistantMessage('抱歉,请求出错了,请稍后重试。')
|
||||
}
|
||||
|
||||
// 关闭 SSE
|
||||
closeSSE()
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('发送消息错误: ', error)
|
||||
// 提示用户 “请求出错”
|
||||
updateAssistantMessage('抱歉,请求出错了,请稍后重试。')
|
||||
closeSSE()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 关闭 SSE 连接
|
||||
const closeSSE = () => {
|
||||
if (eventSource) {
|
||||
eventSource.close()
|
||||
eventSource = null
|
||||
}
|
||||
}
|
||||
|
||||
// 组件卸载时自动关闭连接
|
||||
onBeforeUnmount(() => {
|
||||
closeSSE()
|
||||
})
|
||||
|
||||
// 自动调整文本域高度
|
||||
const autoResize = () => {
|
||||
|
||||
Reference in New Issue
Block a user