// @ts-nocheck import { debugLog, traceLog } from './common'; export const conversationMethods = { // 完整重置所有状态 resetAllStates(reason = 'unspecified', options: { preserveMonitorWindows?: boolean } = {}) { debugLog('重置所有前端状态', { reason, conversationId: this.currentConversationId }); this.logMessageState('resetAllStates:before-cleanup', { reason }); this.fileHideContextMenu(); this.monitorResetVisual({ preserveBubble: true, preservePointer: true, preserveWindows: !!options?.preserveMonitorWindows, preserveQueue: !!options?.preserveMonitorWindows }); // 重置消息和流状态 this.streamingMessage = false; this.currentMessageIndex = -1; this.stopRequested = false; this.taskInProgress = false; this.dropToolEvents = false; // 清理工具状态 this.toolResetTracking(); // 新增:将所有未完成的工具标记为已完成 this.messages.forEach(msg => { if (msg.role === 'assistant' && msg.actions) { msg.actions.forEach(action => { if (action.type === 'tool' && (action.tool.status === 'preparing' || action.tool.status === 'running')) { action.tool.status = 'completed'; } }); } }); // 清理Markdown缓存 if (this.markdownCache) { this.markdownCache.clear(); } this.chatClearThinkingLocks(); // 强制更新视图 this.$forceUpdate(); this.inputSetSettingsOpen(false); this.inputSetToolMenuOpen(false); this.inputSetQuickMenuOpen(false); this.modeMenuOpen = false; this.inputSetLineCount(1); this.inputSetMultiline(false); this.inputClearMessage(); this.inputClearSelectedImages(); this.inputSetImagePickerOpen(false); this.imageEntries = []; this.imageLoading = false; this.conversationHasImages = false; this.toolSetSettingsLoading(false); this.toolSetSettings([]); debugLog('前端状态重置完成'); this._scrollListenerReady = false; this.$nextTick(() => { this.ensureScrollListener(); }); // 重置已加载对话标记,便于后续重新加载新对话历史 this.lastHistoryLoadedConversationId = null; this.logMessageState('resetAllStates:after-cleanup', { reason }); }, scheduleResetAfterTask(reason = 'unspecified', options: { preserveMonitorWindows?: boolean } = {}) { const start = Date.now(); const maxWait = 4000; const interval = 200; const tryReset = () => { if (!this.monitorIsLocked || Date.now() - start >= maxWait) { this.resetAllStates(reason, options); return; } setTimeout(tryReset, interval); }; tryReset(); }, resetTokenStatistics() { this.resourceResetTokenStatistics(); }, // ========================================== // 对话管理核心功能 // ========================================== async loadConversationsList() { const queryOffset = this.conversationsOffset; const queryLimit = this.conversationsLimit; const refreshToken = queryOffset === 0 ? ++this.conversationListRefreshToken : this.conversationListRefreshToken; const requestSeq = ++this.conversationListRequestSeq; this.conversationsLoading = true; try { const response = await fetch(`/api/conversations?limit=${queryLimit}&offset=${queryOffset}`); const data = await response.json(); if (data.success) { if (refreshToken !== this.conversationListRefreshToken) { debugLog('忽略已过期的对话列表响应', { requestSeq, responseOffset: queryOffset }); return; } if (queryOffset === 0) { this.conversations = data.data.conversations; } else { this.conversations.push(...data.data.conversations); } if (this.currentConversationId) { this.promoteConversationToTop(this.currentConversationId); } this.hasMoreConversations = data.data.has_more; debugLog(`已加载 ${this.conversations.length} 个对话`); if (this.conversationsOffset === 0 && !this.currentConversationId && this.conversations.length > 0) { const latestConversation = this.conversations[0]; if (latestConversation && latestConversation.id) { await this.loadConversation(latestConversation.id); } } } else { console.error('加载对话列表失败:', data.error); } } catch (error) { console.error('加载对话列表异常:', error); } finally { if (refreshToken === this.conversationListRefreshToken) { this.conversationsLoading = false; } } }, async loadMoreConversations() { if (this.loadingMoreConversations || !this.hasMoreConversations) return; this.loadingMoreConversations = true; this.conversationsOffset += this.conversationsLimit; await this.loadConversationsList(); this.loadingMoreConversations = false; }, async loadConversation(conversationId, options = {}) { const force = Boolean(options.force); debugLog('加载对话:', conversationId); traceLog('loadConversation:start', { conversationId, currentConversationId: this.currentConversationId, force }); this.logMessageState('loadConversation:start', { conversationId, force }); this.suppressTitleTyping = true; this.titleReady = false; this.currentConversationTitle = ''; this.titleTypingText = ''; if (!force && conversationId === this.currentConversationId) { debugLog('已是当前对话,跳过加载'); traceLog('loadConversation:skip-same', { conversationId }); this.suppressTitleTyping = false; this.titleReady = true; return; } // 检查是否有运行中的任务,如果有则提示用户 let hasActiveTask = false; try { const { useTaskStore } = await import('../../stores/task'); const taskStore = useTaskStore(); if (taskStore.hasActiveTask) { hasActiveTask = true; // 显示提示 const confirmed = await this.confirmAction({ title: '切换对话', message: '当前有任务正在执行,切换对话后任务会停止。确定要切换吗?', confirmText: '切换', cancelText: '取消' }); if (!confirmed) { // 用户取消切换 this.suppressTitleTyping = false; this.titleReady = true; return; } // 用户确认,停止任务 debugLog('[切换对话] 用户确认,正在停止任务...'); await taskStore.cancelTask(); taskStore.clearTask(); // 重置任务相关状态 this.streamingMessage = false; this.taskInProgress = false; this.stopRequested = false; debugLog('[切换对话] 任务已停止'); } } catch (error) { console.error('[切换对话] 检查/停止任务失败:', error); } try { // 1. 调用加载API const response = await fetch(`/api/conversations/${conversationId}/load`, { method: 'PUT' }); const result = await response.json(); if (result.success) { debugLog('对话加载API成功:', result); traceLog('loadConversation:api-success', { conversationId, title: result.title }); // 2. 更新当前对话信息 this.skipConversationHistoryReload = true; this.currentConversationId = conversationId; this.currentConversationTitle = result.title; this.titleReady = true; this.suppressTitleTyping = false; this.startTitleTyping(this.currentConversationTitle, { animate: false }); this.promoteConversationToTop(conversationId); history.pushState({ conversationId }, '', `/${this.stripConversationPrefix(conversationId)}`); this.skipConversationLoadedEvent = true; // 3. 重置UI状态 this.resetAllStates(`loadConversation:${conversationId}`); this.subAgentFetch(); this.fetchTodoList(); // 4. 立即加载历史和统计,确保列表切换后界面同步更新 await this.fetchAndDisplayHistory(); this.fetchConversationTokenStatistics(); this.updateCurrentContextTokens(); traceLog('loadConversation:after-history', { conversationId, messagesLen: Array.isArray(this.messages) ? this.messages.length : 'n/a' }); // 如果停止了任务,显示提示 if (hasActiveTask) { this.uiPushToast({ title: '任务已停止', message: '切换对话后,之前的任务已停止', type: 'info', duration: 3000 }); } } else { console.error('对话加载失败:', result.message); this.suppressTitleTyping = false; this.titleReady = true; this.uiPushToast({ title: '加载对话失败', message: result.message || '服务器未返回成功状态', type: 'error' }); } } catch (error) { console.error('加载对话异常:', error); traceLog('loadConversation:error', { conversationId, error: error?.message || String(error) }); this.suppressTitleTyping = false; this.titleReady = true; this.uiPushToast({ title: '加载对话异常', message: error.message || String(error), type: 'error' }); } }, promoteConversationToTop(conversationId) { if (!Array.isArray(this.conversations) || !conversationId) { return; } const index = this.conversations.findIndex(conv => conv && conv.id === conversationId); if (index > 0) { const [selected] = this.conversations.splice(index, 1); this.conversations.unshift(selected); } }, async createNewConversation() { debugLog('创建新对话...'); traceLog('createNewConversation:start', { currentConversationId: this.currentConversationId, convCount: Array.isArray(this.conversations) ? this.conversations.length : 'n/a' }); this.logMessageState('createNewConversation:start'); // 检查是否有运行中的任务,如果有则提示用户 let hasActiveTask = false; try { const { useTaskStore } = await import('../../stores/task'); const taskStore = useTaskStore(); if (taskStore.hasActiveTask) { hasActiveTask = true; // 显示提示 const confirmed = await this.confirmAction({ title: '创建新对话', message: '当前有任务正在执行,创建新对话后任务会停止。确定要创建吗?', confirmText: '创建', cancelText: '取消' }); if (!confirmed) { // 用户取消创建 return; } // 用户确认,停止任务 debugLog('[创建新对话] 用户确认,正在停止任务...'); await taskStore.cancelTask(); taskStore.clearTask(); // 重置任务相关状态 this.streamingMessage = false; this.taskInProgress = false; this.stopRequested = false; debugLog('[创建新对话] 任务已停止'); } } catch (error) { console.error('[创建新对话] 检查/停止任务失败:', error); } try { const response = await fetch('/api/conversations', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ thinking_mode: this.thinkingMode, mode: this.runMode }) }); const result = await response.json(); if (result.success) { const newConversationId = result.conversation_id; debugLog('新对话创建成功:', newConversationId); traceLog('createNewConversation:created', { newConversationId }); // 在本地列表插入占位,避免等待刷新 const placeholder = { id: newConversationId, title: '新对话', updated_at: new Date().toISOString(), total_messages: 0, total_tools: 0 }; this.conversations = [ placeholder, ...this.conversations.filter(conv => conv && conv.id !== newConversationId) ]; // 直接加载新对话,确保状态一致 // 如果 socket 事件已把 currentConversationId 设置为新ID,则强制加载一次以同步状态 await this.loadConversation(newConversationId, { force: true }); traceLog('createNewConversation:after-load', { newConversationId, currentConversationId: this.currentConversationId }); // 刷新对话列表获取最新统计 this.conversationsOffset = 0; await this.loadConversationsList(); traceLog('createNewConversation:after-refresh', { newConversationId, conversationsLen: Array.isArray(this.conversations) ? this.conversations.length : 'n/a' }); // 如果停止了任务,显示提示 if (hasActiveTask) { this.uiPushToast({ title: '任务已停止', message: '创建新对话后,之前的任务已停止', type: 'info', duration: 3000 }); } } else { console.error('创建对话失败:', result.message); this.uiPushToast({ title: '创建对话失败', message: result.message || '服务器未返回成功状态', type: 'error' }); } } catch (error) { console.error('创建对话异常:', error); this.uiPushToast({ title: '创建对话异常', message: error.message || String(error), type: 'error' }); } }, async deleteConversation(conversationId) { const confirmed = await this.confirmAction({ title: '删除对话', message: '确定要删除这个对话吗?删除后无法恢复。', confirmText: '删除', cancelText: '取消' }); if (!confirmed) { return; } debugLog('删除对话:', conversationId); this.logMessageState('deleteConversation:start', { conversationId }); try { const response = await fetch(`/api/conversations/${conversationId}`, { method: 'DELETE' }); const result = await response.json(); if (result.success) { debugLog('对话删除成功'); // 如果删除的是当前对话,清空界面 if (conversationId === this.currentConversationId) { this.logMessageState('deleteConversation:before-clear', { conversationId }); this.messages = []; this.logMessageState('deleteConversation:after-clear', { conversationId }); this.currentConversationId = null; this.currentConversationTitle = ''; this.resetAllStates(`deleteConversation:${conversationId}`); this.resetTokenStatistics(); history.replaceState({}, '', '/new'); } // 刷新对话列表 this.conversationsOffset = 0; await this.loadConversationsList(); } else { console.error('删除对话失败:', result.message); this.uiPushToast({ title: '删除对话失败', message: result.message || '服务器未返回成功状态', type: 'error' }); } } catch (error) { console.error('删除对话异常:', error); this.uiPushToast({ title: '删除对话异常', message: error.message || String(error), type: 'error' }); } }, async duplicateConversation(conversationId) { debugLog('复制对话:', conversationId); try { const response = await fetch(`/api/conversations/${conversationId}/duplicate`, { method: 'POST' }); const result = await response.json(); if (response.ok && result.success) { const newId = result.duplicate_conversation_id; if (newId) { this.currentConversationId = newId; } this.conversationsOffset = 0; await this.loadConversationsList(); } else { const message = result.message || result.error || '复制失败'; this.uiPushToast({ title: '复制对话失败', message, type: 'error' }); } } catch (error) { console.error('复制对话异常:', error); this.uiPushToast({ title: '复制对话异常', message: error.message || String(error), type: 'error' }); } } };