diff --git a/src/views/AiChatPage.vue b/src/views/AiChatPage.vue index a9a98d9..e47ba0c 100644 --- a/src/views/AiChatPage.vue +++ b/src/views/AiChatPage.vue @@ -5,7 +5,7 @@ import type { ConversationsProps, PromptsProps, } from 'ant-design-x-vue' -import type { VNode } from 'vue' +import type { Component, VNode } from 'vue' import { CloudUploadOutlined, CommentOutlined, @@ -18,7 +18,7 @@ import { ShareAltOutlined, SmileOutlined, } from '@ant-design/icons-vue' -import { Badge, Button, Flex, Space, Typography, theme } from 'ant-design-vue' +import { Badge, Button, Flex, Space, theme } from 'ant-design-vue' import { Attachments, Bubble, @@ -213,7 +213,7 @@ let messageIdCounter = 0 // ==================== Runtime ==================== async function sendMessage(userMessage: string) { - console.log('📝 [Submit] 用户提交消息:', userMessage) + console.log('[Submit] 用户提交消息:', userMessage) if (!userMessage) return // 添加用户消息 @@ -243,10 +243,10 @@ async function sendMessage(userMessage: string) { message: userMessage, conversionId: convId, } - console.log('📤 [Request] 请求体:', JSON.stringify(requestBody, null, 2)) + console.log('[Request] 请求体:', JSON.stringify(requestBody, null, 2)) - // 调用后端接口 - const response = await fetch('http://localhost:8080/dashscope/chat', { + // 调用后端接口 - POST 请求 SSE 流式响应 + const response = await fetch('http://localhost:8080/dashscope/generateStream', { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -254,7 +254,7 @@ async function sendMessage(userMessage: string) { body: JSON.stringify(requestBody), }) - console.log('📥 [Response] 响应状态:', response.status, response.statusText) + console.log('[Response] 响应状态:', response.status, response.statusText) if (!response.ok) { throw new Error(`请求失败: ${response.status} ${response.statusText}`) @@ -266,25 +266,22 @@ async function sendMessage(userMessage: string) { const decoder = new TextDecoder() let buffer = '' - console.log('🌊 [Stream] 开始读取流数据...') + console.log('[Stream] 开始读取流数据...') try { + let isFirstChunk = true while (true) { const { done, value } = await reader.read() if (done) { - console.log('✅ [Stream] 流读取完成') - const msg = messages.value[aiMsgIndex] - if (msg) { - msg.status = 'success' - } + console.log('[Stream] 流读取完成') break } // 解码数据 const text = decoder.decode(value, { stream: true }) buffer += text - console.log('📦 [Stream] 收到原始数据:', text) + console.log('[Stream] 收到原始数据:', text) // 解析 SSE 格式: data: content\n\n const lines = buffer.split('\n\n') @@ -299,14 +296,28 @@ async function sendMessage(userMessage: string) { for (const block of lines) { if (block.startsWith('data:')) { // 提取 data: 后面的内容 - const content = block.substring(5) - console.log('✨ [Parse] 解析后的内容:', JSON.stringify(content)) + const data = block.substring(5).trim() + console.log('[Parse] 接收到数据:', data) - // 直接累积到消息内容 - const msg = messages.value[aiMsgIndex] - if (msg) { - msg.content += content - console.log('🔄 [Update] 更新消息, 当前长度:', msg.content.length) + if (data) { // 若响应数据不为空 + // 解析 JSON + const response = JSON.parse(data) + console.log('[Parse] 解析后的内容:', JSON.stringify(response)) + + // 更新消息内容并强制触发响应式更新 + const msg = messages.value[aiMsgIndex] + if (msg) { + // 第一次接收到数据时,将状态改为 success,这样内容才会显示 + if (isFirstChunk) { + isFirstChunk = false + console.log('[Stream] 首次接收数据,设置状态为 success') + } + + // 持续追加流式回答 + msg.content += response.v + msg.status = 'success' + console.log('[Update] 更新消息, 当前长度:', msg.content.length) + } } } } @@ -316,7 +327,7 @@ async function sendMessage(userMessage: string) { } } } catch (error) { - console.error('❌ [Error] 请求错误:', error) + console.error('[Error] 请求错误:', error) const msg = messages.value[aiMsgIndex] if (msg) { msg.status = 'error' @@ -331,7 +342,7 @@ watch( activeKey, () => { if (activeKey.value !== undefined) { - console.log('🔄 [Conversation] 切换会话:', activeKey.value) + console.log('[Conversation] 切换会话:', activeKey.value) messages.value = [] } }, @@ -370,18 +381,16 @@ const handleFileChange: AttachmentsProps['onChange'] = (info) => // ==================== Nodes ==================== const placeholderNode = computed(() => h(Space, { direction: 'vertical', size: 16, style: styles.value.placeholder }, () => [ - h(Welcome, { + h(Welcome as Component, { variant: 'borderless', icon: 'https://mdn.alipayobjects.com/huamei_iwk9zp/afts/img/A*s5sNRo5LjfQAAAAAAAAAAAAADgCCAQ/fmt.webp', - title: "Hello, I'm Ant Design X", description: '基于Ant Design,AGI产品界面解决方案,创造更美好的智能愿景~', extra: h(Space, {}, () => [ h(Button, { icon: h(ShareAltOutlined) }), h(Button, { icon: h(EllipsisOutlined) }), ]), - }), - h(Prompts, { - title: '想要了是吧?', + }, () => "Hello, I'm Ant Design X"), + h(Prompts as Component, { items: placeholderPromptsItems, styles: { list: { @@ -392,20 +401,20 @@ const placeholderNode = computed(() => }, }, onItemClick: onPromptsItemClick, - }), + }, () => '想要了是吧?'), ]), ) const items = computed(() => { - console.log('🎨 [Items] 计算 items, messages数量:', messages.value.length) + console.log('[Items] 计算 items, messages数量:', messages.value.length) if (messages.value.length === 0) { - console.log('🎨 [Items] 显示占位符') + console.log('[Items] 显示占位符') return [{ content: placeholderNode, variant: 'borderless' }] } const result = messages.value.map(({ id, role, content, status }) => { - console.log('🎨 [Items] 处理消息:', { id, role, status, contentLength: content?.length }) + console.log('[Items] 处理消息:', { id, role, status, contentLength: content?.length }) return { key: id, loading: status === 'loading', @@ -414,7 +423,7 @@ const items = computed(() => { } }) - console.log('🎨 [Items] 最终 items:', result) + console.log('[Items] 最终 items:', result) return result }) @@ -493,19 +502,19 @@ const items = computed(() => { vertical gap="2" > - +
- - +
+
Upload files - - +
+
Click or drag files to this area to upload - +
- +
Drop file here - +