Gem2a / app /static /js /session.js
misonL's picture
Force clear current session key on init
cbb8ecb
Raw
History Blame Contribute Delete
12.9 kB
// 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 = `
<div class="message-content">${content}</div>
`;
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 = ''; // 请求出错也清空聊天窗口
}
}