/**
* 会话管理器模块
*
* 负责管理设备连接会话,包括保存会话配置、加载会话和会话组管理
*/
class SessionManager {
constructor() {
this.activeSessions = {}; // 当前活跃会话
this.savedSessions = {}; // 保存的会话配置
this.sessionGroups = {}; // 会话组
// 从本地存储加载保存的会话配置
this.loadFromStorage();
}
/**
* 将会话配置保存到本地存储
*/
saveToStorage() {
localStorage.setItem('netbrain_saved_sessions', JSON.stringify(this.savedSessions));
localStorage.setItem('netbrain_session_groups', JSON.stringify(this.sessionGroups));
}
/**
* 从本地存储加载会话配置
*/
loadFromStorage() {
try {
const savedSessions = localStorage.getItem('netbrain_saved_sessions');
const sessionGroups = localStorage.getItem('netbrain_session_groups');
if (savedSessions) {
this.savedSessions = JSON.parse(savedSessions);
}
if (sessionGroups) {
this.sessionGroups = JSON.parse(sessionGroups);
}
} catch (error) {
console.error('加载会话配置失败:', error);
}
}
/**
* 注册活跃会话
* @param {string} sessionId 会话ID
* @param {Object} sessionInfo 会话信息对象
*/
registerActiveSession(sessionId, sessionInfo) {
// 检查是否已存在相同的会话
// 防止重复注册同一个设备和凭据的会话
if (sessionInfo.deviceId && sessionInfo.credentialId) {
const existingSessionIds = Object.keys(this.activeSessions);
for (const existingId of existingSessionIds) {
const existingSession = this.activeSessions[existingId];
// 如果发现相同设备和凭据的活跃会话,则移除旧会话
if (existingSession.deviceId === sessionInfo.deviceId &&
existingSession.credentialId === sessionInfo.credentialId &&
existingId !== sessionId) {
console.log(`发现重复会话,正在移除旧会话 ${existingId}`);
delete this.activeSessions[existingId];
}
}
}
// 注册新会话
this.activeSessions[sessionId] = {
...sessionInfo,
startTime: sessionInfo.startTime || new Date(),
status: sessionInfo.status || 'active'
};
// 输出调试信息
console.log(`注册会话: ${sessionId}, 当前活跃会话数: ${Object.keys(this.activeSessions).length}`);
this.notifySessionListeners();
}
/**
* 注销活跃会话
* @param {string} sessionId 会话ID
*/
unregisterActiveSession(sessionId) {
if (this.activeSessions[sessionId]) {
delete this.activeSessions[sessionId];
this.notifySessionListeners();
}
}
/**
* 更新会话状态
* @param {string} sessionId 会话ID
* @param {string} status 状态
*/
updateSessionStatus(sessionId, status) {
if (this.activeSessions[sessionId]) {
this.activeSessions[sessionId].status = status;
this.notifySessionListeners();
}
}
/**
* 保存会话配置
* @param {string} name 会话名称
* @param {Object} config 会话配置
*/
saveSession(name, config) {
this.savedSessions[name] = {
...config,
createdAt: new Date().toISOString()
};
this.saveToStorage();
this.notifySessionListeners();
return true;
}
/**
* 删除保存的会话配置
* @param {string} name 会话名称
*/
deleteSession(name) {
if (this.savedSessions[name]) {
delete this.savedSessions[name];
// 同时从会话组中移除
for (const groupName in this.sessionGroups) {
const group = this.sessionGroups[groupName];
const index = group.sessions.indexOf(name);
if (index !== -1) {
group.sessions.splice(index, 1);
}
}
this.saveToStorage();
this.notifySessionListeners();
return true;
}
return false;
}
/**
* 创建会话组
* @param {string} groupName 组名称
* @param {Array} sessions 会话列表
*/
createSessionGroup(groupName, sessions = []) {
this.sessionGroups[groupName] = {
name: groupName,
sessions: sessions,
createdAt: new Date().toISOString()
};
this.saveToStorage();
this.notifySessionListeners();
return true;
}
/**
* 更新会话组
* @param {string} groupName 组名称
* @param {Array} sessions 会话列表
*/
updateSessionGroup(groupName, sessions) {
if (this.sessionGroups[groupName]) {
this.sessionGroups[groupName].sessions = sessions;
this.saveToStorage();
this.notifySessionListeners();
return true;
}
return false;
}
/**
* 删除会话组
* @param {string} groupName 组名称
*/
deleteSessionGroup(groupName) {
if (this.sessionGroups[groupName]) {
delete this.sessionGroups[groupName];
this.saveToStorage();
this.notifySessionListeners();
return true;
}
return false;
}
/**
* 获取所有活跃会话
* @returns {Object} 活跃会话对象
*/
getActiveSessions() {
return this.activeSessions;
}
/**
* 获取所有保存的会话配置
* @returns {Object} 保存的会话配置
*/
getSavedSessions() {
return this.savedSessions;
}
/**
* 获取所有会话组
* @returns {Object} 会话组对象
*/
getSessionGroups() {
return this.sessionGroups;
}
// 会话变更监听器列表
sessionListeners = [];
/**
* 添加会话变更监听器
* @param {Function} listener 监听器函数
*/
addSessionListener(listener) {
this.sessionListeners.push(listener);
}
/**
* 移除会话变更监听器
* @param {Function} listener 监听器函数
*/
removeSessionListener(listener) {
const index = this.sessionListeners.indexOf(listener);
if (index !== -1) {
this.sessionListeners.splice(index, 1);
}
}
/**
* 通知所有会话监听器
*/
notifySessionListeners() {
const data = {
activeSessions: this.activeSessions,
savedSessions: this.savedSessions,
sessionGroups: this.sessionGroups
};
this.sessionListeners.forEach(listener => {
try {
listener(data);
} catch (error) {
console.error('会话监听器执行错误:', error);
}
});
}
/**
* 连接到设备
* @param {string} deviceId 设备ID
* @param {string} credentialId 凭据ID
* @param {string} name 会话名称
* @returns {Object} 连接信息
*/
async connectToDevice(deviceId, credentialId, name) {
// 会话ID使用设备ID和凭据ID组合
const sessionId = `${deviceId}_${credentialId}_${Date.now()}`;
// 创建会话记录
const sessionInfo = {
id: sessionId,
name: name,
deviceId: deviceId,
credentialId: credentialId,
startTime: new Date(),
status: 'connecting'
};
// 注册活跃会话
this.registerActiveSession(sessionId, sessionInfo);
return sessionInfo;
}
}
/**
* 会话管理UI类
*
* 处理会话管理相关的UI交互
*/
class SessionManagerUI {
constructor() {
// 获取DOM元素
this.activeSessionsList = document.getElementById('active-sessions-list');
this.savedSessionsList = document.getElementById('saved-sessions-list');
this.sessionGroupsList = document.getElementById('session-groups-list');
// 获取会话操作按钮
this.newSessionBtn = document.getElementById('new-session-btn');
this.saveSessionBtn = document.getElementById('save-session-btn');
this.manageGroupsBtn = document.getElementById('manage-groups-btn');
// 绑定按钮事件
if (this.newSessionBtn) {
this.newSessionBtn.addEventListener('click', () => {
// 显示连接对话框
window.terminalManager.showConnectDialog();
});
}
if (this.saveSessionBtn) {
this.saveSessionBtn.addEventListener('click', () => {
this.showSaveSessionDialog();
});
}
if (this.manageGroupsBtn) {
this.manageGroupsBtn.addEventListener('click', () => {
this.showManageGroupsDialog();
});
}
// 绑定保存会话表单提交事件
const saveSessionForm = document.getElementById('save-session-form');
if (saveSessionForm) {
saveSessionForm.addEventListener('submit', (e) => {
e.preventDefault();
this.saveSession();
});
}
// 绑定创建组按钮事件
const createGroupBtn = document.getElementById('create-group-btn');
if (createGroupBtn) {
createGroupBtn.addEventListener('click', () => {
this.createSessionGroup();
});
}
// 绑定对话框关闭按钮事件
document.querySelectorAll('.modal .close, .modal .close-btn').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelectorAll('.modal').forEach(modal => {
modal.style.display = 'none';
});
});
});
// 添加会话变更监听器
if (window.sessionManager) {
window.sessionManager.addSessionListener(this.updateUI.bind(this));
}
// 初始化UI
this.updateUI({
activeSessions: window.sessionManager?.getActiveSessions() || {},
savedSessions: window.sessionManager?.getSavedSessions() || {},
sessionGroups: window.sessionManager?.getSessionGroups() || {}
});
}
// 更新UI
updateUI(data) {
const { activeSessions, savedSessions, sessionGroups } = data;
// 更新活跃会话列表
this.updateActiveSessionsList(activeSessions);
// 更新保存的会话列表
this.updateSavedSessionsList(savedSessions);
// 更新会话组列表
this.updateSessionGroupsList(sessionGroups, savedSessions);
}
// 更新活跃会话列表
updateActiveSessionsList(activeSessions) {
if (!this.activeSessionsList) return;
this.activeSessionsList.innerHTML = '';
const sessionIds = Object.keys(activeSessions);
if (sessionIds.length === 0) {
this.activeSessionsList.innerHTML = '<div class="session-empty">暂无活跃会话</div>';
return;
}
// 使用Set避免重复会话
const processedSessions = new Set();
sessionIds.forEach(sessionId => {
// 避免重复显示会话
if (processedSessions.has(sessionId)) {
console.warn(`会话ID ${sessionId} 已存在,避免重复显示`);
return;
}
processedSessions.add(sessionId);
const session = activeSessions[sessionId];
const sessionItem = document.createElement('div');
sessionItem.className = 'session-item';
sessionItem.dataset.sessionId = sessionId; // 添加会话ID作为数据属性
// 状态指示灯
let statusClass = '';
switch (session.status) {
case 'active':
statusClass = 'status-active';
break;
case 'connecting':
statusClass = 'status-connecting';
break;
case 'disconnected':
statusClass = 'status-disconnected';
break;
case 'error':
statusClass = 'status-error';
break;
default:
statusClass = 'status-connecting';
break;
}
sessionItem.innerHTML = `
<div class="session-item-content">
<div class="session-item-title">
<span class="status-indicator ${statusClass}"></span>
${session.name}
</div>
<div class="session-item-details">
${new Date(session.startTime).toLocaleTimeString()}
</div>
</div>
<div class="session-item-actions">
<button class="session-item-action save-session" title="保存会话">
<i class="fas fa-save"></i>
</button>
<button class="session-item-action close-session" title="关闭会话" data-session-id="${sessionId}">
<i class="fas fa-times"></i>
</button>
</div>
`;
// 保存会话按钮
sessionItem.querySelector('.save-session').addEventListener('click', () => {
this.showSaveSessionDialog(session);
});
// 关闭会话按钮 - 修复关闭逻辑
sessionItem.querySelector('.close-session').addEventListener('click', (event) => {
const clickedSessionId = event.currentTarget.dataset.sessionId;
console.log(`关闭会话: ${clickedSessionId}`);
const termInfo = window.terminalManager.getTerminalById(clickedSessionId);
if (termInfo) {
window.terminalManager.closeTerminal(clickedSessionId);
} else {
// 如果没有找到对应的终端,直接注销会话
window.sessionManager.unregisterActiveSession(clickedSessionId);
// 手动更新UI
this.updateActiveSessionsList(window.sessionManager.getActiveSessions());
}
});
this.activeSessionsList.appendChild(sessionItem);
});
}
// 更新保存的会话列表
updateSavedSessionsList(savedSessions) {
if (!this.savedSessionsList) return;
this.savedSessionsList.innerHTML = '';
const sessionNames = Object.keys(savedSessions);
if (sessionNames.length === 0) {
this.savedSessionsList.innerHTML = '<div class="session-empty">暂无保存的会话</div>';
return;
}
sessionNames.forEach(sessionName => {
const session = savedSessions[sessionName];
const sessionItem = document.createElement('div');
sessionItem.className = 'session-item';
sessionItem.innerHTML = `
<div class="session-item-content">
<div class="session-item-title">${sessionName}</div>
<div class="session-item-details">
${session.description || ''}
</div>
</div>
<div class="session-item-actions">
<button class="session-item-action connect-session" title="连接会话">
<i class="fas fa-plug"></i>
</button>
<button class="session-item-action delete-session" title="删除会话">
<i class="fas fa-trash"></i>
</button>
</div>
`;
// 连接会话按钮
sessionItem.querySelector('.connect-session').addEventListener('click', () => {
if (session.deviceId && session.credentialId) {
// 创建连接
window.terminalManager.createTerminal(
sessionName,
session.deviceId,
session.credentialId
);
} else {
alert('无法连接会话:缺少设备ID或凭据ID');
}
});
// 删除会话按钮
sessionItem.querySelector('.delete-session').addEventListener('click', () => {
if (confirm(`确定要删除保存的会话"${sessionName}"吗?`)) {
window.sessionManager.deleteSession(sessionName);
}
});
this.savedSessionsList.appendChild(sessionItem);
});
}
// 更新会话组列表
updateSessionGroupsList(sessionGroups, savedSessions) {
if (!this.sessionGroupsList) return;
this.sessionGroupsList.innerHTML = '';
const groupNames = Object.keys(sessionGroups);
if (groupNames.length === 0) {
this.sessionGroupsList.innerHTML = '<div class="session-empty">暂无会话组</div>';
return;
}
groupNames.forEach(groupName => {
const group = sessionGroups[groupName];
const groupItem = document.createElement('div');
groupItem.className = 'session-group-item';
// 组标题
const groupHeader = document.createElement('div');
groupHeader.className = 'session-group-header';
groupHeader.innerHTML = `
<div class="session-item-title">
<i class="fas fa-folder"></i> ${groupName}
</div>
<div class="session-item-actions">
<button class="session-item-action toggle-group">
<i class="fas fa-chevron-down"></i>
</button>
</div>
`;
// 组内容
const groupContent = document.createElement('div');
groupContent.className = 'session-group-content';
// 添加组内会话
if (group.sessions.length === 0) {
groupContent.innerHTML = '<div class="session-empty">此组中没有会话</div>';
} else {
group.sessions.forEach(sessionName => {
if (savedSessions[sessionName]) {
const session = savedSessions[sessionName];
const sessionItem = document.createElement('div');
sessionItem.className = 'session-item';
sessionItem.innerHTML = `
<div class="session-item-content">
<div class="session-item-title">${sessionName}</div>
</div>
<div class="session-item-actions">
<button class="session-item-action connect-session" title="连接会话">
<i class="fas fa-plug"></i>
</button>
</div>
`;
// 连接会话按钮
sessionItem.querySelector('.connect-session').addEventListener('click', () => {
if (session.deviceId && session.credentialId) {
// 创建连接
window.terminalManager.createTerminal(
sessionName,
session.deviceId,
session.credentialId
);
} else {
alert('无法连接会话:缺少设备ID或凭据ID');
}
});
groupContent.appendChild(sessionItem);
}
});
}
// 切换组展开/折叠
groupHeader.querySelector('.toggle-group').addEventListener('click', () => {
groupContent.classList.toggle('active');
const icon = groupHeader.querySelector('.toggle-group i');
if (groupContent.classList.contains('active')) {
icon.className = 'fas fa-chevron-up';
} else {
icon.className = 'fas fa-chevron-down';
}
});
groupItem.appendChild(groupHeader);
groupItem.appendChild(groupContent);
this.sessionGroupsList.appendChild(groupItem);
});
}
// 显示保存会话对话框
showSaveSessionDialog(session) {
const modal = document.getElementById('save-session-modal');
modal.style.display = 'block';
// 加载活跃会话列表到下拉框
const activeSessionSelect = document.getElementById('active-session-select');
if (activeSessionSelect) {
activeSessionSelect.innerHTML = '<option value="">-- 请选择会话 --</option>';
const activeSessions = window.sessionManager.getActiveSessions();
for (const sessionId in activeSessions) {
const s = activeSessions[sessionId];
const option = document.createElement('option');
option.value = sessionId;
option.textContent = s.name;
activeSessionSelect.appendChild(option);
// 如果有指定的会话,则选中它
if (session && sessionId === session.id) {
option.selected = true;
}
}
}
// 加载会话组列表到下拉框
const sessionGroupSelect = document.getElementById('session-group');
if (sessionGroupSelect) {
sessionGroupSelect.innerHTML = '<option value="">-- 无分组 --</option>';
const sessionGroups = window.sessionManager.getSessionGroups();
for (const groupName in sessionGroups) {
const option = document.createElement('option');
option.value = groupName;
option.textContent = groupName;
sessionGroupSelect.appendChild(option);
}
}
// 设置表单默认值
if (session) {
const sessionNameInput = document.getElementById('session-name');
if (sessionNameInput) {
sessionNameInput.value = session.name;
}
}
}
// 显示管理会话组对话框
showManageGroupsDialog() {
const modal = document.getElementById('manage-groups-modal');
modal.style.display = 'block';
this.updateGroupsManagerUI();
}
// 更新管理会话组对话框内容
updateGroupsManagerUI() {
const groupsList = document.getElementById('manage-groups-list');
if (!groupsList) return;
groupsList.innerHTML = '';
const sessionGroups = window.sessionManager.getSessionGroups();
const groupNames = Object.keys(sessionGroups);
if (groupNames.length === 0) {
groupsList.innerHTML = '<div class="group-empty">暂无会话组</div>';
return;
}
groupNames.forEach(groupName => {
const group = sessionGroups[groupName];
const groupItem = document.createElement('div');
groupItem.className = 'group-item';
groupItem.innerHTML = `
<div class="group-item-name">${groupName}</div>
<div class="group-item-actions">
<button class="session-item-action edit-group" title="编辑组">
<i class="fas fa-edit"></i>
</button>
<button class="session-item-action delete-group" title="删除组">
<i class="fas fa-trash"></i>
</button>
</div>
`;
// 编辑组按钮
groupItem.querySelector('.edit-group').addEventListener('click', () => {
this.showEditGroupDialog(groupName, group);
});
// 删除组按钮
groupItem.querySelector('.delete-group').addEventListener('click', () => {
if (confirm(`确定要删除会话组"${groupName}"吗?`)) {
window.sessionManager.deleteSessionGroup(groupName);
this.updateGroupsManagerUI();
}
});
groupsList.appendChild(groupItem);
});
}
// 显示编辑会话组对话框
showEditGroupDialog(groupName, group) {
// 实现编辑会话组功能
alert(`编辑会话组功能开发中: ${groupName}`);
}
// 创建会话组
createSessionGroup() {
const groupNameInput = document.getElementById('new-group-name');
if (!groupNameInput) return;
const groupName = groupNameInput.value.trim();
if (!groupName) {
alert('请输入组名称');
return;
}
if (window.sessionManager.getSessionGroups()[groupName]) {
alert('已存在同名会话组');
return;
}
window.sessionManager.createSessionGroup(groupName);
groupNameInput.value = '';
this.updateGroupsManagerUI();
}
// 保存会话
saveSession() {
const sessionId = document.getElementById('active-session-select')?.value;
const sessionName = document.getElementById('session-name')?.value.trim();
const sessionDesc = document.getElementById('session-description')?.value.trim();
const groupName = document.getElementById('session-group')?.value;
if (!sessionId || !sessionName) {
alert('请选择会话并输入会话名称');
return;
}
const activeSessions = window.sessionManager.getActiveSessions();
const sessionInfo = activeSessions[sessionId];
if (!sessionInfo) {
alert('所选会话不存在');
return;
}
// 保存会话配置
const config = {
name: sessionName,
description: sessionDesc,
deviceId: sessionInfo.deviceId,
credentialId: sessionInfo.credentialId,
originalId: sessionId
};
window.sessionManager.saveSession(sessionName, config);
// 如果指定了会话组,添加到组
if (groupName) {
const groups = window.sessionManager.getSessionGroups();
if (groups[groupName]) {
const sessions = [...groups[groupName].sessions];
if (!sessions.includes(sessionName)) {
sessions.push(sessionName);
window.sessionManager.updateSessionGroup(groupName, sessions);
}
}
}
// 关闭对话框
document.getElementById('save-session-modal').style.display = 'none';
// 重置表单
document.getElementById('save-session-form')?.reset();
}
}
// 原本在这里自动创建实例的代码已移除,改为在页面加载时统一创建