|
@@ -682,6 +682,7 @@ import { useSpeechRecognition } from '@/composables/useSpeechRecognition'
|
|
|
// ===== 已删除:getUserId - 不再需要,改用token =====
|
|
// ===== 已删除:getUserId - 不再需要,改用token =====
|
|
|
// import { getUserId } from '@/utils/userManager.js'
|
|
// import { getUserId } from '@/utils/userManager.js'
|
|
|
import { stopSSEStream, updateAIMessageContent } from '@/utils/api.js'
|
|
import { stopSSEStream, updateAIMessageContent } from '@/utils/api.js'
|
|
|
|
|
+import { buildAIMessageUpdatePayload, hydratePersistedReports, normalizeReportsForPersistence } from '@/utils/chatHistoryPersistence.js'
|
|
|
import { getToken, getTokenType } from '@/utils/auth.js'
|
|
import { getToken, getTokenType } from '@/utils/auth.js'
|
|
|
import { renderMarkdown } from '@/utils/markdown'
|
|
import { renderMarkdown } from '@/utils/markdown'
|
|
|
import 'katex/dist/katex.min.css'
|
|
import 'katex/dist/katex.min.css'
|
|
@@ -1525,7 +1526,7 @@ const getConversationMessages = async (conversationId) => {
|
|
|
|
|
|
|
|
// 检查是否是新格式(包含reports和webSearch数据)
|
|
// 检查是否是新格式(包含reports和webSearch数据)
|
|
|
if (parsedContent.reports && Array.isArray(parsedContent.reports)) {
|
|
if (parsedContent.reports && Array.isArray(parsedContent.reports)) {
|
|
|
- reports = parsedContent.reports
|
|
|
|
|
|
|
+ reports = hydratePersistedReports(parsedContent.reports)
|
|
|
// 恢复网络搜索数据
|
|
// 恢复网络搜索数据
|
|
|
if (parsedContent.webSearchRaw) {
|
|
if (parsedContent.webSearchRaw) {
|
|
|
message.webSearchRaw = parsedContent.webSearchRaw
|
|
message.webSearchRaw = parsedContent.webSearchRaw
|
|
@@ -1543,7 +1544,7 @@ const getConversationMessages = async (conversationId) => {
|
|
|
}
|
|
}
|
|
|
} else if (Array.isArray(parsedContent)) {
|
|
} else if (Array.isArray(parsedContent)) {
|
|
|
// 旧格式,直接是reports数组
|
|
// 旧格式,直接是reports数组
|
|
|
- reports = parsedContent
|
|
|
|
|
|
|
+ reports = hydratePersistedReports(parsedContent)
|
|
|
} else if (parsedContent.answer || parsedContent.content || parsedContent.thinkingContent) {
|
|
} else if (parsedContent.answer || parsedContent.content || parsedContent.thinkingContent) {
|
|
|
if (parsedContent.thinkingContent) {
|
|
if (parsedContent.thinkingContent) {
|
|
|
thinkingContent = parsedContent.thinkingContent
|
|
thinkingContent = parsedContent.thinkingContent
|
|
@@ -2840,19 +2841,19 @@ const handleSSEMessage = (data, aiMessageIndex) => {
|
|
|
// 保存完整数据到后端 - 使用完整的summary而不是打字机过程中的部分内容
|
|
// 保存完整数据到后端 - 使用完整的summary而不是打字机过程中的部分内容
|
|
|
if (aiMessage.ai_message_id) {
|
|
if (aiMessage.ai_message_id) {
|
|
|
const contentData = {
|
|
const contentData = {
|
|
|
- reports: aiMessage.reports || [],
|
|
|
|
|
|
|
+ reports: normalizeReportsForPersistence(aiMessage.reports || []),
|
|
|
webSearchRaw: aiMessage.webSearchRaw || null,
|
|
webSearchRaw: aiMessage.webSearchRaw || null,
|
|
|
webSearchSummary: aiMessage._fullWebSearchSummary || data.summary, // 使用完整的webSearchSummary
|
|
webSearchSummary: aiMessage._fullWebSearchSummary || data.summary, // 使用完整的webSearchSummary
|
|
|
hasWebSearchResults: true,
|
|
hasWebSearchResults: true,
|
|
|
thinkingContent: aiMessage.thinkingContent || '',
|
|
thinkingContent: aiMessage.thinkingContent || '',
|
|
|
// ===== 🔧 修复:将summary也包含在content JSON中 =====
|
|
// ===== 🔧 修复:将summary也包含在content JSON中 =====
|
|
|
- summary: aiMessage.summary || aiMessage._fullSummary || ''
|
|
|
|
|
|
|
+ summary: aiMessage._fullSummary || aiMessage.summary || ''
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const collectedContent = JSON.stringify(contentData)
|
|
const collectedContent = JSON.stringify(contentData)
|
|
|
|
|
|
|
|
// 同时保存summary字段(作为单独字段)
|
|
// 同时保存summary字段(作为单独字段)
|
|
|
- const summaryToSave = aiMessage.summary || aiMessage._fullSummary || ''
|
|
|
|
|
|
|
+ const summaryToSave = aiMessage._fullSummary || aiMessage.summary || ''
|
|
|
|
|
|
|
|
updateAIMessageContent(aiMessage.ai_message_id, collectedContent, summaryToSave)
|
|
updateAIMessageContent(aiMessage.ai_message_id, collectedContent, summaryToSave)
|
|
|
.then((response) => {
|
|
.then((response) => {
|
|
@@ -2897,6 +2898,11 @@ const handleSSEMessage = (data, aiMessageIndex) => {
|
|
|
if (aiMessage.progress < 100) {
|
|
if (aiMessage.progress < 100) {
|
|
|
updateMessageStatus(aiMessage, 'completed')
|
|
updateMessageStatus(aiMessage, 'completed')
|
|
|
}
|
|
}
|
|
|
|
|
+ const completedPayload = buildAIMessageUpdatePayload(aiMessage)
|
|
|
|
|
+ if (completedPayload) {
|
|
|
|
|
+ updateAIMessageContent(completedPayload.aiMessageId, completedPayload.content)
|
|
|
|
|
+ .catch(err => console.error('completed事件回写AI消息失败:', err))
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
ElMessage.success('报告生成完成')
|
|
ElMessage.success('报告生成完成')
|
|
|
break
|
|
break
|
|
@@ -2946,14 +2952,14 @@ const handleSSEComplete = () => {
|
|
|
if (message.ai_message_id) {
|
|
if (message.ai_message_id) {
|
|
|
// 构建完整的内容数据,包含报告、网络搜索结果和summary
|
|
// 构建完整的内容数据,包含报告、网络搜索结果和summary
|
|
|
const contentData = {
|
|
const contentData = {
|
|
|
- reports: message.reports || [],
|
|
|
|
|
|
|
+ reports: normalizeReportsForPersistence(message.reports || []),
|
|
|
webSearchRaw: message.webSearchRaw || null,
|
|
webSearchRaw: message.webSearchRaw || null,
|
|
|
// 使用完整的webSearchSummary,而不是打字机过程中的部分内容
|
|
// 使用完整的webSearchSummary,而不是打字机过程中的部分内容
|
|
|
webSearchSummary: message._fullWebSearchSummary || message.webSearchSummary || null,
|
|
webSearchSummary: message._fullWebSearchSummary || message.webSearchSummary || null,
|
|
|
hasWebSearchResults: message.hasWebSearchResults || false,
|
|
hasWebSearchResults: message.hasWebSearchResults || false,
|
|
|
thinkingContent: message.thinkingContent || '',
|
|
thinkingContent: message.thinkingContent || '',
|
|
|
// ===== 🔧 修复:将summary也包含在content JSON中 =====
|
|
// ===== 🔧 修复:将summary也包含在content JSON中 =====
|
|
|
- summary: message.summary || message._fullSummary || ''
|
|
|
|
|
|
|
+ summary: message._fullSummary || message.summary || ''
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const plainAnswer = message.content || message._fullSummary || message.summary || ''
|
|
const plainAnswer = message.content || message._fullSummary || message.summary || ''
|
|
@@ -2968,7 +2974,7 @@ const handleSSEComplete = () => {
|
|
|
|
|
|
|
|
if (collectedContent) {
|
|
if (collectedContent) {
|
|
|
// 同时保存summary字段(作为单独字段)
|
|
// 同时保存summary字段(作为单独字段)
|
|
|
- const summaryToSave = message.summary || message._fullSummary || ''
|
|
|
|
|
|
|
+ const summaryToSave = message._fullSummary || message.summary || ''
|
|
|
|
|
|
|
|
updateAIMessageContent(message.ai_message_id, collectedContent, summaryToSave)
|
|
updateAIMessageContent(message.ai_message_id, collectedContent, summaryToSave)
|
|
|
.catch(err => console.error('回写AI消息失败:', err))
|
|
.catch(err => console.error('回写AI消息失败:', err))
|
|
@@ -2976,6 +2982,13 @@ const handleSSEComplete = () => {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
|
|
|
+
|
|
|
|
|
+ const lastAIMessageForPersistence = chatMessages.value.filter(msg => msg.type === 'ai').pop()
|
|
|
|
|
+ const finalPayload = buildAIMessageUpdatePayload(lastAIMessageForPersistence)
|
|
|
|
|
+ if (finalPayload) {
|
|
|
|
|
+ updateAIMessageContent(finalPayload.aiMessageId, finalPayload.content)
|
|
|
|
|
+ .catch(err => console.error('SSE完成时回写AI消息失败:', err))
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
isAIReplyProcessComplete.value = true
|
|
isAIReplyProcessComplete.value = true
|
|
|
|
|
|
|
@@ -3107,14 +3120,14 @@ const handleSSEInterrupted = (data) => {
|
|
|
if (message.ai_message_id) {
|
|
if (message.ai_message_id) {
|
|
|
// 构建完整的内容数据,包含报告、网络搜索结果和summary
|
|
// 构建完整的内容数据,包含报告、网络搜索结果和summary
|
|
|
const contentData = {
|
|
const contentData = {
|
|
|
- reports: message.reports || [],
|
|
|
|
|
|
|
+ reports: normalizeReportsForPersistence(message.reports || []),
|
|
|
webSearchRaw: message.webSearchRaw || null,
|
|
webSearchRaw: message.webSearchRaw || null,
|
|
|
// 使用完整的webSearchSummary,而不是打字机过程中的部分内容
|
|
// 使用完整的webSearchSummary,而不是打字机过程中的部分内容
|
|
|
webSearchSummary: message._fullWebSearchSummary || message.webSearchSummary || null,
|
|
webSearchSummary: message._fullWebSearchSummary || message.webSearchSummary || null,
|
|
|
hasWebSearchResults: message.hasWebSearchResults || false,
|
|
hasWebSearchResults: message.hasWebSearchResults || false,
|
|
|
thinkingContent: message.thinkingContent || '',
|
|
thinkingContent: message.thinkingContent || '',
|
|
|
// ===== 🔧 修复:将summary也包含在content JSON中 =====
|
|
// ===== 🔧 修复:将summary也包含在content JSON中 =====
|
|
|
- summary: message.summary || message._fullSummary || ''
|
|
|
|
|
|
|
+ summary: message._fullSummary || message.summary || ''
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const plainAnswer = message.content || message._fullSummary || message.summary || ''
|
|
const plainAnswer = message.content || message._fullSummary || message.summary || ''
|
|
@@ -3129,7 +3142,7 @@ const handleSSEInterrupted = (data) => {
|
|
|
|
|
|
|
|
if (collectedContent) {
|
|
if (collectedContent) {
|
|
|
// 同时保存summary字段(作为单独字段)
|
|
// 同时保存summary字段(作为单独字段)
|
|
|
- const summaryToSave = message.summary || message._fullSummary || ''
|
|
|
|
|
|
|
+ const summaryToSave = message._fullSummary || message.summary || ''
|
|
|
|
|
|
|
|
updateAIMessageContent(message.ai_message_id, collectedContent, summaryToSave)
|
|
updateAIMessageContent(message.ai_message_id, collectedContent, summaryToSave)
|
|
|
.catch(err => console.error('回写AI消息失败:', err))
|
|
.catch(err => console.error('回写AI消息失败:', err))
|
|
@@ -3183,14 +3196,14 @@ const handleStopGeneration = async () => {
|
|
|
// 回写数据到后端,包含网络搜索结果和summary
|
|
// 回写数据到后端,包含网络搜索结果和summary
|
|
|
if (message.ai_message_id) {
|
|
if (message.ai_message_id) {
|
|
|
const contentData = {
|
|
const contentData = {
|
|
|
- reports: message.reports || [],
|
|
|
|
|
|
|
+ reports: normalizeReportsForPersistence(message.reports || []),
|
|
|
webSearchRaw: message.webSearchRaw || null,
|
|
webSearchRaw: message.webSearchRaw || null,
|
|
|
// 使用完整的webSearchSummary,而不是打字机过程中的部分内容
|
|
// 使用完整的webSearchSummary,而不是打字机过程中的部分内容
|
|
|
webSearchSummary: message._fullWebSearchSummary || message.webSearchSummary || null,
|
|
webSearchSummary: message._fullWebSearchSummary || message.webSearchSummary || null,
|
|
|
hasWebSearchResults: message.hasWebSearchResults || false,
|
|
hasWebSearchResults: message.hasWebSearchResults || false,
|
|
|
thinkingContent: message.thinkingContent || '',
|
|
thinkingContent: message.thinkingContent || '',
|
|
|
// ===== 🔧 修复:将summary也包含在content JSON中 =====
|
|
// ===== 🔧 修复:将summary也包含在content JSON中 =====
|
|
|
- summary: message.summary || message._fullSummary || ''
|
|
|
|
|
|
|
+ summary: message._fullSummary || message.summary || ''
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const plainAnswer = message.content || message._fullSummary || message.summary || ''
|
|
const plainAnswer = message.content || message._fullSummary || message.summary || ''
|
|
@@ -3205,7 +3218,7 @@ const handleStopGeneration = async () => {
|
|
|
|
|
|
|
|
if (collectedContent) {
|
|
if (collectedContent) {
|
|
|
// 同时保存summary字段(作为单独字段)
|
|
// 同时保存summary字段(作为单独字段)
|
|
|
- const summaryToSave = message.summary || message._fullSummary || ''
|
|
|
|
|
|
|
+ const summaryToSave = message._fullSummary || message.summary || ''
|
|
|
|
|
|
|
|
updateAIMessageContent(message.ai_message_id, collectedContent, summaryToSave)
|
|
updateAIMessageContent(message.ai_message_id, collectedContent, summaryToSave)
|
|
|
.catch(err => console.error('回写AI消息失败:', err))
|
|
.catch(err => console.error('回写AI消息失败:', err))
|