Jelajahi Sumber

[Mod 0000] AI助手切换会话补加载态:历史拉取期间显示居中加载指示器,不再闪空会话

- 切换任务时 currentTaskId 立即切过去,而新任务的 messages 要等 /chat/history 返回才填充;这段窗口原先落到
  messages.length === 0,会先闪一屏「新对话缺省图」再突然出现内容。现在区分「加载中」与「确实没有消息」
- useChatTasks 新增 historyLoadingTaskIds / setHistoryLoading(按前端任务 id 记账)。用数组而非 Set:
  ref<Set> 的 mutation 不被 Vue 追踪,整体替换数组才是可靠的响应式写法
- switchTask 与弹框打开时的历史加载点都用 try/finally 打标记,接口异常也不会卡在加载态
- ChatMessages 新增 historyLoading prop;指示器用 transform 放大(antd 3.x 点阵是固定像素,
  只改 font-size 会让框变大而四点仍为原尺寸),按反馈不显示文字提示

验证:浏览器实测——切换未加载任务显示加载指示器且缺省图不再闪出;切回已加载任务不闪加载态;
  新建任务仍正常显示缺省图;console 无报错。vue-tsc 与 ESLint 通过(仅剩全仓既有 CRLF 噪音)
wangkeyi 1 Minggu lalu
induk
melakukan
f9002a5cbb

+ 14 - 0
src/views/ventAI/manageAssistent/components/AiAssistantModal.vue

@@ -85,6 +85,7 @@
           <ChatMessages
             ref="chatMessagesRef"
             :messages="messages"
+            :history-loading="isCurrentHistoryLoading"
             @file-preview="openFilePreview"
             @download-word="downloadWordFile"
             @open-file-preview="handleOpenFilePreview"
@@ -251,6 +252,8 @@
     currentTask,
     messages,
     historyLoadedTasks,
+    historyLoadingTaskIds,
+    setHistoryLoading,
     getDefaultMessages,
     transformHistoryToMessages,
     fetchSessionList,
@@ -259,6 +262,10 @@
     setCurrentSessionId,
   } = useChatTasks();
 
+  // 当前任务的历史是否正在拉取:切换任务时新任务的 messages 要等接口回来才填充,
+  // 消息区据此显示加载态,避免中间先闪一屏空会话缺省图。
+  const isCurrentHistoryLoading = computed(() => historyLoadingTaskIds.value.includes(currentTaskId.value));
+
   // —— 右侧面板/标签页/全屏相关职责已下沉到 useChatPanels ——
   const {
     showFileList,
@@ -568,6 +575,8 @@
       // 流式中的任务不拉历史(避免覆盖在途消息),但必须继续走完下面的初始化:
       // currentTaskId 已经切过来了,提前 return 会让上下文用量/模式/进度条/滚动全部停在上一个任务上。
       if (!newTask.isStreaming) {
+        // 标记加载中:消息区据 isCurrentHistoryLoading 显示加载态,而不是先闪一屏空会话
+        setHistoryLoading(taskId, true);
         try {
           const res = await getDetail(newTask.sessionId);
           const messages = res?.messages;
@@ -579,6 +588,8 @@
           // 拉不到历史时界面会是空白会话,容易被误认为「对话丢了」,必须给出提示
           console.error('加载历史会话失败:', error);
           message.error('加载历史会话失败,请稍后重试');
+        } finally {
+          setHistoryLoading(taskId, false);
         }
       }
     }
@@ -852,6 +863,7 @@
 
         // 如果是历史任务,加载其消息
         if (targetTask.sessionId && !historyLoadedTasks.has(targetTask.id)) {
+          setHistoryLoading(targetTask.id, true);
           try {
             const res = await getDetail(targetTask.sessionId);
             const messages = res?.messages;
@@ -862,6 +874,8 @@
           } catch (error) {
             console.error('加载历史会话失败:', error);
             message.error('加载历史会话失败,请稍后重试');
+          } finally {
+            setHistoryLoading(targetTask.id, false);
           }
         }
 

+ 21 - 1
src/views/ventAI/manageAssistent/components/chat/ChatMessages.vue

@@ -10,8 +10,13 @@
     />
 
     <div class="chat-messages" ref="messagesRef">
+      <!-- 历史加载中:切换任务后新任务的 messages 要等接口返回才填充,
+           这里必须先占位,否则会先闪一屏「新对话缺省图」再突然出现内容 -->
+      <div v-if="messages.length === 0 && historyLoading" class="history-loading">
+        <a-spin size="large" />
+      </div>
       <!-- 新对话缺省图 -->
-      <div v-if="messages.length === 0" class="welcome-empty">
+      <div v-else-if="messages.length === 0" class="welcome-empty">
         <img :src="getCssUrl('--img-chat-welcome-img')" class="welcome-img" />
       </div>
       <div
@@ -176,6 +181,8 @@
 
   const props = defineProps<{
     messages: Message[];
+    /** 当前任务的历史是否正在拉取:为真且暂无消息时显示加载态,而不是空会话缺省图 */
+    historyLoading?: boolean;
   }>();
 
   const emit = defineEmits<{
@@ -541,6 +548,19 @@
       }
     }
 
+    /* 历史加载中的占位:与缺省图同样居中,避免两态之间出现位置跳动。
+       antd 3.x 的点阵是固定像素(large 为 32px 框 + 14px 点),不随 font-size 缩放,
+       因此整体放大用 transform,既不动 antd 内部类名,比例也不会走样。 */
+    .history-loading {
+      position: absolute;
+      top: 50%;
+      left: 50%;
+      transform: translate(-50%, -50%) scale(2.5);
+      display: flex;
+      align-items: center;
+      justify-content: center;
+    }
+
     .message-wrapper {
       // margin-bottom: 20px;
 

+ 17 - 0
src/views/ventAI/manageAssistent/hooks/useChatTasks.ts

@@ -98,6 +98,21 @@ export const useChatTasks = () => {
   // Non-reactive lookup cache - Vue 3 does not track Set mutations on ref<Set>
   const historyLoadedTasks = new Set<string>();
 
+  // 正在拉取历史的任务 id(前端任务 id)。
+  //
+  // 切换任务时 currentTaskId 立即切过去,而新任务的 messages 要等 getDetail 回来才有内容:
+  // 这段窗口里消息区会落到 messages.length === 0,若不区分「加载中」与「确实没有消息」,
+  // 用户会先看到一屏空会话缺省图再突然出现内容。消息区据此显示加载态。
+  //
+  // 用数组而非 Set:ref<Set> 的 mutation 不被 Vue 追踪(同 historyLoadedTasks 的注释),
+  // 整体替换数组才是可靠的响应式写法。
+  const historyLoadingTaskIds = ref<string[]>([]);
+
+  const setHistoryLoading = (taskId: string, loading: boolean) => {
+    const rest = historyLoadingTaskIds.value.filter((id) => id !== taskId);
+    historyLoadingTaskIds.value = loading ? [...rest, taskId] : rest;
+  };
+
   const setCurrentSessionId = (sessionId: string) => {
     const currentTask = taskList.value.find((t) => t.id === currentTaskId.value);
     if (currentTask && currentTask.id !== sessionId) {
@@ -191,6 +206,8 @@ export const useChatTasks = () => {
     currentTask,
     messages,
     historyLoadedTasks,
+    historyLoadingTaskIds,
+    setHistoryLoading,
     getDefaultMessages,
     transformHistoryToMessages,
     fetchSessionList,