Add files via upload
This commit is contained in:
@@ -0,0 +1,818 @@
|
||||
/**
|
||||
* 仪表盘功能模块
|
||||
* 提供主题切换、数据统计、系统状态监控等功能
|
||||
*/
|
||||
|
||||
class DashboardManager {
|
||||
constructor() {
|
||||
this.refreshInterval = null;
|
||||
this.timeInterval = null;
|
||||
this.activities = [];
|
||||
this.lastDataSnapshot = null;
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化仪表盘
|
||||
*/
|
||||
init() {
|
||||
console.log('初始化仪表盘管理器...');
|
||||
|
||||
// 初始化主题切换器
|
||||
this.initializeThemeSwitcher();
|
||||
|
||||
// 更新当前时间
|
||||
this.updateCurrentTime();
|
||||
this.timeInterval = setInterval(() => this.updateCurrentTime(), 1000);
|
||||
|
||||
// 加载仪表盘数据
|
||||
this.loadDashboardData();
|
||||
|
||||
// 绑定仪表盘事件
|
||||
this.bindEvents();
|
||||
|
||||
// 添加活动记录
|
||||
this.addActivity('仪表盘初始化成功', 'success');
|
||||
|
||||
// 设置自动刷新(30秒)
|
||||
this.refreshInterval = setInterval(() => this.loadDashboardData(), 30000);
|
||||
}
|
||||
|
||||
/**
|
||||
* 主题切换器初始化
|
||||
*/
|
||||
initializeThemeSwitcher() {
|
||||
const themeToggle = document.getElementById('theme-toggle');
|
||||
if (!themeToggle) {
|
||||
console.warn('主题切换器元素未找到');
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取保存的主题或使用默认主题
|
||||
const currentTheme = localStorage.getItem('theme') || 'light';
|
||||
console.log('当前主题:', currentTheme);
|
||||
|
||||
// 应用主题
|
||||
this.applyTheme(currentTheme);
|
||||
|
||||
// 设置切换器状态
|
||||
themeToggle.checked = currentTheme === 'dark';
|
||||
|
||||
// 监听主题切换事件
|
||||
themeToggle.addEventListener('change', (event) => {
|
||||
const newTheme = event.target.checked ? 'dark' : 'light';
|
||||
console.log('切换主题到:', newTheme);
|
||||
|
||||
this.applyTheme(newTheme);
|
||||
localStorage.setItem('theme', newTheme);
|
||||
|
||||
// 添加活动记录
|
||||
this.addActivity(`切换到${newTheme === 'dark' ? '暗色' : '明亮'}主题`, 'info');
|
||||
});
|
||||
|
||||
console.log('主题切换器初始化完成');
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用主题
|
||||
*/
|
||||
applyTheme(theme) {
|
||||
document.documentElement.setAttribute('data-theme', theme);
|
||||
|
||||
// 更新元标签以支持系统主题
|
||||
let themeColorMeta = document.querySelector('meta[name="theme-color"]');
|
||||
if (!themeColorMeta) {
|
||||
themeColorMeta = document.createElement('meta');
|
||||
themeColorMeta.name = 'theme-color';
|
||||
document.head.appendChild(themeColorMeta);
|
||||
}
|
||||
|
||||
// 根据主题设置元标签颜色
|
||||
if (theme === 'dark') {
|
||||
themeColorMeta.content = '#1e293b';
|
||||
} else {
|
||||
themeColorMeta.content = '#ffffff';
|
||||
}
|
||||
|
||||
console.log(`主题 ${theme} 已应用`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新当前时间
|
||||
*/
|
||||
updateCurrentTime() {
|
||||
const now = new Date();
|
||||
const timeString = now.toLocaleString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit'
|
||||
});
|
||||
|
||||
const currentTimeElement = document.getElementById('current-time');
|
||||
if (currentTimeElement) {
|
||||
currentTimeElement.textContent = timeString;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载仪表盘数据
|
||||
*/
|
||||
async loadDashboardData() {
|
||||
try {
|
||||
console.log('开始加载仪表盘数据...');
|
||||
|
||||
// 显示加载状态
|
||||
this.setLoadingState(true);
|
||||
|
||||
// 并行加载各种数据
|
||||
const [devices, topology, credentials, activeConnections, terminalSessions] = await Promise.all([
|
||||
this.fetchWithFallback('/api/devices', []),
|
||||
this.fetchWithFallback('/api/topology', { nodes: {}, links: [] }),
|
||||
this.fetchWithFallback('/api/credentials', []),
|
||||
this.getActiveSessions(),
|
||||
this.getTerminalSessions()
|
||||
]);
|
||||
|
||||
console.log('数据加载完成:', {
|
||||
devices: devices.length,
|
||||
topology: Object.keys(topology.nodes || {}).length,
|
||||
credentials: credentials.length,
|
||||
activeConnections: activeConnections.length,
|
||||
terminalSessions: terminalSessions.length
|
||||
});
|
||||
|
||||
// 检测数据变化
|
||||
const currentSnapshot = {
|
||||
devicesCount: devices.length,
|
||||
topologyCount: Object.keys(topology.nodes || {}).length,
|
||||
credentialsCount: credentials.length,
|
||||
connectionsCount: activeConnections.length,
|
||||
sessionsCount: terminalSessions.length
|
||||
};
|
||||
|
||||
// 如果有数据变化,记录活动
|
||||
if (this.lastDataSnapshot) {
|
||||
this.detectDataChanges(this.lastDataSnapshot, currentSnapshot);
|
||||
}
|
||||
|
||||
this.lastDataSnapshot = currentSnapshot;
|
||||
|
||||
// 更新统计数据
|
||||
this.updateDashboardStats(devices, topology, credentials, activeConnections, terminalSessions);
|
||||
|
||||
// 更新设备概览
|
||||
this.updateDevicesOverview(devices);
|
||||
|
||||
// 检查系统状态
|
||||
await this.checkSystemStatus();
|
||||
|
||||
} catch (error) {
|
||||
console.error('加载仪表盘数据失败:', error);
|
||||
this.addActivity('加载仪表盘数据失败', 'error');
|
||||
} finally {
|
||||
this.setLoadingState(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测数据变化
|
||||
*/
|
||||
detectDataChanges(oldSnapshot, newSnapshot) {
|
||||
if (newSnapshot.devicesCount !== oldSnapshot.devicesCount) {
|
||||
const change = newSnapshot.devicesCount - oldSnapshot.devicesCount;
|
||||
this.addActivity(`设备数量${change > 0 ? '增加' : '减少'}${Math.abs(change)}个`, change > 0 ? 'success' : 'warning');
|
||||
}
|
||||
|
||||
if (newSnapshot.topologyCount !== oldSnapshot.topologyCount) {
|
||||
const change = newSnapshot.topologyCount - oldSnapshot.topologyCount;
|
||||
this.addActivity(`拓扑节点${change > 0 ? '增加' : '减少'}${Math.abs(change)}个`, 'info');
|
||||
}
|
||||
|
||||
if (newSnapshot.credentialsCount !== oldSnapshot.credentialsCount) {
|
||||
const change = newSnapshot.credentialsCount - oldSnapshot.credentialsCount;
|
||||
this.addActivity(`凭据${change > 0 ? '增加' : '减少'}${Math.abs(change)}个`, 'info');
|
||||
}
|
||||
|
||||
if (newSnapshot.connectionsCount !== oldSnapshot.connectionsCount) {
|
||||
const change = newSnapshot.connectionsCount - oldSnapshot.connectionsCount;
|
||||
this.addActivity(`活跃连接${change > 0 ? '增加' : '减少'}${Math.abs(change)}个`, change > 0 ? 'success' : 'info');
|
||||
}
|
||||
|
||||
if (newSnapshot.sessionsCount !== oldSnapshot.sessionsCount) {
|
||||
const change = newSnapshot.sessionsCount - oldSnapshot.sessionsCount;
|
||||
this.addActivity(`终端会话${change > 0 ? '增加' : '减少'}${Math.abs(change)}个`, change > 0 ? 'success' : 'info');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取活跃会话数
|
||||
*/
|
||||
async getActiveSessions() {
|
||||
try {
|
||||
// 首先尝试从后端API获取活跃连接
|
||||
const response = await fetch('/api/connections/active');
|
||||
if (response.ok) {
|
||||
const connections = await response.json();
|
||||
return connections || [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('无法从API获取活跃连接:', error);
|
||||
}
|
||||
|
||||
// 从全局会话管理器获取活跃会话
|
||||
if (window.sessionManager && window.sessionManager.activeSessions) {
|
||||
return Object.keys(window.sessionManager.activeSessions);
|
||||
}
|
||||
|
||||
// 从终端管理器获取
|
||||
if (window.terminalManager && window.terminalManager.terminals) {
|
||||
return Object.keys(window.terminalManager.terminals);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取真正的终端会话数
|
||||
*/
|
||||
async getTerminalSessions() {
|
||||
try {
|
||||
// 尝试从终端管理器获取
|
||||
if (window.terminalManager && window.terminalManager.terminals) {
|
||||
return Object.keys(window.terminalManager.terminals);
|
||||
}
|
||||
|
||||
// 尝试从会话管理器获取
|
||||
if (window.sessionManager && window.sessionManager.activeSessions) {
|
||||
return Object.keys(window.sessionManager.activeSessions);
|
||||
}
|
||||
|
||||
// 尝试从DOM获取终端标签页数量
|
||||
const terminalTabs = document.querySelectorAll('.terminal-tab');
|
||||
if (terminalTabs.length > 0) {
|
||||
return Array.from(terminalTabs).map((tab, index) => `terminal-${index}`);
|
||||
}
|
||||
|
||||
return [];
|
||||
} catch (error) {
|
||||
console.warn('获取终端会话失败:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 带回退的网络请求
|
||||
*/
|
||||
async fetchWithFallback(url, fallback) {
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
console.log(`API ${url} 请求成功:`, data);
|
||||
return data;
|
||||
} else {
|
||||
console.warn(`API ${url} 请求失败 (${response.status}):`, response.statusText);
|
||||
return fallback;
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`API ${url} 请求异常:`, error);
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置加载状态
|
||||
*/
|
||||
setLoadingState(loading) {
|
||||
const refreshBtn = document.getElementById('refresh-dashboard');
|
||||
if (refreshBtn) {
|
||||
if (loading) {
|
||||
refreshBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> 加载中';
|
||||
refreshBtn.disabled = true;
|
||||
} else {
|
||||
refreshBtn.innerHTML = '<i class="fas fa-sync"></i> 刷新';
|
||||
refreshBtn.disabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新仪表盘统计数据
|
||||
*/
|
||||
updateDashboardStats(devices, topology, credentials, activeConnections, terminalSessions) {
|
||||
// 设备数量
|
||||
const devicesCount = devices.length;
|
||||
this.updateStatCard('dashboard-devices-count', devicesCount);
|
||||
this.updateStatChange('dashboard-devices-change', `+${devicesCount}`);
|
||||
|
||||
// 活跃连接数(会话数)
|
||||
const connectionsCount = activeConnections.length;
|
||||
this.updateStatCard('dashboard-connections-count', connectionsCount);
|
||||
this.updateStatChange('dashboard-connections-change', connectionsCount > 0 ? `+${connectionsCount}` : '0');
|
||||
|
||||
// 拓扑节点数
|
||||
const topologyNodes = topology.nodes ? Object.keys(topology.nodes).length : 0;
|
||||
this.updateStatCard('dashboard-topology-nodes', topologyNodes);
|
||||
this.updateStatChange('dashboard-topology-change', `+${topologyNodes}`);
|
||||
|
||||
// 终端会话数(修正:之前错误地显示凭据数量)
|
||||
const sessionsCount = terminalSessions.length;
|
||||
this.updateStatCard('dashboard-sessions-count', sessionsCount);
|
||||
this.updateStatChange('dashboard-sessions-change', `+${sessionsCount}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新统计卡片的数值
|
||||
*/
|
||||
updateStatCard(elementId, value) {
|
||||
const element = document.getElementById(elementId);
|
||||
if (element) {
|
||||
const currentValue = parseInt(element.textContent) || 0;
|
||||
const targetValue = parseInt(value) || 0;
|
||||
|
||||
if (currentValue !== targetValue) {
|
||||
this.animateNumber(element, currentValue, targetValue, 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新统计变化指示器
|
||||
*/
|
||||
updateStatChange(elementId, value) {
|
||||
const element = document.getElementById(elementId);
|
||||
if (element) {
|
||||
element.textContent = value;
|
||||
|
||||
// 根据值设置样式
|
||||
element.className = 'stat-change';
|
||||
if (value.startsWith('+') && value !== '+0') {
|
||||
element.classList.add('positive');
|
||||
} else if (value.startsWith('-')) {
|
||||
element.classList.add('negative');
|
||||
} else {
|
||||
element.classList.add('neutral');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 数字动画效果
|
||||
*/
|
||||
animateNumber(element, start, end, duration) {
|
||||
const startTime = performance.now();
|
||||
|
||||
const updateNumber = (currentTime) => {
|
||||
const elapsed = currentTime - startTime;
|
||||
const progress = Math.min(elapsed / duration, 1);
|
||||
|
||||
// 使用缓动函数
|
||||
const easedProgress = 1 - Math.pow(1 - progress, 3);
|
||||
const current = Math.round(start + (end - start) * easedProgress);
|
||||
element.textContent = current;
|
||||
|
||||
if (progress < 1) {
|
||||
requestAnimationFrame(updateNumber);
|
||||
}
|
||||
};
|
||||
|
||||
requestAnimationFrame(updateNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新设备概览
|
||||
*/
|
||||
updateDevicesOverview(devices) {
|
||||
const devicesOverview = document.getElementById('devices-overview');
|
||||
if (!devicesOverview) return;
|
||||
|
||||
if (!devices || devices.length === 0) {
|
||||
devicesOverview.innerHTML = `
|
||||
<div class="no-devices">
|
||||
<i class="fas fa-server"></i>
|
||||
<p>暂无设备</p>
|
||||
<button class="btn btn-sm" onclick="dashboardManager.goToDevices()">
|
||||
<i class="fas fa-plus"></i> 添加设备
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
|
||||
// 显示前5个设备
|
||||
const displayDevices = devices.slice(0, 5);
|
||||
const deviceItems = displayDevices.map(device => `
|
||||
<div class="device-overview-item" onclick="dashboardManager.goToDevices('${device.id}')" title="点击查看设备详情">
|
||||
<div class="device-overview-icon">
|
||||
<i class="fas fa-${this.getDeviceIcon(device.device_type)}"></i>
|
||||
</div>
|
||||
<div class="device-overview-info">
|
||||
<div class="device-overview-name">${device.name}</div>
|
||||
<div class="device-overview-details">${device.ip_address} • ${this.getVendorDisplayName(device.vendor)}</div>
|
||||
</div>
|
||||
<div class="device-overview-status ${device.status || 'unknown'}" title="状态: ${this.getStatusDisplayName(device.status)}"></div>
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
devicesOverview.innerHTML = deviceItems;
|
||||
|
||||
// 如果设备数量超过5个,显示"查看更多"
|
||||
if (devices.length > 5) {
|
||||
devicesOverview.innerHTML += `
|
||||
<div class="device-overview-item" onclick="dashboardManager.goToDevices()" style="cursor: pointer;" title="查看所有设备">
|
||||
<div class="device-overview-icon">
|
||||
<i class="fas fa-ellipsis-h"></i>
|
||||
</div>
|
||||
<div class="device-overview-info">
|
||||
<div class="device-overview-name">查看更多</div>
|
||||
<div class="device-overview-details">还有 ${devices.length - 5} 个设备</div>
|
||||
</div>
|
||||
<div class="device-overview-status more-indicator"></div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备图标
|
||||
*/
|
||||
getDeviceIcon(deviceType) {
|
||||
const icons = {
|
||||
router: 'route',
|
||||
switch: 'network-wired',
|
||||
firewall: 'shield-alt',
|
||||
wireless_controller: 'wifi',
|
||||
access_point: 'broadcast-tower',
|
||||
load_balancer: 'balance-scale',
|
||||
server: 'server'
|
||||
};
|
||||
return icons[deviceType] || 'server';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取厂商显示名称
|
||||
*/
|
||||
getVendorDisplayName(vendor) {
|
||||
const vendorNames = {
|
||||
cisco: 'Cisco',
|
||||
huawei: '华为',
|
||||
h3c: 'H3C',
|
||||
juniper: 'Juniper',
|
||||
arista: 'Arista',
|
||||
fortinet: 'Fortinet',
|
||||
checkpoint: 'CheckPoint',
|
||||
other: '其他'
|
||||
};
|
||||
return vendorNames[vendor] || vendor;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取状态显示名称
|
||||
*/
|
||||
getStatusDisplayName(status) {
|
||||
const statusNames = {
|
||||
online: '在线',
|
||||
offline: '离线',
|
||||
unreachable: '不可达',
|
||||
maintenance: '维护中',
|
||||
unknown: '未知'
|
||||
};
|
||||
return statusNames[status] || '未知';
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查系统状态
|
||||
*/
|
||||
async checkSystemStatus() {
|
||||
// 检查网络连接
|
||||
await this.checkNetworkStatus();
|
||||
|
||||
// 检查数据库状态
|
||||
await this.checkDatabaseStatus();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查网络状态
|
||||
*/
|
||||
async checkNetworkStatus() {
|
||||
const statusIcon = document.getElementById('network-status');
|
||||
const statusText = document.getElementById('network-status-text');
|
||||
|
||||
if (!statusIcon || !statusText) return;
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/devices', {
|
||||
method: 'HEAD',
|
||||
signal: AbortSignal.timeout(5000) // 5秒超时
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
statusIcon.className = 'status-icon online';
|
||||
statusIcon.innerHTML = '<i class="fas fa-wifi"></i>';
|
||||
statusText.textContent = '连接正常';
|
||||
} else {
|
||||
statusIcon.className = 'status-icon warning';
|
||||
statusIcon.innerHTML = '<i class="fas fa-exclamation-triangle"></i>';
|
||||
statusText.textContent = '连接异常';
|
||||
}
|
||||
} catch (error) {
|
||||
statusIcon.className = 'status-icon offline';
|
||||
statusIcon.innerHTML = '<i class="fas fa-times-circle"></i>';
|
||||
statusText.textContent = '连接失败';
|
||||
console.error('网络状态检查失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查数据库状态
|
||||
*/
|
||||
async checkDatabaseStatus() {
|
||||
const statusIcon = document.getElementById('database-status');
|
||||
const statusText = document.getElementById('database-status-text');
|
||||
|
||||
if (!statusIcon || !statusText) return;
|
||||
|
||||
try {
|
||||
// 通过尝试加载设备列表来检查数据库状态
|
||||
const response = await fetch('/api/devices', {
|
||||
signal: AbortSignal.timeout(3000) // 3秒超时
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
statusIcon.className = 'status-icon online';
|
||||
statusIcon.innerHTML = '<i class="fas fa-check-circle"></i>';
|
||||
statusText.textContent = '正常';
|
||||
} else {
|
||||
statusIcon.className = 'status-icon warning';
|
||||
statusIcon.innerHTML = '<i class="fas fa-exclamation-triangle"></i>';
|
||||
statusText.textContent = '异常';
|
||||
}
|
||||
} catch (error) {
|
||||
statusIcon.className = 'status-icon offline';
|
||||
statusIcon.innerHTML = '<i class="fas fa-times-circle"></i>';
|
||||
statusText.textContent = '错误';
|
||||
console.error('数据库状态检查失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定事件
|
||||
*/
|
||||
bindEvents() {
|
||||
// 刷新仪表盘按钮
|
||||
const refreshBtn = document.getElementById('refresh-dashboard');
|
||||
if (refreshBtn) {
|
||||
refreshBtn.addEventListener('click', () => {
|
||||
this.loadDashboardData();
|
||||
this.addActivity('手动刷新仪表盘数据', 'info');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加活动记录
|
||||
*/
|
||||
addActivity(text, type = 'info') {
|
||||
const activitiesList = document.getElementById('recent-activities');
|
||||
if (!activitiesList) return;
|
||||
|
||||
const now = new Date();
|
||||
|
||||
// 添加到内部数组
|
||||
this.activities.unshift({ text, type, time: now });
|
||||
|
||||
// 限制活动数量
|
||||
if (this.activities.length > 50) {
|
||||
this.activities = this.activities.slice(0, 50);
|
||||
}
|
||||
|
||||
const activityItem = document.createElement('div');
|
||||
activityItem.className = 'activity-item';
|
||||
activityItem.innerHTML = `
|
||||
<div class="activity-icon ${type}">
|
||||
<i class="fas fa-${this.getActivityIcon(type)}"></i>
|
||||
</div>
|
||||
<div class="activity-content">
|
||||
<div class="activity-text">${text}</div>
|
||||
<div class="activity-time">${this.getRelativeTime(now)}</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// 插入到列表顶部
|
||||
activitiesList.insertBefore(activityItem, activitiesList.firstChild);
|
||||
|
||||
// 限制显示的活动数量(最多保留10条显示)
|
||||
const displayedActivities = activitiesList.querySelectorAll('.activity-item');
|
||||
if (displayedActivities.length > 10) {
|
||||
activitiesList.removeChild(displayedActivities[displayedActivities.length - 1]);
|
||||
}
|
||||
|
||||
// 定期更新活动时间
|
||||
setTimeout(() => this.updateActivityTimes(), 60000);
|
||||
|
||||
console.log(`活动记录: ${text} (${type})`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取活动图标
|
||||
*/
|
||||
getActivityIcon(type) {
|
||||
const icons = {
|
||||
info: 'info-circle',
|
||||
success: 'check-circle',
|
||||
warning: 'exclamation-triangle',
|
||||
error: 'times-circle'
|
||||
};
|
||||
return icons[type] || 'info-circle';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取相对时间
|
||||
*/
|
||||
getRelativeTime(date) {
|
||||
const now = new Date();
|
||||
const diff = now - date;
|
||||
const seconds = Math.floor(diff / 1000);
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
const hours = Math.floor(minutes / 60);
|
||||
const days = Math.floor(hours / 24);
|
||||
|
||||
if (seconds < 60) return '刚刚';
|
||||
if (minutes < 60) return `${minutes}分钟前`;
|
||||
if (hours < 24) return `${hours}小时前`;
|
||||
if (days < 7) return `${days}天前`;
|
||||
return date.toLocaleDateString('zh-CN');
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新活动时间显示
|
||||
*/
|
||||
updateActivityTimes() {
|
||||
const timeElements = document.querySelectorAll('.activity-time');
|
||||
timeElements.forEach((element, index) => {
|
||||
if (this.activities[index]) {
|
||||
element.textContent = this.getRelativeTime(this.activities[index].time);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 导航到设备页面
|
||||
*/
|
||||
goToDevices(deviceId = null) {
|
||||
const devicesLink = document.querySelector('.nav-link[data-page="devices"]');
|
||||
if (devicesLink) {
|
||||
devicesLink.click();
|
||||
|
||||
// 如果指定了设备ID,在设备页面高亮该设备
|
||||
if (deviceId) {
|
||||
setTimeout(() => {
|
||||
const deviceElement = document.getElementById(`device-${deviceId}`);
|
||||
if (deviceElement) {
|
||||
deviceElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
deviceElement.style.backgroundColor = 'var(--primary-color)';
|
||||
deviceElement.style.color = 'white';
|
||||
setTimeout(() => {
|
||||
deviceElement.style.backgroundColor = '';
|
||||
deviceElement.style.color = '';
|
||||
}, 2000);
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
|
||||
this.addActivity('跳转到设备管理页面', 'info');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新系统状态
|
||||
*/
|
||||
refreshSystemStatus() {
|
||||
this.checkSystemStatus();
|
||||
this.addActivity('刷新系统状态', 'info');
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新设备概览
|
||||
*/
|
||||
refreshDevicesOverview() {
|
||||
this.loadDashboardData();
|
||||
this.addActivity('刷新设备概览', 'info');
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空活动记录
|
||||
*/
|
||||
clearActivities() {
|
||||
this.activities = [];
|
||||
const activitiesList = document.getElementById('recent-activities');
|
||||
if (activitiesList) {
|
||||
activitiesList.innerHTML = `
|
||||
<div class="activity-item">
|
||||
<div class="activity-icon info">
|
||||
<i class="fas fa-info-circle"></i>
|
||||
</div>
|
||||
<div class="activity-content">
|
||||
<div class="activity-text">活动记录已清空</div>
|
||||
<div class="activity-time">刚刚</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
console.log('活动记录已清空');
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁函数
|
||||
*/
|
||||
destroy() {
|
||||
if (this.refreshInterval) {
|
||||
clearInterval(this.refreshInterval);
|
||||
this.refreshInterval = null;
|
||||
}
|
||||
if (this.timeInterval) {
|
||||
clearInterval(this.timeInterval);
|
||||
this.timeInterval = null;
|
||||
}
|
||||
console.log('仪表盘管理器已销毁');
|
||||
}
|
||||
}
|
||||
|
||||
// 全局仪表盘管理器实例
|
||||
let dashboardManager = null;
|
||||
|
||||
// 初始化仪表盘的全局函数
|
||||
function initializeDashboard() {
|
||||
if (!dashboardManager) {
|
||||
dashboardManager = new DashboardManager();
|
||||
console.log('仪表盘管理器初始化完成');
|
||||
}
|
||||
return dashboardManager;
|
||||
}
|
||||
|
||||
// 全局快捷函数
|
||||
function goToDevices(deviceId = null) {
|
||||
if (dashboardManager) {
|
||||
dashboardManager.goToDevices(deviceId);
|
||||
}
|
||||
}
|
||||
|
||||
function refreshSystemStatus() {
|
||||
if (dashboardManager) {
|
||||
dashboardManager.refreshSystemStatus();
|
||||
}
|
||||
}
|
||||
|
||||
function refreshDevicesOverview() {
|
||||
if (dashboardManager) {
|
||||
dashboardManager.refreshDevicesOverview();
|
||||
}
|
||||
}
|
||||
|
||||
function clearActivities() {
|
||||
if (dashboardManager) {
|
||||
dashboardManager.clearActivities();
|
||||
}
|
||||
}
|
||||
|
||||
// 暴露到全局作用域
|
||||
window.DashboardManager = DashboardManager;
|
||||
window.initializeDashboard = initializeDashboard;
|
||||
window.dashboardManager = dashboardManager;
|
||||
window.goToDevices = goToDevices;
|
||||
window.refreshSystemStatus = refreshSystemStatus;
|
||||
window.refreshDevicesOverview = refreshDevicesOverview;
|
||||
window.clearActivities = clearActivities;
|
||||
|
||||
// 页面卸载时清理
|
||||
window.addEventListener('beforeunload', () => {
|
||||
if (dashboardManager) {
|
||||
dashboardManager.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
// 页面可见性变化时的处理
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (dashboardManager) {
|
||||
if (document.hidden) {
|
||||
console.log('页面隐藏,暂停定时器');
|
||||
if (dashboardManager.refreshInterval) {
|
||||
clearInterval(dashboardManager.refreshInterval);
|
||||
dashboardManager.refreshInterval = null;
|
||||
}
|
||||
} else {
|
||||
console.log('页面可见,恢复定时器');
|
||||
if (!dashboardManager.refreshInterval) {
|
||||
dashboardManager.refreshInterval = setInterval(() => dashboardManager.loadDashboardData(), 30000);
|
||||
}
|
||||
// 立即刷新一次数据
|
||||
dashboardManager.loadDashboardData();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
console.log('仪表盘模块已加载');
|
||||
Reference in New Issue
Block a user