ソースを参照

[Bugfix 0000]修复计划模式下模型思考过程和ai对话渲染多条的问题

wangkeyi 3 日 前
コミット
cacbc44cea

+ 59 - 46
src/views/ventAI/manageAssistent/components/AiAssistantModal.vue

@@ -74,16 +74,16 @@
         />
 
         <!-- 下载确认条 -->
-        <div v-if="downloadConfirm" class="download-confirm-bar">
+        <div v-if="downloadConfirm && downloadConfirmTaskId === currentTaskId" class="download-confirm-bar">
           <span class="download-confirm-text">是否下载 {{ downloadConfirm.filename }} 到本地?</span>
           <div class="download-confirm-actions">
             <button class="download-confirm-btn download-btn" @click="doDownload">下载</button>
-            <button class="download-confirm-btn cancel-btn" @click="downloadConfirm = null">取消</button>
+            <button class="download-confirm-btn cancel-btn" @click="clearDownloadConfirm">取消</button>
           </div>
         </div>
 
         <!-- 任务进度条 -->
-        <TodoListBar v-if="currentTodos.length > 0" :todos="currentTodos" />
+        <TodoListBar v-if="showTodoBar" :todos="currentTodos" />
 
         <!-- 输入区域 -->
         <ChatInputArea
@@ -214,9 +214,28 @@
   const pendingApprovalSessionId = ref('');
   const pendingApprovalThreadId = ref('');
   const downloadConfirm = ref<{ url: string; filename: string } | null>(null);
+  const downloadConfirmTaskId = ref('');
+  let downloadConfirmTimer: ReturnType<typeof setTimeout> | null = null;
+
+  const clearDownloadConfirm = () => {
+    downloadConfirm.value = null;
+    downloadConfirmTaskId.value = '';
+    if (downloadConfirmTimer) {
+      clearTimeout(downloadConfirmTimer);
+      downloadConfirmTimer = null;
+    }
+  };
+
+  const startDownloadConfirmTimer = () => {
+    if (downloadConfirmTimer) clearTimeout(downloadConfirmTimer);
+    downloadConfirmTimer = setTimeout(() => {
+      clearDownloadConfirm();
+    }, 10000);
+  };
   const contextBreakdown = ref({ messages: 0, mcp: 0, skills: 0, system_prompt: 0, other: 0 });
 
   const currentTodos = ref<Array<{ content: string; status: string }>>([]);
+  const showTodoBar = ref(false);
 
   const syncTodoBarFromMessages = () => {
     for (let i = messages.value.length - 1; i >= 0; i--) {
@@ -226,11 +245,13 @@
           const step = msg.thinkingSteps[j];
           if (step.type === 'updated_todo_list' && step.todos) {
             currentTodos.value = [...step.todos];
+            showTodoBar.value = true;
             return;
           }
         }
       }
     }
+    showTodoBar.value = false;
     currentTodos.value = [];
   };
 
@@ -445,29 +466,18 @@
     const approveTask = taskList.value.find((t) => t.sessionId === sessionId);
     if (!approveTask) return;
 
-    // 清除中断消息的 pendingApproval 状态,获取中断前累计时长和思考内容
     const taskMessages = approveTask.messages;
     const lastMsg = taskMessages[taskMessages.length - 1];
-    const prevElapsedMs = lastMsg?.pendingApprovalData?.elapsedMs || 0;
-    const prevThinkingContent = lastMsg?.pendingApprovalData?.prevThinkingContent || lastMsg?.thinkingContent || '';
-    if (lastMsg && lastMsg.isPendingApproval) {
-      lastMsg.isPendingApproval = false;
-    }
+    if (!lastMsg || !lastMsg.isPendingApproval) return;
 
-    const now = dayjs();
-    const aiMsgIndex = taskMessages.length;
-    const aiMsg: Message = {
-      type: 'ai',
-      content: '',
-      thinkingContent: prevThinkingContent,
-      time: now.format('HH:mm'),
-      createdAt: now.toISOString(),
-      isLoading: true,
-      thinkingSteps: [],
-      generateStartTime: Date.now(),
-      baseDurationMs: prevElapsedMs,
-    };
-    taskMessages.push(aiMsg);
+    // 复用同一条消息继续渲染:清除审批状态,恢复 loading,重置前端计时起点
+    lastMsg.isPendingApproval = false;
+    lastMsg.pendingApprovalData = undefined;
+    lastMsg.isLoading = true;
+    lastMsg.generateStartTime = Date.now();
+    // thinkingSteps、thinkingContent、baseDurationMs 保持不变,后续继续累积
+
+    const aiMsgIndex = taskMessages.length - 1;
     approveTask.messages = [...taskMessages];
     if (currentTaskId.value === approveTask.id) {
       messages.value = approveTask.messages;
@@ -496,9 +506,9 @@
     } catch (error) {
       console.error('审批继续执行失败:', error);
       message.error('审批失败,请重试');
-      if (aiMsg.isLoading) {
-        aiMsg.isLoading = false;
-        aiMsg.content = '抱歉,审批请求失败,请稍后重试。';
+      lastMsg.isLoading = false;
+      if (!lastMsg.content) {
+        lastMsg.content = '抱歉,审批请求失败,请稍后重试。';
       }
     } finally {
       loading.value = false;
@@ -518,12 +528,20 @@
     const rejectTask = taskList.value.find((t) => t.sessionId === sessionId);
     if (!rejectTask) return;
 
-    // 清除中断消息的 pendingApproval 状态
     const taskMessages = rejectTask.messages;
     const lastMsg = taskMessages[taskMessages.length - 1];
-    if (lastMsg && lastMsg.isPendingApproval) {
-      lastMsg.isPendingApproval = false;
-    }
+    if (!lastMsg || !lastMsg.isPendingApproval) return;
+
+    // 复用同一条消息:清除审批状态,标记为已拒绝
+    lastMsg.isPendingApproval = false;
+    lastMsg.pendingApprovalData = undefined;
+    lastMsg.isLoading = false;
+    lastMsg.content = (lastMsg.content ? lastMsg.content + '\n\n' : '') + '已拒绝执行计划。';
+
+    rejectTask.messages = [...taskMessages];
+
+    pendingApprovalSessionId.value = '';
+    pendingApprovalThreadId.value = '';
 
     try {
       await chatResumeStream(
@@ -538,19 +556,6 @@
       console.error('拒绝操作失败:', error);
     }
 
-    const now = dayjs();
-    const rejectMsg: Message = {
-      type: 'ai',
-      content: '已拒绝执行计划。',
-      time: now.format('HH:mm'),
-      createdAt: now.toISOString(),
-    };
-    taskMessages.push(rejectMsg);
-    rejectTask.messages = [...taskMessages];
-
-    pendingApprovalSessionId.value = '';
-    pendingApprovalThreadId.value = '';
-
     if (currentTaskId.value === rejectTask.id) {
       messages.value = rejectTask.messages;
       await scrollToBottom();
@@ -736,7 +741,7 @@
     document.body.appendChild(a);
     a.click();
     document.body.removeChild(a);
-    downloadConfirm.value = null;
+    clearDownloadConfirm();
   };
 
   const downloadWordFile = (url: string) => {
@@ -953,6 +958,7 @@
           });
         }
         currentTodos.value = [...(data.todos || [])];
+        showTodoBar.value = true;
         break;
       }
 
@@ -981,6 +987,8 @@
         });
         if (data.download_url) {
           downloadConfirm.value = { url: data.download_url, filename: data.filename || '文件' };
+          downloadConfirmTaskId.value = taskId;
+          startDownloadConfirmTimer();
         }
         break;
       }
@@ -993,9 +1001,10 @@
           message: `${(data.message || '审查完成').replace(/(\d+ms)/g, '')}${data.duration_ms ? `(${(data.duration_ms / 1000).toFixed(0)}s)` : ''}${data.progress ? ` [${data.progress}]` : ''}`,
           timestamp: Date.now(),
         });
-        // 用后端返回的时间替换前端计时,作为中断前的累计基准
+        // 累加后端返回的各段耗时,并重置前端计时起点,避免重复计算已验证的时间段
         if (data.duration_ms) {
-          aiMsg.baseDurationMs = data.duration_ms;
+          aiMsg.baseDurationMs = (aiMsg.baseDurationMs || 0) + data.duration_ms;
+          aiMsg.generateStartTime = Date.now();
         }
         if (data.preview) {
           aiMsg.content += data.preview;
@@ -1423,6 +1432,10 @@
   onBeforeUnmount(() => {
     document.removeEventListener('mousemove', onDragMove);
     document.removeEventListener('mouseup', onDragEnd);
+    if (downloadConfirmTimer) {
+      clearTimeout(downloadConfirmTimer);
+      downloadConfirmTimer = null;
+    }
   });
 </script>