This commit is contained in:
2025-09-10 21:56:53 +08:00
parent e78a7b95a1
commit cb710b47df
6 changed files with 911 additions and 649 deletions
+87 -32
View File
@@ -128,7 +128,7 @@ import {
formatChatMessage,
formatMessageTime
} from '@/http/api/assistant.js'
import { createPetAssistantSSE } from '@/utils/sse.js'
import { createPetAssistantWebSocket } from '@/utils/websocket.js'
export default {
data() {
@@ -192,6 +192,9 @@ export default {
// 开始AI思考
this.isThinking = true
// 用于存储当前AI消息的引用
let currentAiMessage = null
// 确保DOM更新后再滚动到思考状态
this.$nextTick(() => {
this.forceScrollToBottom()
@@ -205,11 +208,14 @@ export default {
})
try {
// 使用SSE流式响应
// 使用WebSocket流式响应
const response = await this.handleStreamResponse({
message: userInput,
temperature: 0.7,
maxTokens: 1000
}, (aiMessage) => {
// 保存AI消息引用,用于fallback时更新
currentAiMessage = aiMessage
})
// 检查是否包含敏感词
@@ -235,17 +241,25 @@ export default {
})
if (fallbackResponse && fallbackResponse.data) {
const aiMessage = {
type: 'ai',
content: fallbackResponse.data.message || fallbackResponse.data.content || '抱歉,我现在无法回答您的问题。',
time: this.getCurrentTime(),
isSensitive: fallbackResponse.data.isSensitive || false,
tokenCount: fallbackResponse.data.tokenCount || 0,
responseTime: fallbackResponse.data.responseTime || 0
// 更新已存在的AI消息,而不是创建新的
if (currentAiMessage) {
currentAiMessage.content = fallbackResponse.data.message || fallbackResponse.data.content || '抱歉,我现在无法回答您的问题。'
currentAiMessage.isSensitive = fallbackResponse.data.isSensitive || false
currentAiMessage.tokenCount = fallbackResponse.data.tokenCount || 0
currentAiMessage.responseTime = fallbackResponse.data.responseTime || 0
} else {
// 如果没有AI消息引用,创建新的(兜底逻辑)
const aiMessage = {
type: 'ai',
content: fallbackResponse.data.message || fallbackResponse.data.content || '抱歉,我现在无法回答您的问题。',
time: this.getCurrentTime(),
isSensitive: fallbackResponse.data.isSensitive || false,
tokenCount: fallbackResponse.data.tokenCount || 0,
responseTime: fallbackResponse.data.responseTime || 0
}
this.messageList.push(aiMessage)
}
// 检查敏感词
if (fallbackResponse.data.isSensitive) {
uni.showToast({
@@ -255,14 +269,13 @@ export default {
})
}
this.messageList.push(aiMessage)
this.forceScrollToBottom()
} else {
this.handleApiError('API响应格式异常')
this.handleApiError('API响应格式异常', currentAiMessage)
}
} catch (fallbackError) {
console.error('普通请求也失败:', fallbackError)
this.handleApiError(fallbackError.message || '网络请求失败')
this.handleApiError(fallbackError.message || '网络请求失败', currentAiMessage)
}
} finally {
// 确保思考状态结束(防止异常情况)
@@ -273,17 +286,26 @@ export default {
/**
* 处理API错误
* @param {string} errorMessage 错误信息
* @param {Object} existingAiMessage 已存在的AI消息对象(可选)
*/
handleApiError(errorMessage) {
const errorMessage_display = {
type: 'ai',
content: `抱歉,我遇到了一些问题:${errorMessage}\n\n请稍后再试,或者检查网络连接。`,
time: this.getCurrentTime(),
isError: true
handleApiError(errorMessage, existingAiMessage = null) {
const errorContent = `抱歉,我遇到了一些问题:${errorMessage}\n\n请稍后再试,或者检查网络连接。`
if (existingAiMessage) {
// 更新已存在的AI消息
existingAiMessage.content = errorContent
existingAiMessage.isError = true
} else {
// 创建新的错误消息(兜底逻辑)
const errorMessage_display = {
type: 'ai',
content: errorContent,
time: this.getCurrentTime(),
isError: true
}
this.messageList.push(errorMessage_display)
}
// 直接添加错误消息
this.messageList.push(errorMessage_display)
this.forceScrollToBottom()
},
@@ -291,28 +313,43 @@ export default {
* 加载聊天历史记录
*/
async loadChatHistory() {
console.log('开始加载聊天历史记录...')
try {
const response = await getAssistantHistory({
page: 1,
pageSize: 50
pageSize: 20
})
if (response && response.data && response.data.list && response.data.list.length > 0) {
console.log('历史记录API响应:', response)
if (response && Array.isArray(response) && response.length > 0) {
// 格式化并设置历史消息
const historyMessages = response.data.list.map(msg => formatChatMessage(msg))
const historyMessages = response.map(msg => formatChatMessage(msg))
console.log('格式化后的历史消息:', historyMessages)
this.messageList = historyMessages
// 滚动到底部
this.$nextTick(() => {
this.scrollToBottom()
})
console.log('历史记录加载成功,共', historyMessages.length, '条消息')
} else {
// 没有历史记录,保持空列表
console.log('没有历史记录或数据格式异常:', response)
this.messageList = []
}
} catch (error) {
console.error('加载历史记录失败:', error)
// 静默处理,保持空列表
// 显示用户友好的错误提示
uni.showToast({
title: '加载历史记录失败',
icon: 'none',
duration: 2000
})
// 保持空列表
this.messageList = []
}
},
@@ -360,11 +397,12 @@ export default {
/**
* 处理SSE流式响应
* 处理WebSocket流式响应
* @param {Object} messageData 消息数据
* @param {Function} onAiMessageCreated 创建AI消息后的回调函数
* @returns {Promise} 返回流式响应处理结果
*/
async handleStreamResponse(messageData) {
async handleStreamResponse(messageData, onAiMessageCreated) {
// 创建AI消息对象
const aiMessage = {
type: 'ai',
@@ -378,14 +416,23 @@ export default {
// 添加到消息列表
this.messageList.push(aiMessage)
// 使用SSE工具类处理流式响应
return createPetAssistantSSE(messageData, {
// 通知调用者AI消息已创建
if (onAiMessageCreated) {
onAiMessageCreated(aiMessage)
}
// 使用WebSocket工具类处理流式响应
return createPetAssistantWebSocket(messageData, {
onChunk: (chunk) => {
// 实时更新消息内容
console.log('收到WebSocket流式数据块:', chunk)
// 实时更新消息内容,实现打字机效果
aiMessage.content = chunk.totalContent
// 强制触发视图更新
this.$forceUpdate()
this.forceScrollToBottom()
},
onComplete: (result) => {
console.log('WebSocket流式响应完成:', result)
// 更新消息的元数据
if (result.data.isSensitive !== undefined) {
aiMessage.isSensitive = result.data.isSensitive
@@ -396,9 +443,17 @@ export default {
if (result.data.responseTime !== undefined) {
aiMessage.responseTime = result.data.responseTime
}
// 强制触发视图更新
this.$forceUpdate()
},
onError: (error) => {
console.error('SSE流式响应失败:', error)
console.error('WebSocket流式响应失败:', error)
// 显示错误提示
uni.showToast({
title: '消息发送失败',
icon: 'error',
duration: 2000
})
}
})
},