805 lines
28 KiB
JavaScript
805 lines
28 KiB
JavaScript
|
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 = `
|
|||
|
<style>
|
|||
|
:host {
|
|||
|
display: block;
|
|||
|
height: 100%;
|
|||
|
overflow: auto;
|
|||
|
padding: 16px;
|
|||
|
box-sizing: border-box;
|
|||
|
}
|
|||
|
|
|||
|
.system-info-container {
|
|||
|
background-color: white;
|
|||
|
border-radius: 8px;
|
|||
|
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
|||
|
padding: 16px;
|
|||
|
height: 100%;
|
|||
|
box-sizing: border-box;
|
|||
|
display: flex;
|
|||
|
flex-direction: column;
|
|||
|
}
|
|||
|
|
|||
|
.info-header {
|
|||
|
display: flex;
|
|||
|
justify-content: space-between;
|
|||
|
align-items: center;
|
|||
|
margin-bottom: 16px;
|
|||
|
padding-bottom: 8px;
|
|||
|
border-bottom: 1px solid #e0e0e0;
|
|||
|
}
|
|||
|
|
|||
|
.info-title {
|
|||
|
font-size: 18px;
|
|||
|
font-weight: bold;
|
|||
|
color: #333;
|
|||
|
}
|
|||
|
|
|||
|
.system-overview {
|
|||
|
display: grid;
|
|||
|
grid-template-columns: repeat(4, 1fr);
|
|||
|
gap: 16px;
|
|||
|
margin-bottom: 24px;
|
|||
|
background-color: #f5f5f5;
|
|||
|
border-radius: 6px;
|
|||
|
padding: 16px;
|
|||
|
}
|
|||
|
|
|||
|
.overview-item {
|
|||
|
display: flex;
|
|||
|
flex-direction: column;
|
|||
|
}
|
|||
|
|
|||
|
.overview-label {
|
|||
|
font-size: 14px;
|
|||
|
color: #666;
|
|||
|
margin-bottom: 8px;
|
|||
|
}
|
|||
|
|
|||
|
.overview-value {
|
|||
|
font-size: 16px;
|
|||
|
font-weight: bold;
|
|||
|
color: #333;
|
|||
|
}
|
|||
|
|
|||
|
.overview-subvalue {
|
|||
|
font-size: 12px;
|
|||
|
color: #666;
|
|||
|
margin-top: 4px;
|
|||
|
}
|
|||
|
|
|||
|
.charts-container {
|
|||
|
display: grid;
|
|||
|
grid-template-columns: repeat(2, 1fr);
|
|||
|
grid-template-rows: repeat(2, 1fr);
|
|||
|
gap: 16px;
|
|||
|
flex-grow: 1;
|
|||
|
min-height: 400px;
|
|||
|
}
|
|||
|
|
|||
|
.chart-card {
|
|||
|
background-color: #f5f5f5;
|
|||
|
border-radius: 6px;
|
|||
|
padding: 16px;
|
|||
|
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
|||
|
display: flex;
|
|||
|
flex-direction: column;
|
|||
|
min-height: 200px;
|
|||
|
}
|
|||
|
|
|||
|
.chart-header {
|
|||
|
display: flex;
|
|||
|
justify-content: space-between;
|
|||
|
align-items: center;
|
|||
|
margin-bottom: 8px;
|
|||
|
}
|
|||
|
|
|||
|
.chart-title {
|
|||
|
font-size: 14px;
|
|||
|
font-weight: bold;
|
|||
|
color: #333;
|
|||
|
}
|
|||
|
|
|||
|
.chart-select {
|
|||
|
padding: 4px;
|
|||
|
border-radius: 4px;
|
|||
|
border: 1px solid #ccc;
|
|||
|
font-size: 12px;
|
|||
|
}
|
|||
|
|
|||
|
.chart-container {
|
|||
|
flex-grow: 1;
|
|||
|
position: relative;
|
|||
|
min-height: 150px;
|
|||
|
}
|
|||
|
|
|||
|
canvas {
|
|||
|
width: 100% !important;
|
|||
|
height: 100% !important;
|
|||
|
position: absolute;
|
|||
|
}
|
|||
|
</style>
|
|||
|
<div class="system-info-container">
|
|||
|
<div class="info-header">
|
|||
|
<div class="info-title">系统信息</div>
|
|||
|
</div>
|
|||
|
|
|||
|
<div class="system-overview">
|
|||
|
<div class="overview-item">
|
|||
|
<div class="overview-label">操作系统</div>
|
|||
|
<div class="overview-value" id="os-info">Loading...</div>
|
|||
|
<div class="overview-subvalue" id="kernel-info">Loading...</div>
|
|||
|
</div>
|
|||
|
<div class="overview-item">
|
|||
|
<div class="overview-label">CPU核心数</div>
|
|||
|
<div class="overview-value" id="cpu-cores">Loading...</div>
|
|||
|
</div>
|
|||
|
<div class="overview-item">
|
|||
|
<div class="overview-label">IP地址</div>
|
|||
|
<div class="overview-value" id="ip-address">Loading...</div>
|
|||
|
</div>
|
|||
|
<div class="overview-item">
|
|||
|
<div class="overview-label">已隔离CPU核心号</div>
|
|||
|
<div class="overview-value" id="isolated-cores">Loading...</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
<div class="charts-container">
|
|||
|
<div class="chart-card">
|
|||
|
<div class="chart-header">
|
|||
|
<div class="chart-title">监控图表 1</div>
|
|||
|
<select class="chart-select" data-chart-index="0">
|
|||
|
${options0}
|
|||
|
</select>
|
|||
|
</div>
|
|||
|
<div class="chart-container">
|
|||
|
<canvas id="chart0"></canvas>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
<div class="chart-card">
|
|||
|
<div class="chart-header">
|
|||
|
<div class="chart-title">监控图表 2</div>
|
|||
|
<select class="chart-select" data-chart-index="1">
|
|||
|
${options1}
|
|||
|
</select>
|
|||
|
</div>
|
|||
|
<div class="chart-container">
|
|||
|
<canvas id="chart1"></canvas>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
<div class="chart-card">
|
|||
|
<div class="chart-header">
|
|||
|
<div class="chart-title">监控图表 3</div>
|
|||
|
<select class="chart-select" data-chart-index="2">
|
|||
|
${options2}
|
|||
|
</select>
|
|||
|
</div>
|
|||
|
<div class="chart-container">
|
|||
|
<canvas id="chart2"></canvas>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
<div class="chart-card">
|
|||
|
<div class="chart-header">
|
|||
|
<div class="chart-title">监控图表 4</div>
|
|||
|
<select class="chart-select" data-chart-index="3">
|
|||
|
${options3}
|
|||
|
</select>
|
|||
|
</div>
|
|||
|
<div class="chart-container">
|
|||
|
<canvas id="chart3"></canvas>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
`;
|
|||
|
}
|
|||
|
|
|||
|
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 += `<option value="${option}" ${selected}>${option}</option>`;
|
|||
|
});
|
|||
|
return html;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
customElements.define('system-info', SystemInfo);
|