layui.use(['layer'], function(){ var layer = layui.layer; var $ = layui.$; // 初始化图表 var registerTrendChart = echarts.init(document.getElementById('register-trend')); var deviceTypesChart = echarts.init(document.getElementById('device-types')); // 加载统计数据 function loadStats() { fetch('/api/dashboard/stats', { credentials: 'include', headers: { 'Authorization': 'Bearer ' + localStorage.getItem('token') } }) .then(response => { if (response.status === 401) { window.location.href = '/login'; throw new Error('认证失败'); } return response.json(); }) .then(result => { if (result.error) { layer.msg(result.error); return; } // 更新统计数据 updateStats(result.data); // 更新图表 updateCharts(result.data); // 更新系统状态 updateSystemStatus(result.data); }) .catch(error => { layer.msg('加载统计数据失败:' + error.message); }); } // 更新统计数据 function updateStats(data) { $('#total-devices').text(data.total_devices); $('#total-licenses').text(data.total_licenses); $('#online-devices').text(data.online_devices); $('#expired-devices').text(data.expired_devices); $('#today-new').text(data.today_new); $('#unused-licenses').text(data.unused_licenses); // 计算比率 var activeRate = data.total_devices > 0 ? ((data.online_devices / data.total_devices) * 100).toFixed(1) : 0; var expiredRate = data.total_devices > 0 ? ((data.expired_devices / data.total_devices) * 100).toFixed(1) : 0; $('#active-rate').text(activeRate + '%'); $('#expired-rate').text(expiredRate + '%'); } // 更新图表 function updateCharts(data) { // 设备注册趋势图 var trendOption = { title: { text: '最近7天设备注册趋势' }, tooltip: { trigger: 'axis' }, xAxis: { type: 'category', data: data.trend_dates }, yAxis: { type: 'value' }, series: [{ name: '新增设备', type: 'line', smooth: true, data: data.trend_counts, itemStyle: { color: '#009688' }, areaStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ offset: 0, color: 'rgba(0, 150, 136, 0.3)' }, { offset: 1, color: 'rgba(0, 150, 136, 0.1)' }]) } }] }; registerTrendChart.setOption(trendOption); // 设备类型分布图 var typesOption = { title: { text: '设备类型分布' }, tooltip: { trigger: 'item', formatter: '{b}: {c} ({d}%)' }, series: [{ type: 'pie', radius: ['50%', '70%'], avoidLabelOverlap: false, itemStyle: { borderRadius: 10, borderColor: '#fff', borderWidth: 2 }, label: { show: false, position: 'center' }, emphasis: { label: { show: true, fontSize: '20', fontWeight: 'bold' } }, labelLine: { show: false }, data: data.device_types.map(item => ({ name: item.type, value: item.count })) }] }; deviceTypesChart.setOption(typesOption); } // 更新系统状态 function updateSystemStatus(data) { $('#cpu-usage').text(data.cpu_usage + '%'); $('#memory-usage').text(data.memory_usage + '%'); $('#disk-usage').text(data.disk_usage + '%'); $('#uptime').text(formatDuration(data.uptime)); $('#load-avg').text(data.load_avg.join(' ')); $('#network-traffic').text(formatBytes(data.network_traffic) + '/s'); $('#online-users').text(data.online_users); $('#last-update').text(new Date().toLocaleString()); } // 格式化时间 function formatDuration(seconds) { var days = Math.floor(seconds / 86400); var hours = Math.floor((seconds % 86400) / 3600); var minutes = Math.floor((seconds % 3600) / 60); var parts = []; if (days > 0) parts.push(days + '天'); if (hours > 0) parts.push(hours + '小时'); if (minutes > 0) parts.push(minutes + '分钟'); return parts.join(' ') || '0分钟'; } // 格式化字节大小 function formatBytes(bytes) { if (bytes === 0) return '0 B'; var k = 1024; var sizes = ['B', 'KB', 'MB', 'GB', 'TB']; var i = Math.floor(Math.log(bytes) / Math.log(k)); return (bytes / Math.pow(k, i)).toFixed(2) + ' ' + sizes[i]; } // 初始加载 loadStats(); // 定时刷新(每30秒) setInterval(loadStats, 30000); // 窗口大小改变时重绘图表 window.onresize = function() { registerTrendChart.resize(); deviceTypesChart.resize(); }; });