// app/static/js/session.js - 会话管理相关逻辑 import { AUTH_TOKEN_STORAGE_KEY, CURRENT_SESSION_KEY, SESSION_LIST_KEY } from './constants.js'; /** * 初始化标签式会话管理UI。 * @param {HTMLElement} chatWindow - 聊天窗口元素。 */ export async function initTabbedSessionManagement(chatWindow) { console.log("DEBUG: initTabbedSessionManagement 开始执行。"); // 调试日志 // 获取会话标签栏容器及其子元素 const sessionTabBar = document.getElementById('session-tab-bar'); const newSessionBtn = document.getElementById('new-session-btn'); const deleteSessionBtn = document.getElementById('delete-session-btn'); // 添加事件监听 if (newSessionBtn) { newSessionBtn.addEventListener('click', () => createNewSession(chatWindow)); console.log("DEBUG: '新建会话' 按钮事件监听已添加。"); // 调试日志 } if (deleteSessionBtn) { deleteSessionBtn.addEventListener('click', () => deleteCurrentSession(chatWindow)); console.log("DEBUG: '删除当前' 按钮事件监听已添加。"); // 调试日志 } // 强制清除 CURRENT_SESSION_KEY,确保每次加载都检查并创建新会话 localStorage.removeItem(CURRENT_SESSION_KEY); console.log("DEBUG: 强制清除 CURRENT_SESSION_KEY。"); // 调试日志 // 加载会话列表 console.log("DEBUG: 调用 loadSessions() 加载会话列表。"); // 调试日志 await loadSessions(); // 确保会话加载完成后再检查 // 确保总是有一个活动会话 let currentSessionId = localStorage.getItem(CURRENT_SESSION_KEY); console.log(`DEBUG: 当前会话ID (localStorage): ${currentSessionId}`); // 调试日志 if (!currentSessionId) { console.log("DEBUG: 未检测到当前会话,自动创建新会话..."); const created = await createNewSession(chatWindow); // 获取创建结果 console.log(`DEBUG: createNewSession 返回: ${created}`); // 调试日志 if (created) { console.log("DEBUG: 新会话创建成功,重新加载会话列表。"); // 调试日志 currentSessionId = localStorage.getItem(CURRENT_SESSION_KEY); // 重新获取当前会话ID await loadSessions(); // 重新加载会话列表,确保最新数据 // renderSessionTabs() 在 loadSessions() 内部已经调用,无需在此处重复调用 } else { console.error("ERROR: 自动创建新会话失败。"); // 调试日志 } } else { console.log(`DEBUG: 检测到当前会话ID: ${currentSessionId},加载其历史消息。`); // 调试日志 // 加载当前会话的历史消息 await loadSessionHistory(currentSessionId); } console.log("DEBUG: initTabbedSessionManagement 执行完毕。"); // 调试日志 // 移除此处的 renderSessionTabs(),因为它在 loadSessions() 内部已经调用 // renderSessionTabs(); } /** * 创建新会话。 * @param {HTMLElement} chatWindow - 聊天窗口元素。 */ export async function createNewSession(chatWindow) { const authToken = localStorage.getItem(AUTH_TOKEN_STORAGE_KEY); let sessionName; let threadId; if (authToken) { try { const response = await fetch('/v1/sessions', { method: 'POST', headers: { 'X-Auth-Token': authToken } }); if (response.ok) { const data = await response.json(); console.log('会话创建成功,响应数据:', data); // 添加日志 threadId = data.thread_id; sessionName = data.name; } else { const errorText = await response.text(); // 获取错误响应文本 console.error(`创建会话失败: ${response.status} ${response.statusText}, 响应: ${errorText}`); // 添加详细错误日志 // 如果后端创建失败,尝试创建本地会话 console.log("DEBUG: 后端会话创建失败,尝试创建本地会话。"); } } catch (error) { console.error('创建会话请求出错:', error); // 修改日志信息 // 如果请求出错,尝试创建本地会话 console.log("DEBUG: 创建会话请求出错,尝试创建本地会话。"); } } // 如果没有认证令牌,或者后端会话创建失败,则创建本地会话 if (!threadId) { threadId = `local-session-${Date.now()}`; // 生成一个本地会话ID const now = new Date(); const year = now.getFullYear(); const month = (now.getMonth() + 1).toString().padStart(2, '0'); const day = now.getDate().toString().padStart(2, '0'); const hours = now.getHours().toString().padStart(2, '0'); const minutes = now.getMinutes().toString().padStart(2, '0'); sessionName = `本地会话-${year}${month}${day}-${hours}${minutes}`; // 例如: 本地会话-20250531-1605 console.log(`DEBUG: 生成本地会话名称: ${sessionName}`); // 调试日志 } localStorage.setItem(CURRENT_SESSION_KEY, threadId); saveSessionToList(threadId, sessionName); // 使用生成的名称 console.log('会话ID已保存到localStorage:', localStorage.getItem(CURRENT_SESSION_KEY)); // 添加日志 chatWindow.innerHTML = ''; // 清空聊天窗口 renderSessionTabs(); // 立即渲染会话标签,确保新会话显示 return true; // 返回成功状态,因为至少创建了本地会话 } /** * 删除当前会话。 * @param {HTMLElement} chatWindow - 聊天窗口元素。 */ export async function deleteCurrentSession(chatWindow) { const authToken = localStorage.getItem(AUTH_TOKEN_STORAGE_KEY); const sessionId = localStorage.getItem(CURRENT_SESSION_KEY); if (!authToken || !sessionId) return; try { const response = await fetch(`/v1/sessions/${sessionId}`, { method: 'DELETE', headers: { 'X-Auth-Token': authToken } }); if (response.ok) { removeSessionFromList(sessionId); localStorage.removeItem(CURRENT_SESSION_KEY); loadSessions(); chatWindow.innerHTML = ''; // 清空聊天窗口 } } catch (error) { console.error('删除会话失败:', error); } } /** * 保存会话到列表。 * @param {string} sessionId - 会话ID。 * @param {string} name - 会话名称。 */ export function saveSessionToList(sessionId, name) { const sessions = JSON.parse(localStorage.getItem(SESSION_LIST_KEY) || '{}'); sessions[sessionId] = { name: name, created: Date.now() }; localStorage.setItem(SESSION_LIST_KEY, JSON.stringify(sessions)); } /** * 从列表中移除会话。 * @param {string} sessionId - 要移除的会话ID。 */ export function removeSessionFromList(sessionId) { const sessions = JSON.parse(localStorage.getItem(SESSION_LIST_KEY) || '{}'); delete sessions[sessionId]; localStorage.setItem(SESSION_LIST_KEY, JSON.stringify(sessions)); } /** * 渲染会话标签 */ function renderSessionTabs() { const sessions = JSON.parse(localStorage.getItem(SESSION_LIST_KEY) || '{}'); const tabsContainer = document.getElementById('session-tabs'); tabsContainer.innerHTML = ''; Object.keys(sessions).forEach(sessionId => { const session = sessions[sessionId]; // 计算剩余时间 const hoursLeft = Math.max(0, 24 - Math.floor((Date.now() - session.created) / 3600000)); const isCurrent = sessionId === localStorage.getItem(CURRENT_SESSION_KEY); const tab = document.createElement('button'); tab.className = `session-tab ${isCurrent ? 'active' : ''}`; tab.dataset.id = sessionId; tab.textContent = `${session.name} (${hoursLeft}h)`; tab.addEventListener('click', () => { switchSession(sessionId); }); tabsContainer.appendChild(tab); }); // 更新状态提示 const statusBar = document.getElementById('session-status'); const sessionCount = Object.keys(sessions).length; statusBar.innerHTML = `会话数: ${sessionCount}/5`; if (sessionCount >= 5) { statusBar.innerHTML += ' (已达上限)'; statusBar.style.color = 'red'; } } /** * 加载会话列表(标签版)。 */ export async function loadSessions() { // 总是先从 localStorage 加载并渲染会话,包括本地会话 renderSessionTabs(); // 首次渲染,显示本地已保存的会话 const authToken = localStorage.getItem(AUTH_TOKEN_STORAGE_KEY); if (!authToken) { console.log("DEBUG: 无认证令牌,跳过从后端获取会话列表。"); // 调试日志 return; // 如果没有认证令牌,则不尝试从后端获取 } // 从后端获取最新会话列表 try { const response = await fetch('/v1/sessions', { headers: { 'X-Auth-Token': authToken } }); if (response.ok) { const sessions = await response.json(); const sessionMap = {}; sessions.forEach(session => { sessionMap[session.thread_id] = { name: session.name, created: session.created_at * 1000 }; }); localStorage.setItem(SESSION_LIST_KEY, JSON.stringify(sessionMap)); // 重新渲染会话标签,以包含从后端获取的最新数据 renderSessionTabs(); } else { const errorText = await response.text(); console.error(`获取会话列表失败: ${response.status} ${response.statusText}, 响应: ${errorText}`); } } catch (error) { console.error('获取会话列表请求出错:', error); } } /** * 切换会话。 * @param {string} sessionId - 要切换到的会话ID。 */ export function switchSession(sessionId) { const chatWindow = document.getElementById('chat-window'); localStorage.setItem(CURRENT_SESSION_KEY, sessionId); chatWindow.innerHTML = ''; // 清空聊天窗口 loadSessions(); // 加载该会话的历史消息 loadSessionHistory(sessionId); } /** * 加载指定会话的历史消息。 * @param {string} sessionId - 要加载历史消息的会话ID。 */ async function loadSessionHistory(sessionId) { const chatWindow = document.getElementById('chat-window'); // 如果是本地会话,不尝试从后端加载历史 if (sessionId.startsWith('local-session-')) { console.log(`DEBUG: 加载本地会话 ${sessionId},清空聊天窗口。`); // 调试日志 chatWindow.innerHTML = ''; // 清空聊天窗口 return; } const authToken = localStorage.getItem(AUTH_TOKEN_STORAGE_KEY); if (!authToken) { console.log("DEBUG: 无认证令牌,无法加载后端会话历史。"); // 调试日志 chatWindow.innerHTML = ''; // 清空聊天窗口 return; } try { const response = await fetch(`/v1/sessions/${sessionId}/history`, { headers: { 'X-Auth-Token': authToken } }); if (response.ok) { const data = await response.json(); // 遍历历史消息并添加到聊天窗口 for (const message of data.history) { const messageElement = document.createElement('div'); messageElement.className = `chat-message ${message.role === 'user' ? 'user-message' : 'ai-message'}`; let content = ''; if (message.parts && message.parts.length > 0) { // 提取文本内容 content = message.parts.map(part => part.text).join(''); } else if (message.text) { // 直接使用文本内容 content = message.text; } messageElement.innerHTML = `
`; chatWindow.appendChild(messageElement); } // 滚动到底部 chatWindow.scrollTop = chatWindow.scrollHeight; } else { const errorText = await response.text(); console.error(`加载会话历史失败: ${response.status} ${response.statusText}, 响应: ${errorText}`); chatWindow.innerHTML = ''; // 加载失败也清空聊天窗口 } } catch (error) { console.error('加载会话历史请求出错:', error); chatWindow.innerHTML = ''; // 请求出错也清空聊天窗口 } }