From ee816f95c7932803584d023f93b6785211c16f9d Mon Sep 17 00:00:00 2001
From: Codewoc <947380458@qq.com>
Date: Fri, 27 Mar 2026 14:25:13 +0800
Subject: [PATCH] =?UTF-8?q?fix(review-tablet):=20=E4=BF=AE=E5=A4=8DAI?=
=?UTF-8?q?=E5=8A=A9=E6=89=8B=E9=A1=B9=E7=9B=AE=E5=88=87=E6=8D=A2=E7=8A=B6?=
=?UTF-8?q?=E6=80=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/views/review/tablet/index.vue | 77 ++++++++++++++++++++++++++++---
1 file changed, 70 insertions(+), 7 deletions(-)
diff --git a/src/views/review/tablet/index.vue b/src/views/review/tablet/index.vue
index c7c4e3913..9a09f5bea 100644
--- a/src/views/review/tablet/index.vue
+++ b/src/views/review/tablet/index.vue
@@ -144,7 +144,7 @@
- 当前项目尚未生成 AI 摘要,请上传资料后自动触发生成
+ {{ aiSummary?.blockReason || '当前项目 AI 尚未就绪,请联系管理员先完成构建' }}
@@ -154,7 +154,7 @@
- 摘要生成失败:{{ aiSummary.errorMessage || '未知错误' }}
+ {{ aiSummary.blockReason || `摘要生成失败:${aiSummary.errorMessage || '未知错误'}` }}
@@ -268,7 +268,7 @@
- AI 摘要就绪后才能开始对话
+ {{ aiSummary.blockReason || 'AI 摘要就绪后才能开始对话' }}
@@ -338,6 +338,12 @@ interface FileTreeNode {
source: ReviewMeetingFileRespVO
}
+interface ProjectAiState {
+ summary: ReviewAiSummaryVO | null
+ conversationId: number | null
+ messages: ChatMessageVO[]
+}
+
// ── 目录 & 预览 state ─────────────────────────────────────────
const pageLoading = ref(false)
const previewLoading = ref(false)
@@ -393,8 +399,10 @@ const streamingLoading = ref(false)
const aiMessagesRef = ref
()
let streamAbortCtrl: AbortController | null = null
let summaryPollingTimer: ReturnType | null = null
+let aiLoadToken = 0
let officeWarmupTimers: ReturnType[] = []
let previewFrameTimeoutTimer: ReturnType | null = null
+const aiProjectCache = ref>({})
// ── 搜索过滤 ──────────────────────────────────────────────────
watch(keyword, (val) => treeRef.value?.filter(val.trim()))
@@ -529,14 +537,24 @@ const handleNodeCollapse = (data: TreeNode) => {
const handleNodeClick = async (data: TreeNode) => {
if (data.type === 'project') {
+ persistCurrentProjectAiState()
const switched = activeProjectId.value !== data.projectId
activeProjectId.value = data.projectId
await loadProjectFiles(data)
- if (switched) resetProjectAiState()
- if (aiPanelOpen.value) await loadProjectAi(data.projectId)
+ if (switched && aiPanelOpen.value) {
+ restoreOrLoadProjectAi(data.projectId)
+ }
+ if (!switched && aiPanelOpen.value) {
+ restoreOrLoadProjectAi(data.projectId)
+ }
return
}
+ persistCurrentProjectAiState()
+ const switched = activeProjectId.value !== data.projectId
await selectFileNode(data)
+ if (aiPanelOpen.value && (switched || !aiSummary.value)) {
+ restoreOrLoadProjectAi(data.projectId)
+ }
}
// ── 目录加载 ──────────────────────────────────────────────────
@@ -571,7 +589,7 @@ const loadCatalog = async () => {
const toggleAiPanel = async () => {
aiPanelOpen.value = !aiPanelOpen.value
if (aiPanelOpen.value && activeProjectId.value) {
- await loadProjectAi(activeProjectId.value)
+ restoreOrLoadProjectAi(activeProjectId.value)
}
}
@@ -585,11 +603,47 @@ const resetProjectAiState = () => {
streamingText.value = ''
}
+const persistCurrentProjectAiState = () => {
+ const projectId = activeProjectId.value
+ if (!projectId) return
+ aiProjectCache.value[projectId] = {
+ summary: aiSummary.value,
+ conversationId: aiConversationId.value,
+ messages: [...aiMessages.value]
+ }
+}
+
+const applyProjectAiState = (state?: ProjectAiState) => {
+ resetProjectAiState()
+ aiSummary.value = state?.summary ?? null
+ aiConversationId.value = state?.conversationId ?? null
+ aiMessages.value = state?.messages ? [...state.messages] : []
+}
+
+const restoreOrLoadProjectAi = (projectId: number) => {
+ const cached = aiProjectCache.value[projectId]
+ if (cached) {
+ applyProjectAiState(cached)
+ if (cached.summary?.status === AI_SUMMARY_STATUS.BUILDING) {
+ startSummaryPolling(projectId)
+ } else if (cached.summary?.status !== AI_SUMMARY_STATUS.SUCCESS) {
+ loadProjectAi(projectId)
+ }
+ scrollAiToBottom()
+ return
+ }
+ loadProjectAi(projectId)
+}
+
const loadProjectAi = async (projectId: number) => {
resetProjectAiState()
aiSummaryLoading.value = true
+ const token = ++aiLoadToken
try {
- aiSummary.value = await getProjectAiSummary(projectId)
+ const summary = await getProjectAiSummary(projectId)
+ if (token !== aiLoadToken || activeProjectId.value !== projectId) return
+ aiSummary.value = summary
+ persistCurrentProjectAiState()
// 如果生成中,开启轮询
if (aiSummary.value?.status === AI_SUMMARY_STATUS.BUILDING) {
startSummaryPolling(projectId)
@@ -608,11 +662,15 @@ const loadProjectAi = async (projectId: number) => {
const openAndLoadConversation = async (projectId: number) => {
aiChatLoading.value = true
+ const token = aiLoadToken
try {
const conv = await openProjectAiConversation(projectId)
+ if (token !== aiLoadToken || activeProjectId.value !== projectId) return
aiConversationId.value = conv.conversationId
const msgs = await ChatMessageApi.getChatMessageListByConversationId(conv.conversationId)
+ if (token !== aiLoadToken || activeProjectId.value !== projectId) return
aiMessages.value = msgs || []
+ persistCurrentProjectAiState()
await scrollAiToBottom()
} catch {
// 会话打开失败不影响主流程
@@ -628,6 +686,7 @@ const startSummaryPolling = (projectId: number) => {
if (activeProjectId.value !== projectId || !aiPanelOpen.value) return
try {
aiSummary.value = await getProjectAiSummary(projectId)
+ persistCurrentProjectAiState()
} catch { /* silent */ }
if (aiSummary.value?.status === AI_SUMMARY_STATUS.BUILDING) {
startSummaryPolling(projectId)
@@ -679,6 +738,7 @@ const handleSend = async () => {
// 用真实 send 消息替换假消息
aiMessages.value.pop()
aiMessages.value.push(data.send)
+ persistCurrentProjectAiState()
}
if (data?.receive?.content) {
streamingText.value += data.receive.content
@@ -699,6 +759,7 @@ const handleSend = async () => {
createTime: new Date()
} as any)
streamingText.value = ''
+ persistCurrentProjectAiState()
}
streamingLoading.value = false
await scrollAiToBottom()
@@ -719,6 +780,7 @@ const stopStream = () => {
createTime: new Date()
} as any)
streamingText.value = ''
+ persistCurrentProjectAiState()
}
streamingLoading.value = false
}
@@ -729,6 +791,7 @@ const handleClearMessages = async () => {
stopStream()
await clearProjectAiMessages(activeProjectId.value)
aiMessages.value = []
+ persistCurrentProjectAiState()
ElMessage.success('问答记录已清空')
}