更新 static/js/app.js
This commit is contained in:
599
static/js/app.js
Normal file
599
static/js/app.js
Normal file
@ -0,0 +1,599 @@
|
||||
// static/js/app.js
|
||||
|
||||
import * as api from './api.js'; // 导入API模块
|
||||
import * as ui from './ui_manager.js'; // 导入UI管理模块
|
||||
|
||||
let currentSession = {}; // 全局变量,存储当前会话信息
|
||||
let currentRoles = []; // 全局变量,存储当前角色列表
|
||||
let currentMemories = []; // 全局变量,存储当前记忆集列表
|
||||
|
||||
// 初始化应用程序。
|
||||
// 加载所有初始数据并设置事件监听器。
|
||||
async function initializeApp() {
|
||||
console.log("应用程序初始化开始...");
|
||||
|
||||
ui.initializeUIElements();
|
||||
|
||||
// 恢复窗口宽度设置
|
||||
const savedWidth = localStorage.getItem('chatWindowWidth');
|
||||
if (savedWidth === 'full') {
|
||||
const mainContent = document.querySelector('.main-content');
|
||||
const container = document.querySelector('.container');
|
||||
mainContent.classList.add('full-width');
|
||||
container.classList.add('full-width');
|
||||
// 更新按钮图标
|
||||
const icon = ui.Elements.toggleWidthBtn.querySelector('i');
|
||||
icon.classList.remove('fa-expand-alt');
|
||||
icon.classList.add('fa-compress-alt');
|
||||
}
|
||||
|
||||
// 设置导航菜单点击事件
|
||||
ui.Elements.navItems.forEach(item => {
|
||||
item.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
ui.showSection(item.dataset.target);
|
||||
// 切换section时刷新数据
|
||||
if (item.dataset.target === 'config-section') loadConfigAndModels();
|
||||
if (item.dataset.target === 'features-section') loadFeaturesAndRoles();
|
||||
if (item.dataset.target === 'memory-section') loadMemoryAndMemories();
|
||||
if (item.dataset.target === 'log-section') loadLogs();
|
||||
});
|
||||
});
|
||||
|
||||
// 加载初始会话信息、角色和记忆列表
|
||||
await loadInitialData();
|
||||
|
||||
// 设置聊天区域事件监听
|
||||
ui.Elements.sendBtn.addEventListener('click', sendMessageHandler);
|
||||
ui.Elements.userInput.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault(); // 阻止默认换行
|
||||
sendMessageHandler();
|
||||
}
|
||||
});
|
||||
ui.Elements.clearChatHistoryBtn.addEventListener('click', clearChatLogHandler);
|
||||
ui.Elements.toggleWidthBtn.addEventListener('click', toggleChatWindowWidthHandler);
|
||||
|
||||
// 设置配置区域事件监听
|
||||
ui.Elements.saveConfigBtn.addEventListener('click', saveConfigHandler);
|
||||
ui.Elements.showApiKeyCheckbox.addEventListener('change', (e) => {
|
||||
ui.Elements.geminiApiKeyInput.type = e.target.checked ? 'text' : 'password';
|
||||
});
|
||||
|
||||
// 设置特征区域事件监听
|
||||
ui.Elements.refreshFeaturesBtn.addEventListener('click', loadFeaturesAndRoles);
|
||||
ui.Elements.saveFeaturesBtn.addEventListener('click', saveFeaturesContentHandler);
|
||||
ui.Elements.createRoleBtn.addEventListener('click', createRoleHandler);
|
||||
|
||||
// 设置记忆区域事件监听
|
||||
ui.Elements.refreshMemoryBtn.addEventListener('click', loadMemoryAndMemories);
|
||||
ui.Elements.saveMemoryBtn.addEventListener('click', saveMemoryContentHandler);
|
||||
ui.Elements.triggerMemoryUpdateBtn.addEventListener('click', triggerMemoryUpdateHandler);
|
||||
ui.Elements.createMemoryBtn.addEventListener('click', createMemoryHandler);
|
||||
|
||||
// 设置日志区域事件监听
|
||||
ui.Elements.refreshLogBtn.addEventListener('click', loadLogs);
|
||||
|
||||
// 默认显示聊天界面并加载历史
|
||||
ui.showSection('chat-section');
|
||||
await loadChatLog();
|
||||
console.log("应用程序初始化完毕。");
|
||||
}
|
||||
|
||||
// 加载初始数据:会话信息、角色列表、记忆集列表。
|
||||
async function loadInitialData() {
|
||||
try {
|
||||
currentSession = await api.fetchActiveSession();
|
||||
currentRoles = await api.fetchRoles(); // 赋值给全局变量
|
||||
currentMemories = await api.fetchMemories(); // 赋值给全局变量
|
||||
ui.updateSessionInfo(currentSession, currentRoles, currentMemories);
|
||||
ui.setMemoryUpdateStatus(currentSession.memory_status);
|
||||
console.log("初始数据加载完成。", currentSession);
|
||||
} catch (error) {
|
||||
ui.showToast(`加载初始数据失败: ${error.message}`, 'error');
|
||||
console.error("加载初始数据失败:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// 加载并渲染配置和模型列表。
|
||||
async function loadConfigAndModels() {
|
||||
try {
|
||||
const config = await api.fetchConfig();
|
||||
ui.renderConfigForm(config);
|
||||
|
||||
// fetchModels 现在直接通过后端代理获取模型列表,不再需要 baseUrl 参数
|
||||
const models = await api.fetchModels();
|
||||
ui.populateModelSelects(models, config.API.DEFAULT_GEMINI_MODEL, config.API.MEMORY_UPDATE_MODEL);
|
||||
console.log("配置和模型列表加载完成。");
|
||||
} catch (error) {
|
||||
ui.showToast(`加载配置失败: ${error.message}`, 'error');
|
||||
console.error("加载配置失败:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// 保存配置处理函数。
|
||||
async function saveConfigHandler() {
|
||||
const configData = {
|
||||
API: {
|
||||
GEMINI_API_BASE_URL: document.getElementById('gemini-api-base-url').value,
|
||||
GEMINI_API_KEY: ui.Elements.geminiApiKeyInput.value,
|
||||
DEFAULT_GEMINI_MODEL: ui.Elements.defaultGeminiModelSelect.options[ui.Elements.defaultGeminiModelSelect.selectedIndex].value,
|
||||
MEMORY_UPDATE_MODEL: ui.Elements.memoryUpdateModelSelect.options[ui.Elements.memoryUpdateModelSelect.selectedIndex].value,
|
||||
},
|
||||
Application: {
|
||||
CONTEXT_WINDOW_SIZE: parseInt(document.getElementById('context-window-size').value),
|
||||
MEMORY_RETENTION_TURNS: parseInt(document.getElementById('memory-retention-turns').value),
|
||||
MAX_SHORT_TERM_EVENTS: parseInt(document.getElementById('max-short-term-events').value),
|
||||
}
|
||||
};
|
||||
|
||||
ui.toggleLoadingState(ui.Elements.saveConfigBtn, true);
|
||||
try {
|
||||
const response = await api.saveConfig(configData);
|
||||
ui.showToast(response.message, 'success');
|
||||
await loadConfigAndModels(); // 重新加载以确保UI同步
|
||||
} catch (error) {
|
||||
ui.showToast(`保存配置失败: ${error.message}`, 'error');
|
||||
} finally {
|
||||
ui.toggleLoadingState(ui.Elements.saveConfigBtn, false);
|
||||
}
|
||||
}
|
||||
|
||||
// 加载并渲染聊天记录。
|
||||
async function loadChatLog() {
|
||||
try {
|
||||
const chatLog = await api.fetchChatLog();
|
||||
// 确保聊天消息的 role 字段从后端返回的 'ai' 转换为 'bot'
|
||||
// 并且 content 字段在渲染前经过 DOMPurify 清理和 marked 解析
|
||||
ui.renderChatHistory(chatLog.map(msg => ({
|
||||
id: msg.id, // 传递消息ID
|
||||
role: msg.role === 'user' ? 'user' : 'bot',
|
||||
content: msg.content,
|
||||
timestamp: msg.timestamp
|
||||
})));
|
||||
console.log("聊天记录加载完成。");
|
||||
} catch (error) {
|
||||
ui.showToast(`加载聊天记录失败: ${error.message}`, 'error');
|
||||
console.error("加载聊天记录失败:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// 清空聊天记录处理函数。
|
||||
async function clearChatLogHandler() {
|
||||
ui.showModal(
|
||||
"清空聊天记录",
|
||||
"确定要清空当前聊天记录吗?此操作不可逆。",
|
||||
async () => {
|
||||
ui.toggleLoadingState(ui.Elements.clearChatHistoryBtn, true);
|
||||
try {
|
||||
const response = await api.clearChatLog();
|
||||
ui.showToast(response.message, 'success');
|
||||
ui.renderChatHistory([]); // 清空UI
|
||||
currentSession.turn_counter = 0; // 重置轮次计数器
|
||||
ui.updateSessionInfo(currentSession);
|
||||
triggerMemoryUpdateHandler(); // 触发记忆更新
|
||||
console.log("聊天记录清空成功。");
|
||||
} catch (error) {
|
||||
ui.showToast(`清空聊天记录失败: ${error.message}`, 'error');
|
||||
console.error("清空聊天记录失败:", error);
|
||||
} finally {
|
||||
ui.toggleLoadingState(ui.Elements.clearChatHistoryBtn, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 发送消息处理函数。
|
||||
async function sendMessageHandler() {
|
||||
const message = ui.Elements.userInput.value.trim();
|
||||
if (!message) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 为用户消息生成一个唯一的ID
|
||||
const userMessageId = `user-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
||||
ui.addChatMessage('user', message, new Date().toISOString(), userMessageId);
|
||||
ui.Elements.userInput.value = ''; // 清空输入框
|
||||
|
||||
// 禁用发送按钮和输入框
|
||||
ui.toggleLoadingState(ui.Elements.sendBtn, true);
|
||||
ui.toggleLoadingState(ui.Elements.userInput, true);
|
||||
|
||||
// 添加一个临时的"AI 正在思考..."消息,并生成一个唯一的ID
|
||||
const thinkingMessageId = `bot-thinking-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
||||
ui.addChatMessage('bot', 'AI 正在思考...', new Date().toISOString(), thinkingMessageId);
|
||||
|
||||
// 设置重试参数
|
||||
const maxRetries = 3; // 最大重试次数
|
||||
const retryDelay = 1000; // 重试间隔(毫秒)
|
||||
let retryCount = 0;
|
||||
let success = false;
|
||||
|
||||
while (retryCount < maxRetries && !success) {
|
||||
try {
|
||||
if (retryCount > 0) {
|
||||
// 如果是重试,更新思考消息
|
||||
ui.updateChatMessageContent(thinkingMessageId, `AI 正在思考...(第${retryCount}次重试)`);
|
||||
await new Promise(resolve => setTimeout(resolve, retryDelay)); // 等待一段时间再重试
|
||||
}
|
||||
console.log("正在发送消息...", retryCount > 0 ? `(第${retryCount}次重试)` : "");
|
||||
|
||||
// 检查是否支持流式响应
|
||||
// 使用流式响应方式发送消息
|
||||
try {
|
||||
const response = await api.sendMessage(message, true); // 添加第二个参数表示使用流式响应
|
||||
|
||||
// 标记请求已成功
|
||||
success = true;
|
||||
|
||||
// 初始化流式响应处理
|
||||
let fullResponse = "";
|
||||
let isFirstChunk = true;
|
||||
|
||||
// 为响应创建一个永久的消息ID,替换思考消息
|
||||
const permanentBotMessageId = `bot-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
||||
|
||||
// 处理流式响应的每个块
|
||||
for await (const textChunk of response) {
|
||||
if (textChunk) {
|
||||
console.log('接收到文本块:', textChunk.substring(0, 50) + '...');
|
||||
if (isFirstChunk) {
|
||||
// 第一次收到数据时,替换思考消息为新的消息
|
||||
ui.removeChatMessage(thinkingMessageId);
|
||||
fullResponse = textChunk; // 初始化 fullResponse
|
||||
ui.addChatMessage('bot', fullResponse, new Date().toISOString(), permanentBotMessageId);
|
||||
isFirstChunk = false;
|
||||
} else {
|
||||
// 后续数据,累加并更新现有消息
|
||||
fullResponse += textChunk;
|
||||
ui.updateChatMessageContent(permanentBotMessageId, fullResponse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 流结束后,确保界面更新
|
||||
if (fullResponse && !isFirstChunk) {
|
||||
ui.updateChatMessageContent(permanentBotMessageId, fullResponse);
|
||||
} else if (isFirstChunk) {
|
||||
// 如果没有收到任何内容
|
||||
ui.removeChatMessage(thinkingMessageId);
|
||||
ui.addChatMessage('bot', '抱歉,未收到AI助手的响应,请重试。', new Date().toISOString(), permanentBotMessageId);
|
||||
}
|
||||
|
||||
// 流式响应结束后,重新获取会话信息以更新 turn_counter 和 memory_status
|
||||
currentSession = await api.fetchActiveSession();
|
||||
ui.updateSessionInfo(currentSession, currentRoles, currentMemories);
|
||||
triggerMemoryUpdateHandler(); // 触发记忆更新
|
||||
|
||||
|
||||
} catch (streamError) {
|
||||
console.warn("流式请求失败,回退到标准请求:", streamError);
|
||||
|
||||
// 回退到标准请求
|
||||
const response = await api.sendMessage(message, false);
|
||||
|
||||
if (response && response.success) {
|
||||
console.log("消息发送成功,收到响应:", response);
|
||||
// 成功时,更新临时消息为 AI 的实际回复
|
||||
ui.updateChatMessageContent(thinkingMessageId, response.response);
|
||||
// 更新会话信息
|
||||
currentSession.turn_counter = response.turn_counter;
|
||||
currentSession.memory_status = response.memory_status; // 更新记忆状态
|
||||
ui.updateSessionInfo(currentSession, currentRoles, currentMemories);
|
||||
success = true;
|
||||
triggerMemoryUpdateHandler(); // 触发记忆更新
|
||||
} else if (response) {
|
||||
console.warn("API返回成功但内容表示失败:", response);
|
||||
throw new Error(response.message || '发送消息失败');
|
||||
} else {
|
||||
throw new Error('服务器未返回有效响应');
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`发送消息时发生错误 (尝试 ${retryCount + 1}/${maxRetries}):`, error);
|
||||
retryCount++;
|
||||
|
||||
// 如果是最后一次尝试仍然失败
|
||||
if (retryCount >= maxRetries && !success) {
|
||||
// 移除思考消息并显示错误提示
|
||||
ui.removeChatMessage(thinkingMessageId);
|
||||
ui.showToast(`发送消息失败,已尝试 ${maxRetries} 次。请稍后再试。`, 'error');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 重新启用发送按钮和输入框
|
||||
ui.toggleLoadingState(ui.Elements.sendBtn, false);
|
||||
ui.toggleLoadingState(ui.Elements.userInput, false);
|
||||
}
|
||||
|
||||
// 加载并渲染特征内容和角色列表。
|
||||
async function loadFeaturesAndRoles() {
|
||||
try {
|
||||
const roles = await api.fetchRoles();
|
||||
const featuresContent = await api.fetchFeaturesContent();
|
||||
|
||||
ui.renderFeaturesContent(featuresContent);
|
||||
ui.renderRoleList(roles, currentSession.role_id, switchRoleHandler, deleteRoleHandler);
|
||||
console.log("特征和角色列表加载完成。");
|
||||
} catch (error) {
|
||||
ui.showToast(`加载特征或角色失败: ${error.message}`, 'error');
|
||||
console.error("加载特征或角色失败:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// 保存特征内容处理函数。
|
||||
async function saveFeaturesContentHandler() {
|
||||
let content;
|
||||
try {
|
||||
content = JSON.parse(ui.Elements.featuresContentTextarea.value);
|
||||
} catch (error) {
|
||||
ui.showToast(`特征内容格式错误,请检查 JSON 格式: ${error.message}`, 'error');
|
||||
console.error("解析特征内容失败:", error);
|
||||
return;
|
||||
}
|
||||
|
||||
ui.toggleLoadingState(ui.Elements.saveFeaturesBtn, true);
|
||||
try {
|
||||
const response = await api.saveFeaturesContent(content);
|
||||
ui.showToast(response.message, 'success');
|
||||
} catch (error) {
|
||||
ui.showToast(`保存特征内容失败: ${error.message}`, 'error');
|
||||
console.error("保存特征内容失败:", error);
|
||||
} finally {
|
||||
ui.toggleLoadingState(ui.Elements.saveFeaturesBtn, false);
|
||||
}
|
||||
}
|
||||
|
||||
// 创建新角色处理函数。
|
||||
async function createRoleHandler() {
|
||||
const roleId = ui.Elements.newRoleIdInput.value.trim();
|
||||
const roleName = ui.Elements.newRoleNameInput.value.trim();
|
||||
if (!roleId || !roleName) {
|
||||
ui.showToast("角色ID和名称不能为空。", 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
ui.toggleLoadingState(ui.Elements.createRoleBtn, true);
|
||||
try {
|
||||
const response = await api.createRole(roleId, roleName);
|
||||
ui.showToast(response.message, 'success');
|
||||
ui.Elements.newRoleIdInput.value = '';
|
||||
ui.Elements.newRoleNameInput.value = '';
|
||||
await loadFeaturesAndRoles(); // 刷新列表
|
||||
} catch (error) {
|
||||
ui.showToast(`创建角色失败: ${error.message}`, 'error');
|
||||
} finally {
|
||||
ui.toggleLoadingState(ui.Elements.createRoleBtn, false);
|
||||
}
|
||||
}
|
||||
|
||||
// 切换角色处理函数。
|
||||
// @param { string } roleId 要切换到的角色ID。
|
||||
async function switchRoleHandler(roleId) {
|
||||
if (roleId === currentSession.role_id) {
|
||||
ui.showToast("当前角色已是此角色。", 'info');
|
||||
return;
|
||||
}
|
||||
ui.showModal(
|
||||
"切换角色",
|
||||
`确定要切换到角色 "${roleId}" 吗?这会重置当前对话。`,
|
||||
async (confirmBtn) => { // 传入确认按钮元素
|
||||
ui.toggleLoadingState(confirmBtn, true); // 禁用确认按钮
|
||||
try {
|
||||
const response = await api.setActiveSession(roleId, currentSession.memory_id);
|
||||
currentSession = response;
|
||||
await loadInitialData();
|
||||
await loadFeaturesAndRoles();
|
||||
await loadChatLog();
|
||||
triggerMemoryUpdateHandler(); // 触发记忆更新
|
||||
ui.showToast(`已切换到角色 "${roleId}"`, 'success');
|
||||
} catch (error) {
|
||||
ui.showToast(`切换角色失败: ${error.message}`, 'error');
|
||||
console.error("切换角色失败:", error);
|
||||
} finally {
|
||||
ui.toggleLoadingState(confirmBtn, false); // 重新启用确认按钮
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// 删除角色处理函数。
|
||||
// @param { string } roleId 要删除的角色ID。
|
||||
async function deleteRoleHandler(roleId) {
|
||||
if (roleId === currentSession.role_id) {
|
||||
ui.showToast("不能删除当前活跃的角色!", 'error');
|
||||
return;
|
||||
}
|
||||
ui.showModal(
|
||||
"删除角色",
|
||||
`确定要删除角色 "${roleId}" 吗?此操作可逆。`,
|
||||
async (confirmBtn) => { // 传入确认按钮元素
|
||||
ui.toggleLoadingState(confirmBtn, true); // 禁用确认按钮
|
||||
try {
|
||||
const response = await api.deleteRole(roleId);
|
||||
ui.showToast(response.message, 'success');
|
||||
await loadFeaturesAndRoles(); // 刷新列表
|
||||
} catch (error) {
|
||||
ui.showToast(`删除角色失败: ${error.message}`, 'error');
|
||||
} finally {
|
||||
ui.toggleLoadingState(confirmBtn, false); // 重新启用确认按钮
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// 加载并渲染记忆内容和记忆集列表。
|
||||
async function loadMemoryAndMemories() {
|
||||
try {
|
||||
const memories = await api.fetchMemories();
|
||||
const memoryContent = await api.fetchMemoryContent();
|
||||
|
||||
ui.renderMemoryContent(memoryContent);
|
||||
ui.renderMemoryList(memories, currentSession.memory_id, switchMemoryHandler, deleteMemoryHandler);
|
||||
console.log("记忆内容和记忆集列表加载完成。");
|
||||
} catch (error) {
|
||||
ui.showToast(`加载记忆或记忆集失败: ${error.message}`, 'error');
|
||||
console.error("加载记忆或记忆集失败:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// 保存记忆内容处理函数。
|
||||
async function saveMemoryContentHandler() {
|
||||
let content;
|
||||
try {
|
||||
content = JSON.parse(ui.Elements.memoryContentTextarea.value);
|
||||
} catch (error) {
|
||||
ui.showToast(`记忆内容格式错误,请检查 JSON 格式: ${error.message}`, 'error');
|
||||
console.error("解析记忆内容失败:", error);
|
||||
return;
|
||||
}
|
||||
|
||||
ui.toggleLoadingState(ui.Elements.saveMemoryBtn, true);
|
||||
try {
|
||||
const response = await api.saveMemoryContent(content);
|
||||
ui.showToast(response.message, 'success');
|
||||
} catch (error) {
|
||||
ui.showToast(`保存记忆内容失败: ${error.message}`, 'error');
|
||||
console.error("保存记忆内容失败:", error);
|
||||
} finally {
|
||||
ui.toggleLoadingState(ui.Elements.saveMemoryBtn, false);
|
||||
}
|
||||
}
|
||||
|
||||
// 创建新记忆集处理函数。
|
||||
async function createMemoryHandler() {
|
||||
const memoryId = ui.Elements.newMemoryIdInput.value.trim();
|
||||
const memoryName = ui.Elements.newMemoryNameInput.value.trim();
|
||||
if (!memoryId || !memoryName) {
|
||||
ui.showToast("记忆集ID和名称不能为空。", 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
ui.toggleLoadingState(ui.Elements.createMemoryBtn, true);
|
||||
try {
|
||||
const response = await api.createMemory(memoryId, memoryName);
|
||||
ui.showToast(response.message, 'success');
|
||||
ui.Elements.newMemoryIdInput.value = '';
|
||||
ui.Elements.newMemoryNameInput.value = '';
|
||||
await loadMemoryAndMemories(); // 刷新列表
|
||||
} catch (error) {
|
||||
ui.showToast(`创建记忆集失败: ${error.message}`, 'error');
|
||||
} finally {
|
||||
ui.toggleLoadingState(ui.Elements.createMemoryBtn, false);
|
||||
}
|
||||
}
|
||||
|
||||
// 切换记忆集处理函数。
|
||||
// @param { string } memoryId 要切换到的记忆集ID。
|
||||
async function switchMemoryHandler(memoryId) {
|
||||
if (memoryId === currentSession.memory_id) {
|
||||
ui.showToast("当前记忆集是此记忆集。", 'info');
|
||||
return;
|
||||
}
|
||||
ui.showModal(
|
||||
"切换记忆集",
|
||||
`确定要切换到记忆集 "${memoryId}" 吗?这会重置当前对话。`,
|
||||
async (confirmBtn) => { // 传入确认按钮元素
|
||||
ui.toggleLoadingState(confirmBtn, true); // 禁用确认按钮
|
||||
try {
|
||||
const response = await api.setActiveSession(currentSession.role_id, memoryId);
|
||||
currentSession = response;
|
||||
await loadInitialData();
|
||||
await loadMemoryAndMemories();
|
||||
await loadChatLog();
|
||||
triggerMemoryUpdateHandler(); // 触发记忆更新
|
||||
ui.showToast(`已切换到记忆集 "${memoryId}"`, 'success');
|
||||
} catch (error) {
|
||||
ui.showToast(`切换记忆集失败: ${error.message}`, 'error');
|
||||
console.error("切换记忆集失败:", error);
|
||||
} finally {
|
||||
ui.toggleLoadingState(confirmBtn, false); // 重新启用确认按钮
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// 删除记忆集处理函数。
|
||||
// @param { string } memoryId 要删除的记忆集ID。
|
||||
async function deleteMemoryHandler(memoryId) {
|
||||
if (memoryId === currentSession.memory_id) {
|
||||
ui.showToast("不能删除当前活跃的记忆集!", 'error');
|
||||
return;
|
||||
}
|
||||
ui.showModal(
|
||||
"删除记忆集",
|
||||
`确定要删除记忆集 "${memoryId}" 吗?此操作不可逆。`,
|
||||
async (confirmBtn) => { // 传入确认按钮元素
|
||||
ui.toggleLoadingState(confirmBtn, true); // 禁用确认按钮
|
||||
try {
|
||||
const response = await api.deleteMemory(memoryId);
|
||||
ui.showToast(response.message, 'success');
|
||||
await loadMemoryAndMemories(); // 刷新列表
|
||||
} catch (error) {
|
||||
ui.showToast(`删除记忆集失败: ${error.message}`, 'error');
|
||||
} finally {
|
||||
ui.toggleLoadingState(confirmBtn, false); // 重新启用确认按钮
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// 触发记忆更新处理函数。
|
||||
async function triggerMemoryUpdateHandler() {
|
||||
ui.toggleLoadingState(ui.Elements.triggerMemoryUpdateBtn, true);
|
||||
ui.setMemoryUpdateStatus('updating'); // 设置为updating状态
|
||||
ui.Elements.memoryUpdateStatusText.textContent = '记忆整理中 (手动)...';
|
||||
try {
|
||||
const response = await api.triggerMemoryUpdate();
|
||||
ui.showToast(response.message, 'info');
|
||||
// 记忆更新是异步的,触发后需要重新获取会话信息来更新状态
|
||||
currentSession = await api.fetchActiveSession();
|
||||
ui.setMemoryUpdateStatus(currentSession.memory_status);
|
||||
} catch (error) {
|
||||
ui.showToast(`触发记忆更新失败: ${error.message}`, 'error');
|
||||
console.error("触发记忆更新失败:", error);
|
||||
ui.setMemoryUpdateStatus('error'); // 触发失败则显示错误状态
|
||||
} finally {
|
||||
ui.toggleLoadingState(ui.Elements.triggerMemoryUpdateBtn, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 加载并渲染日志内容。
|
||||
async function loadLogs() {
|
||||
try {
|
||||
const logContent = await api.fetchLogs(); // 通过API获取日志
|
||||
ui.renderLogContent(logContent);
|
||||
console.log("日志加载完成。");
|
||||
} catch (error) {
|
||||
ui.showToast(`加载日志失败: ${error.message}`, 'error');
|
||||
console.error("加载日志失败:", error);
|
||||
ui.renderLogContent(`加载日志失败: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 切换聊天窗口宽度处理函数
|
||||
function toggleChatWindowWidthHandler() {
|
||||
const mainContent = document.querySelector('.main-content');
|
||||
const container = document.querySelector('.container');
|
||||
|
||||
// 同时切换主内容区和容器的全宽类
|
||||
mainContent.classList.toggle('full-width');
|
||||
container.classList.toggle('full-width');
|
||||
|
||||
// 切换按钮图标
|
||||
const icon = ui.Elements.toggleWidthBtn.querySelector('i');
|
||||
if (mainContent.classList.contains('full-width')) {
|
||||
icon.classList.remove('fa-expand-alt');
|
||||
icon.classList.add('fa-compress-alt');
|
||||
localStorage.setItem('chatWindowWidth', 'full');
|
||||
} else {
|
||||
icon.classList.remove('fa-compress-alt');
|
||||
icon.classList.add('fa-expand-alt');
|
||||
localStorage.setItem('chatWindowWidth', 'default');
|
||||
}
|
||||
}
|
||||
|
||||
// 应用程序启动
|
||||
document.addEventListener('DOMContentLoaded', initializeApp);
|
Reference in New Issue
Block a user