class SystemInfo extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.charts = [];
this.chartOptions = [
'内存使用率',
'磁盘使用率',
'网络带宽'
];
// 动态添加CPU核心选项
this.cpuCoreCount = 8; // 默认值,后续会从系统获取
for (let i = 0; i < this.cpuCoreCount; i++) {
this.chartOptions.push(`CPU${i}使用率`);
}
// 每个图表的默认选择
this.chartSelections = [
'CPU0使用率',
'CPU1使用率',
'内存使用率',
'网络带宽'
];
// 存储历史数据
this.historyData = {};
this.chartOptions.forEach(option => {
this.historyData[option] = Array(60).fill(0);
});
// 为上传带宽单独存储数据
this.historyData['上传带宽'] = Array(60).fill(0);
this.historyData['下载带宽'] = Array(60).fill(0);
// 初始化状态
this.chartsInitialized = false;
this.setupInProgress = false;
this.chartJsLoaded = false;
this.domInitialized = false;
this.isActive = false; // 添加活动状态标记
}
connectedCallback() {
this.isActive = true; // 组件连接到DOM时设置为活动状态
this.render();
// 创建一个MutationObserver来监听shadowRoot内容变化
this.observer = new MutationObserver(this.onDomChange.bind(this));
this.observer.observe(this.shadowRoot, { childList: true, subtree: true });
// 立即尝试初始化事件监听器
setTimeout(() => this.setupEventListeners(), 0);
// 获取系统信息
this.fetchSystemInfo();
// 加载Chart.js
this.loadChartJs();
// 设置定时刷新
this.refreshInterval = setInterval(() => {
if (!this.isActive) return; // 非活动状态时不执行更新
this.fetchSystemInfo();
// 图表初始化后才更新
if (this.chartsInitialized) {
this.updateCharts();
}
}, 2000);
}
disconnectedCallback() {
this.isActive = false; // 组件断开连接时设置为非活动状态
if (this.refreshInterval) {
clearInterval(this.refreshInterval);
this.refreshInterval = null;
}
if (this.observer) {
this.observer.disconnect();
}
this.cleanupCharts();
}
// 清理所有图表
cleanupCharts() {
// 销毁图表
if (this.charts) {
this.charts.forEach((chart, index) => {
if (chart) {
try {
chart.destroy();
} catch (e) {
console.error(`销毁图表${index}失败:`, e);
}
this.charts[index] = null;
}
});
}
this.chartsInitialized = false;
}
// 重新激活组件的方法(当标签页重新被选中时调用)
reactivate() {
if (this.isActive) return; // 如果已经是活动状态,不重复处理
this.isActive = true;
// 重置图表状态
this.cleanupCharts();
this.domInitialized = false;
// 重新设置刷新定时器
if (!this.refreshInterval) {
this.refreshInterval = setInterval(() => {
if (!this.isActive) return;
this.fetchSystemInfo();
if (this.chartsInitialized) {
this.updateCharts();
}
}, 2000);
}
// 重新初始化图表
setTimeout(() => {
if (this.chartJsLoaded) {
// 确保DOM已初始化
const canvasElements = this.shadowRoot.querySelectorAll('canvas');
if (canvasElements.length === 4) {
this.domInitialized = true;
this.initializeCharts();
} else {
// 如果DOM还没准备好,等待DOM变化
this.observer = new MutationObserver(this.onDomChange.bind(this));
this.observer.observe(this.shadowRoot, { childList: true, subtree: true });
}
} else {
// 如果Chart.js还没加载,重新加载
this.loadChartJs();
}
}, 300);
}
// 监听DOM变化
onDomChange(mutations) {
if (this.domInitialized) return;
// 检查canvas元素是否已添加到DOM
const canvasElements = this.shadowRoot.querySelectorAll('canvas');
if (canvasElements.length === 4) {
this.domInitialized = true;
// 如果Chart.js已加载,初始化图表
if (this.chartJsLoaded) {
this.initializeCharts();
}
}
}
// 加载Chart.js
loadChartJs() {
if (typeof Chart !== 'undefined') {
this.chartJsLoaded = true;
// 如果DOM已准备就绪,初始化图表
if (this.domInitialized) {
this.initializeCharts();
}
return;
}
const script = document.createElement('script');
script.src = './chart.min.js';
script.onload = () => {
this.chartJsLoaded = true;
// 如果DOM已准备就绪,初始化图表
if (this.domInitialized) {
this.initializeCharts();
}
};
script.onerror = () => {
const cdnScript = document.createElement('script');
cdnScript.src = 'https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js';
cdnScript.onload = () => {
this.chartJsLoaded = true;
// 如果DOM已准备就绪,初始化图表
if (this.domInitialized) {
this.initializeCharts();
}
};
document.head.appendChild(cdnScript);
};
document.head.appendChild(script);
}
render() {
// 提前生成图表选项HTML字符串
const options0 = this.generateChartOptions(0);
const options1 = this.generateChartOptions(1);
const options2 = this.generateChartOptions(2);
const options3 = this.generateChartOptions(3);
this.shadowRoot.innerHTML = `
操作系统
Loading...
Loading...
`;
}
setupEventListeners() {
// 设置图表选择监听器
const selects = this.shadowRoot.querySelectorAll('.chart-select');
if (selects.length !== 4) {
setTimeout(() => this.setupEventListeners(), 100);
return;
}
selects.forEach(select => {
select.addEventListener('change', (e) => {
const chartIndex = parseInt(e.target.getAttribute('data-chart-index'));
const newMetric = e.target.value;
const oldMetric = this.chartSelections[chartIndex];
// 更新选择
this.chartSelections[chartIndex] = newMetric;
// 如果图表已初始化,重新创建图表而不是更新
if (this.charts[chartIndex]) {
this.recreateChart(chartIndex);
}
});
});
}
// 初始化所有图表
initializeCharts() {
if (this.setupInProgress || this.chartsInitialized) {
return;
}
this.setupInProgress = true;
try {
for (let i = 0; i < 4; i++) {
this.createChart(i);
}
this.chartsInitialized = true;
} catch (error) {
console.error('图表初始化失败:', error);
} finally {
this.setupInProgress = false;
}
}
// 创建单个图表
createChart(index) {
const canvas = this.shadowRoot.getElementById(`chart${index}`);
if (!canvas) {
throw new Error(`找不到图表${index}的canvas元素`);
}
// 确保canves宽高被设置
canvas.style.width = '100%';
canvas.style.height = '100%';
const ctx = canvas.getContext('2d');
if (!ctx) {
throw new Error(`无法获取图表${index}的2D上下文`);
}
// 获取指标和数据
const metric = this.chartSelections[index];
const data = [...(this.historyData[metric] || Array(60).fill(0))];
// 针对不同指标设置不同的y轴配置和数据集
const yAxisConfig = this.getYAxisConfig(metric);
let datasets = [];
if (metric === '网络带宽') {
// 网络带宽显示两条线:上传和下载
datasets = [
{
label: '下载带宽',
data: [...this.historyData['下载带宽']],
borderColor: '#4CAF50', // 绿色
borderWidth: 2,
fill: false,
tension: 0.4,
pointRadius: 0, // 去掉点
pointHoverRadius: 0 // 去掉悬停点
},
{
label: '上传带宽',
data: [...this.historyData['上传带宽']],
borderColor: '#F44336', // 红色
borderWidth: 2,
fill: false,
tension: 0.4,
pointRadius: 0, // 去掉点
pointHoverRadius: 0 // 去掉悬停点
}
];
} else {
// 其他指标只显示一条线
datasets = [
{
label: metric,
data: data,
borderColor: this.getChartColor(metric),
borderWidth: 2,
fill: false,
tension: 0.4,
pointRadius: 0, // 去掉点
pointHoverRadius: 0 // 去掉悬停点
}
];
}
// 创建图表
this.charts[index] = new Chart(ctx, {
type: 'line',
data: {
labels: Array(60).fill(''),
datasets: datasets
},
options: {
responsive: true,
maintainAspectRatio: false,
animation: {
duration: 0 // 关闭动画提高性能
},
elements: {
point: {
radius: 0 // 全局去掉点
},
line: {
tension: 0.4 // 平滑曲线
}
},
scales: {
y: yAxisConfig,
x: {
display: false
}
},
plugins: {
legend: {
display: metric === '网络带宽', // 只在网络带宽图表显示图例
position: 'top',
labels: {
boxWidth: 12,
font: {
size: 10
}
}
},
tooltip: {
mode: 'index',
intersect: false,
callbacks: {
label: function(context) {
const value = context.raw;
// 为网络带宽添加单位
if (metric === '网络带宽' ||
context.dataset.label === '上传带宽' ||
context.dataset.label === '下载带宽') {
return `${value} Mbps`;
}
// 为其他指标添加百分比单位
return `${value}%`;
}
}
}
}
}
});
}
// 根据指标类型获取y轴配置
getYAxisConfig(metric) {
// 网络带宽使用Mbps单位,其他使用百分比
if (metric === '网络带宽') {
return {
beginAtZero: true,
// 自动计算最大值,但最少为1
suggestedMax: 1,
title: {
display: true,
text: 'Mbps'
}
};
} else {
return {
beginAtZero: true,
max: 100,
title: {
display: true,
text: '%'
}
};
}
}
// 根据指标类型获取图表颜色
getChartColor(metric) {
if (metric === '下载带宽') {
return '#4CAF50'; // 绿色
} else if (metric === '上传带宽') {
return '#F44336'; // 红色
} else if (metric === '内存使用率') {
return '#2196F3'; // 蓝色
} else if (metric === '磁盘使用率') {
return '#FF9800'; // 橙色
} else if (metric.includes('CPU')) {
// CPU使用率显示紫色色调
return '#9C27B0';
}
// 默认颜色
const colors = [
'#4285F4', // 蓝色
'#EA4335', // 红色
'#34A853', // 绿色
'#FBBC05' // 黄色
];
// 为不同的指标分配不同的默认颜色
const hash = metric.split('').reduce((hash, char) => {
return char.charCodeAt(0) + ((hash << 5) - hash);
}, 0);
return colors[Math.abs(hash) % colors.length];
}
// 更新单个图表
updateChart(index) {
try {
if (!this.charts[index]) {
return;
}
const metric = this.chartSelections[index];
if (!this.historyData[metric] && metric !== '网络带宽') {
return;
}
if (metric === '网络带宽') {
// 获取上传和下载数据
const downloadData = [...this.historyData['下载带宽']];
const uploadData = [...this.historyData['上传带宽']];
// 找出最大值来设置Y轴
const maxDownload = Math.max(...downloadData);
const maxUpload = Math.max(...uploadData);
const maxValue = Math.max(maxDownload, maxUpload);
// 网络带宽图表根据数据自动调整y轴刻度
if (this.charts[index].options.scales.y) {
// 根据当前最大值动态设置y轴,但至少为1
const suggestedMax = Math.max(maxValue * 1.2, 1);
this.charts[index].options.scales.y.suggestedMax = suggestedMax;
}
// 更新两个数据集
this.charts[index].data.datasets[0].data = downloadData;
this.charts[index].data.datasets[1].data = uploadData;
} else {
// 其他指标只有一个数据集
const currentData = [...this.historyData[metric]];
this.charts[index].data.datasets[0].data = currentData;
}
this.charts[index].update();
} catch (error) {
console.error(`更新图表${index}失败:`, error);
}
}
// 更新所有图表
updateCharts() {
if (!this.chartsInitialized) {
return;
}
for (let i = 0; i < 4; i++) {
this.updateChart(i);
}
}
// 重新创建单个图表
recreateChart(index) {
try {
// 销毁现有图表
if (this.charts[index]) {
this.charts[index].destroy();
this.charts[index] = null;
}
// 重新创建
this.createChart(index);
} catch (error) {
console.error(`重新创建图表${index}失败:`, error);
}
}
// 获取系统信息
async fetchSystemInfo() {
try {
const response = await fetch('/api/system-info');
if (!response.ok) {
throw new Error(`获取系统信息失败: ${response.status}`);
}
const data = await response.json();
// 更新系统概览
const osInfo = this.shadowRoot.getElementById('os-info');
const kernelInfo = this.shadowRoot.getElementById('kernel-info');
const cpuCores = this.shadowRoot.getElementById('cpu-cores');
const ipAddress = this.shadowRoot.getElementById('ip-address');
const isolatedCores = this.shadowRoot.getElementById('isolated-cores');
if (osInfo) osInfo.textContent = data.os;
if (kernelInfo) kernelInfo.textContent = data.kernel || '';
if (cpuCores) cpuCores.textContent = data.cpuCores;
if (ipAddress) ipAddress.textContent = data.ipAddress;
if (isolatedCores) isolatedCores.textContent = data.isolatedCores.join(',') || '无';
// 更新CPU核心数并重新生成选项
if (this.cpuCoreCount !== data.cpuCores) {
this.cpuCoreCount = data.cpuCores;
this.updateCpuOptions();
}
// 更新历史数据
for (const [key, value] of Object.entries(data.metrics)) {
if (this.historyData[key]) {
this.historyData[key].shift();
this.historyData[key].push(value);
}
}
// 如果图表已初始化,则更新图表
if (this.chartsInitialized) {
this.updateCharts();
}
} catch (error) {
console.error('获取系统信息失败:', error);
}
}
// 更新CPU选项
updateCpuOptions() {
// 清除旧的CPU选项
this.chartOptions = this.chartOptions.filter(opt => !opt.startsWith('CPU'));
// 添加新的CPU选项
for (let i = 0; i < this.cpuCoreCount; i++) {
const option = `CPU${i}使用率`;
this.chartOptions.push(option);
// 确保历史数据中有此项
if (!this.historyData[option]) {
this.historyData[option] = Array(60).fill(0);
}
}
// 更新图表选择下拉框
const selects = this.shadowRoot.querySelectorAll('.chart-select');
if (selects.length === 4) {
selects.forEach((select, index) => {
// 保存当前选择
const currentValue = this.chartSelections[index];
// 更新选项
select.innerHTML = this.generateChartOptions(index);
// 如果当前选择的值不在新选项中,则更新为第一个选项
if (!this.chartOptions.includes(currentValue)) {
this.chartSelections[index] = this.chartOptions[0];
if (this.charts[index]) {
this.recreateChart(index);
}
}
});
}
}
// 生成图表选项HTML
generateChartOptions(chartIndex) {
let html = '';
this.chartOptions.forEach(option => {
const selected = option === this.chartSelections[chartIndex] ? 'selected' : '';
html += ``;
});
return html;
}
}
customElements.define('system-info', SystemInfo);