This commit is contained in:
2025-10-07 00:57:59 +08:00
parent ca793b4de7
commit c9cc800ed9
5 changed files with 397 additions and 50 deletions

View File

@@ -64,6 +64,23 @@
<version>0.1.55</version> <version>0.1.55</version>
</dependency> </dependency>
</dependencies> </dependencies>
<profiles>
<profile>
<id>test</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<spring.profiles.active>test</spring.profiles.active>
</properties>
</profile>
<profile>
<id>product</id>
<properties>
<spring.profiles.active>product</spring.profiles.active>
</properties>
</profile>
</profiles>
<build> <build>
<plugins> <plugins>

View File

@@ -20,6 +20,7 @@ import java.util.*;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Semaphore;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@RestController @RestController
@@ -71,14 +72,6 @@ public class ApiServerV1 {
} }
} }
} }
@GetMapping("/status/history/{ip}")
public List<Status> getStatus(@PathVariable String ip, @RequestParam(defaultValue = "0", required = false) int limit) {
List<Status> statuses = statusDao.findByHostOrderByTimestampDesc(ip);
if (limit > 0) {
return statuses.stream().limit(limit).collect(Collectors.toList());
}
return statuses;
}
@GetMapping("/server") @GetMapping("/server")
public List<Server> getAllServers() { public List<Server> getAllServers() {
@@ -138,4 +131,178 @@ public class ApiServerV1 {
public void deleteServer(@RequestBody Server server) { public void deleteServer(@RequestBody Server server) {
serverDao.delete(server); serverDao.delete(server);
} }
@GetMapping("/status/history/{ip}")
public List<Status> getStatus(@PathVariable String ip,
@RequestParam(defaultValue = "0000-00-00 00:00:00", required = false) String startDate,
@RequestParam(defaultValue = "9999-99-99 99:99:99", required = false) String endDate) {
System.out.println("getStatus: " + ip + "开始加载");
List<Status> statuses = statusDao.findByHostOrderByTimestampDesc(ip);
System.out.println("getStatus: " + ip + "加载完成");
// 时间范围过滤
LocalDateTime start = parseDateTime(startDate);
LocalDateTime end = parseDateTime(endDate);
List<Status> filteredStatuses = statuses.stream()
.filter(status -> status.getTimestamp() != null)
.filter(status -> !status.getTimestamp().isBefore(start))
.filter(status -> !status.getTimestamp().isAfter(end))
.collect(Collectors.toList());
// 数据优化 - 移除变化较小的数据点
List<Status> optimizedStatuses = optimizeStatusData(filteredStatuses);
System.out.println("getStatus: " + ip + "数据优化完成");
System.out.print(optimizedStatuses.size());
return optimizedStatuses;
}
@GetMapping("/status/history/all/{ips}")
public Map<String,List<Status>> getStatusIps(@PathVariable String ips,
@RequestParam(defaultValue = "0000-00-00 00:00:00", required = false) String startDate,
@RequestParam(defaultValue = "9999-99-99 99:99:99", required = false) String endDate) {
List<String> ipList = Arrays.asList(ips.split(","));
Map<String,List<Status>> optimizedStatuses = new ConcurrentHashMap<>();
// 限制并发数量,避免数据库连接耗尽
Semaphore semaphore = new Semaphore(10); // 最多同时处理10个IP
List<CompletableFuture<Void>> futures = ipList.stream()
.map(ip -> CompletableFuture.runAsync(() -> {
try {
semaphore.acquire(); // 获取许可
List<Status> statuses = statusDao.findByHostOrderByTimestampDesc(ip);
// 时间范围过滤
LocalDateTime start = parseDateTime(startDate);
LocalDateTime end = parseDateTime(endDate);
List<Status> filteredStatuses = statuses.stream()
.filter(status -> status.getTimestamp() != null)
.filter(status -> !status.getTimestamp().isBefore(start))
.filter(status -> !status.getTimestamp().isAfter(end))
.collect(Collectors.toList());
// 数据优化 - 移除变化较小的数据点
List<Status> optimizedStatuse = optimizeStatusData(filteredStatuses);
optimizedStatuses.put(ip, optimizedStatuse);
} catch (Exception e) {
log.error("Error processing status for IP: {}", ip, e);
optimizedStatuses.put(ip, new ArrayList<>());
} finally {
semaphore.release(); // 释放许可
}
}))
.collect(Collectors.toList());
// 等待所有任务完成
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
return optimizedStatuses;
}
private LocalDateTime parseDateTime(String dateTimeStr) {
try {
// 处理默认值情况
if ("0000-00-00 00:00:00".equals(dateTimeStr) || "9999-99-99 99:99:99".equals(dateTimeStr)) {
return "0000-00-00 00:00:00".equals(dateTimeStr) ?
LocalDateTime.MIN : LocalDateTime.MAX;
}
return LocalDateTime.parse(dateTimeStr.replace(" ", "T"));
} catch (Exception e) {
return LocalDateTime.now();
}
}
private List<Status> optimizeStatusData(List<Status> statuses) {
if (statuses.size() <= 2) {
return statuses; // 数据点太少无需优化
}
List<Status> result = new ArrayList<>();
result.add(statuses.get(0)); // 始终保留第一个点
// 计算CPU和内存使用率变化的阈值基于整体数据计算
double cpuThreshold = calculateThreshold(statuses, Status::getCpuUsagePercent);
double memoryThreshold = calculateThreshold(statuses, Status::getMemoryUsagePercent);
// 设置最小阈值,避免过度优化
cpuThreshold = Math.max(cpuThreshold, 0.5); // 最小0.5%
memoryThreshold = Math.max(memoryThreshold, 0.5); // 最小0.5%
Status previousStatus = statuses.get(0);
for (int i = 1; i < statuses.size() - 1; i++) {
Status current = statuses.get(i);
// 检查CPU或内存使用率是否有显著变化
boolean significantChange =
hasSignificantChange(previousStatus, current, cpuThreshold, memoryThreshold);
if (significantChange) {
result.add(current);
previousStatus = current;
}
}
// 始终保留最后一个点
if (!statuses.isEmpty() && !result.contains(statuses.get(statuses.size() - 1))) {
result.add(statuses.get(statuses.size() - 1));
}
return result;
}
private boolean hasSignificantChange(Status prev, Status curr,
double cpuThreshold, double memoryThreshold) {
Double prevCpu = prev.getCpuUsagePercent();
Double currCpu = curr.getCpuUsagePercent();
Double prevMem = prev.getMemoryUsagePercent();
Double currMem = curr.getMemoryUsagePercent();
// 如果任一值为空,则认为有变化
if (prevCpu == null || currCpu == null || prevMem == null || currMem == null) {
return true;
}
// 检查变化是否超过阈值
return Math.abs(currCpu - prevCpu) >= cpuThreshold ||
Math.abs(currMem - prevMem) >= memoryThreshold;
}
// 在Status类中添加辅助方法获取使用率数值
private static class StatusExtensions {
public static Double getCpuUsagePercent(Status status) {
return status.getCpuInfo() != null ? status.getCpuInfo().getUsagePercent() : null;
}
public static Double getMemoryUsagePercent(Status status) {
return status.getMemoryInfo() != null ? status.getMemoryInfo().getUsagePercent() : null;
}
}
private double calculateThreshold(List<Status> statuses, java.util.function.Function<Status, Double> getter) {
// 收集所有有效数值
List<Double> values = statuses.stream()
.map(getter)
.filter(Objects::nonNull)
.collect(Collectors.toList());
if (values.size() < 2) {
return 0.0;
}
// 计算相邻数值间的差值
List<Double> differences = new ArrayList<>();
for (int i = 1; i < values.size(); i++) {
differences.add(Math.abs(values.get(i) - values.get(i-1)));
}
// 计算平均差值作为阈值基础
double averageDifference = differences.stream()
.mapToDouble(Double::doubleValue)
.average()
.orElse(0.0);
// 返回平均差值的一半作为阈值,这样可以过滤掉较小的变化
return averageDifference / 2;
}
} }

View File

@@ -147,6 +147,20 @@ public class Status {
this.error = error; this.error = error;
} }
public Double getCpuUsagePercent() {
if (cpuInfo == null) {
return 0.0;
}
return cpuInfo.getUsagePercent();
}
public Double getMemoryUsagePercent() {
if (memoryInfo == null) {
return 0.0;
}
return memoryInfo.getUsagePercent();
}
// 内部类定义 // 内部类定义
public static class CpuInfo { public static class CpuInfo {
private String modelName; private String modelName;

View File

@@ -0,0 +1,6 @@
spring.application.name=reisaAdminSpring
spring.data.mongodb.uri=mongodb://reisaAdmin:nbAC8hi8xdJeBDDT@127.0.0.1:27017/reisaadmin
server.port=48102
spring.data.redis.host=127.0.0.1
spring.data.redis.port: 6379

View File

@@ -497,13 +497,22 @@
v-model:value="selectedMonitorServers" v-model:value="selectedMonitorServers"
mode="multiple" mode="multiple"
placeholder="请选择要监控的服务器" placeholder="请选择要监控的服务器"
style="width: 800px; margin-right: 16px;" style="width: 600px; margin-right: 16px;"
@change="loadMonitorData" @change="loadMonitorData"
> >
<a-select-option v-for="server in servers" :key="server.id" :value="server.id"> <a-select-option v-for="server in servers" :key="server.id" :value="server.id">
{{ server.name }} ({{ server.ipAddress }}) {{ server.name }} ({{ server.ipAddress }})
</a-select-option> </a-select-option>
</a-select> </a-select>
<a-range-picker
v-model:value="monitorTimeRange"
show-time
format="YYYY-MM-DD HH:mm:ss"
:placeholder="['开始时间', '结束时间']"
style="width: 300px; margin-right: 16px;"
@change="handleTimeRangeChange"
/>
<a-button <a-button
type="primary" type="primary"
@click="loadMonitorData" @click="loadMonitorData"
@@ -511,12 +520,21 @@
> >
刷新数据 刷新数据
</a-button> </a-button>
<a-button <a-button
@click="showMonitor = false" @click="closeMonitor"
style="margin-left: 8px;" style="margin-left: 8px;"
> >
关闭监控 关闭监控
</a-button> </a-button>
<div class="chart-controls" v-if="showMonitor">
<a-switch
v-model:checked="showChartPoints"
checked-children="显示数据点"
un-checked-children="隐藏数据点"
@change="drawCharts"
/>
</div>
</div> </div>
</div> </div>
@@ -532,11 +550,11 @@
<div class="process-list-header"> <div class="process-list-header">
<h3>进程排行</h3> <h3>进程排行</h3>
<div class="process-sort-controls"> <div class="process-sort-controls">
<a-select v-model:value="processSortField" @change="sortProcesses"> <a-select v-model:value="processSortField" @change="sortProcesses" style="width: 120px;">
<a-select-option value="cpuPercent">按CPU排序</a-select-option> <a-select-option value="cpuPercent">按CPU排序</a-select-option>
<a-select-option value="memoryPercent">按内存排序</a-select-option> <a-select-option value="memoryPercent">按内存排序</a-select-option>
</a-select> </a-select>
<a-select v-model:value="processSortOrder" @change="sortProcesses"> <a-select v-model:value="processSortOrder" @change="sortProcesses" style="width: 80px;">
<a-select-option value="desc">降序</a-select-option> <a-select-option value="desc">降序</a-select-option>
<a-select-option value="asc">升序</a-select-option> <a-select-option value="asc">升序</a-select-option>
</a-select> </a-select>
@@ -572,7 +590,7 @@
<!-- 监控大屏切换按钮 --> <!-- 监控大屏切换按钮 -->
<div v-else style="margin-top: 16px; text-align: center;"> <div v-else style="margin-top: 16px; text-align: center;">
<a-button type="primary" @click="showMonitor = true"> <a-button type="primary" @click="openMonitor">
<template #icon> <template #icon>
<BarChartOutlined /> <BarChartOutlined />
</template> </template>
@@ -659,6 +677,7 @@ import {ref, reactive, onMounted, computed, onUnmounted, nextTick} from 'vue';
import { message } from 'ant-design-vue'; import { message } from 'ant-design-vue';
import axios from 'axios'; import axios from 'axios';
import { PlusOutlined, CopyOutlined } from '@ant-design/icons-vue'; import { PlusOutlined, CopyOutlined } from '@ant-design/icons-vue';
import dayjs from 'dayjs';
// 状态管理 // 状态管理
const servers = ref([]); const servers = ref([]);
@@ -673,6 +692,7 @@ const sortField = ref('name');
const sortOrder = ref('asc'); const sortOrder = ref('asc');
const localSearchName = ref(''); const localSearchName = ref('');
const showMonitor = ref(true); const showMonitor = ref(true);
const showChartPoints = ref(false);
const deviceInfoModalVisible = ref(false); const deviceInfoModalVisible = ref(false);
document.title = 'ReisaAdmin'; document.title = 'ReisaAdmin';
@@ -851,6 +871,7 @@ const columns = [
// 初始化加载数据 // 初始化加载数据
onMounted(() => { onMounted(() => {
openMonitor()
fetchServers(); fetchServers();
}); });
@@ -1156,11 +1177,14 @@ const closeDeviceInfoModal = () => {
deviceInfoModalVisible.value = false; deviceInfoModalVisible.value = false;
stopDeviceInfoRefreshTimer(); stopDeviceInfoRefreshTimer();
}; };
// 大屏监控相关状态 // 大屏监控相关状态
const monitorVisible = ref(false); const monitorVisible = ref(false);
const selectedMonitorServers = ref([]); // 多选的服务器 const selectedMonitorServers = ref([]); // 多选的服务器
const monitorHistoryData = ref({}); // 存储各服务器的历史数据 const monitorHistoryData = ref({}); // 存储各服务器的历史数据
const monitorLoading = ref(false); const monitorLoading = ref(false);
const monitorRefreshTimer = ref(null); // 监控数据自动刷新定时器
const monitorTimeRange = ref([]); // 监控时间范围
// 图表实例引用 // 图表实例引用
const cpuChartRef = ref(null); const cpuChartRef = ref(null);
@@ -1196,15 +1220,45 @@ const sortProcesses = () => {
// 打开监控大屏 // 打开监控大屏
const openMonitor = () => { const openMonitor = () => {
monitorVisible.value = true; showMonitor.value = true;
selectedMonitorServers.value = []; selectedMonitorServers.value = [];
monitorHistoryData.value = {}; monitorHistoryData.value = {};
// 设置默认时间范围为最近1小时
const endTime = dayjs();
const startTime = endTime.subtract(1, 'hour');
monitorTimeRange.value = [startTime, endTime];
startMonitorRefreshTimer();
}; };
// 获取服务器历史数据 // 处理时间范围变化
const handleTimeRangeChange = () => {
if (selectedMonitorServers.value.length > 0) {
loadMonitorData();
}
};
const formatTime = (timestamp) => {
if (!timestamp) return 'N/A';
const date = new Date(timestamp);
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
return `${month}-${day} ${hours}:${minutes}`;
};
// 获取服务器历史数据(修改后的版本,支持时间范围)
const fetchServerHistory = async (serverIp) => { const fetchServerHistory = async (serverIp) => {
try { try {
const response = await axios.get(`/api/v1/status/history/${serverIp}?limit=50`); const params = {};
// 如果设置了时间范围,则添加时间参数
if (monitorTimeRange.value && monitorTimeRange.value.length === 2) {
const [startTime, endTime] = monitorTimeRange.value;
params.startDate = startTime.format('YYYY-MM-DD HH:mm:ss');
params.endDate = endTime.format('YYYY-MM-DD HH:mm:ss');
}
const response = await axios.get(`/api/v1/status/history/${serverIp}`, { params });
return response.data; return response.data;
} catch (error) { } catch (error) {
message.error(`获取服务器 ${serverIp} 历史数据失败: ${error.message}`); message.error(`获取服务器 ${serverIp} 历史数据失败: ${error.message}`);
@@ -1212,9 +1266,8 @@ const fetchServerHistory = async (serverIp) => {
} }
}; };
// 加载选中服务器的历史数据 // 修改 loadMonitorData 函数以使用新的多IP API
const loadMonitorData = async () => { const loadMonitorData = async () => {
allProcesses.value = [];
if (selectedMonitorServers.value.length === 0) { if (selectedMonitorServers.value.length === 0) {
monitorHistoryData.value = {}; monitorHistoryData.value = {};
allProcesses.value = []; allProcesses.value = [];
@@ -1224,42 +1277,71 @@ const loadMonitorData = async () => {
monitorLoading.value = true; monitorLoading.value = true;
try { try {
const promises = selectedMonitorServers.value.map(serverId => { // 限制同时监控的服务器数量
const server = servers.value.find(s => s.id === serverId); const limitedServers = selectedMonitorServers.value.slice(0, 99999);
return server ? fetchServerHistory(server.ipAddress) : Promise.resolve([]);
});
const results = await Promise.all(promises); // 获取选中服务器的IP地址
const serverIps = limitedServers
.map(serverId => {
const server = servers.value.find(s => s.id === serverId);
return server ? server.ipAddress : null;
})
.filter(Boolean); // 过滤掉null值
if (serverIps.length === 0) {
monitorHistoryData.value = {};
allProcesses.value = [];
drawCharts();
return;
}
// 构造请求参数
const params = {};
if (monitorTimeRange.value && monitorTimeRange.value.length === 2) {
const [startTime, endTime] = monitorTimeRange.value;
params.startDate = startTime.format('YYYY-MM-DD HH:mm:ss');
params.endDate = endTime.format('YYYY-MM-DD HH:mm:ss');
}
// 使用新的多IP API接口
const response = await axios.get(`/api/v1/status/history/all/${serverIps.join(',')}`, { params });
const results = response.data;
// 整理数据 // 整理数据
const newData = {}; const newData = {};
const processes = []; const processes = [];
selectedMonitorServers.value.forEach((serverId, index) => {
limitedServers.forEach((serverId, index) => {
const server = servers.value.find(s => s.id === serverId); const server = servers.value.find(s => s.id === serverId);
if (server) { if (server && results[server.ipAddress]) {
newData[serverId] = { newData[serverId] = {
serverName: server.name, serverName: server.name,
serverIp: server.ipAddress, serverIp: server.ipAddress,
history: results[index] history: results[server.ipAddress]
}; };
// 收集所有进程数据 // 限制每个服务器的进程数据数量
results[index].forEach(historyItem => { let serverProcesses = [];
if (historyItem.processes) { if (results[server.ipAddress].length > 0) {
historyItem.processes.forEach(process => { const latestData = results[server.ipAddress][0];
processes.push({ if (latestData.processes) {
...process, // 只取前50个进程
serverName: server.name, serverProcesses = latestData.processes.slice(0, 50);
serverId: serverId
});
});
} }
}
serverProcesses.forEach(process => {
processes.push({
...process,
serverName: server.name,
serverId: serverId
});
}); });
} }
}); });
monitorHistoryData.value = newData; monitorHistoryData.value = newData;
allProcesses.value = processes; allProcesses.value = processes.slice(0, 100); // 限制总进程数
drawCharts(); drawCharts();
} catch (error) { } catch (error) {
message.error('加载监控数据失败: ' + error.message); message.error('加载监控数据失败: ' + error.message);
@@ -1268,6 +1350,7 @@ const loadMonitorData = async () => {
} }
}; };
// 绘制图表 // 绘制图表
const drawCharts = () => { const drawCharts = () => {
// 确保DOM已更新 // 确保DOM已更新
@@ -1276,11 +1359,29 @@ const drawCharts = () => {
drawMemoryChart(); drawMemoryChart();
}); });
}; };
import { Chart, registerables } from 'chart.js'; import { Chart, registerables } from 'chart.js';
import 'chartjs-adapter-date-fns'; import 'chartjs-adapter-date-fns';
// 注册Chart.js的所有组件 // 注册Chart.js的所有组件
Chart.register(...registerables); Chart.register(...registerables);
// 绘制CPU使用率图表
// 数据采样函数 - 当数据点过多时减少数据点数量
const sampleData = (data, maxPoints = 50) => {
if (data.length <= maxPoints) {
return data;
}
const sampled = [];
const step = Math.ceil(data.length / maxPoints);
for (let i = 0; i < data.length; i += step) {
sampled.push(data[i]);
}
return sampled;
};
// 修改 drawCPUChart 函数
const drawCPUChart = () => { const drawCPUChart = () => {
if (!cpuChartRef.value) return; if (!cpuChartRef.value) return;
@@ -1295,18 +1396,25 @@ const drawCPUChart = () => {
const datasets = []; const datasets = [];
Object.keys(monitorHistoryData.value).forEach(serverId => { Object.keys(monitorHistoryData.value).forEach(serverId => {
const serverData = monitorHistoryData.value[serverId]; const serverData = monitorHistoryData.value[serverId];
const data = serverData.history.map(item => ({ let data = serverData.history.map(item => ({
x: new Date(item.timestamp), x: new Date(item.timestamp),
y: item.cpuInfo?.usagePercent || 0 y: item.cpuInfo?.usagePercent || 0
})).reverse(); // 图表从左到右时间顺序 })).reverse(); // 图表从左到右时间顺序
// 当数据量过大时进行采样处理
if (data.length > 50) {
data = sampleData(data, 50);
}
datasets.push({ datasets.push({
label: serverData.serverName, label: serverData.serverName,
data: data, data: data,
borderColor: getColorByIndex(datasets.length), borderColor: getColorByIndex(datasets.length),
backgroundColor: getColorByIndex(datasets.length, 0.1), backgroundColor: getColorByIndex(datasets.length, 0.1),
tension: 0.1, tension: 0.4, // 增加曲线平滑度
fill: false fill: true,
pointRadius: showChartPoints.value ? 3 : 0, // 根据开关控制点的显示
pointHoverRadius: showChartPoints.value ? 5 : 0 // 悬停时点的大小
}); });
}); });
@@ -1318,13 +1426,14 @@ const drawCPUChart = () => {
options: { options: {
responsive: true, responsive: true,
maintainAspectRatio: false, maintainAspectRatio: false,
animation: false, // 禁用动画以提高性能
scales: { scales: {
x: { x: {
type: 'time', type: 'time',
time: { time: {
unit: 'minute', unit: 'minute',
displayFormats: { displayFormats: {
minute: 'HH:mm' minute: 'MM-dd HH:mm'
} }
}, },
title: { title: {
@@ -1355,7 +1464,7 @@ const drawCPUChart = () => {
}); });
}; };
// 绘制内存使用率图表 // 同样需要修改 drawMemoryChart 函数中的点显示逻辑
const drawMemoryChart = () => { const drawMemoryChart = () => {
if (!memoryChartRef.value) return; if (!memoryChartRef.value) return;
@@ -1370,18 +1479,25 @@ const drawMemoryChart = () => {
const datasets = []; const datasets = [];
Object.keys(monitorHistoryData.value).forEach(serverId => { Object.keys(monitorHistoryData.value).forEach(serverId => {
const serverData = monitorHistoryData.value[serverId]; const serverData = monitorHistoryData.value[serverId];
const data = serverData.history.map(item => ({ let data = serverData.history.map(item => ({
x: new Date(item.timestamp), x: new Date(item.timestamp),
y: item.memoryInfo?.usagePercent || 0 y: item.memoryInfo?.usagePercent || 0
})).reverse(); })).reverse();
// 当数据量过大时进行采样处理
if (data.length > 50) {
data = sampleData(data, 50);
}
datasets.push({ datasets.push({
label: serverData.serverName, label: serverData.serverName,
data: data, data: data,
borderColor: getColorByIndex(datasets.length), borderColor: getColorByIndex(datasets.length),
backgroundColor: getColorByIndex(datasets.length, 0.1), backgroundColor: getColorByIndex(datasets.length, 0.1),
tension: 0.1, tension: 0.4, // 增加曲线平滑度
fill: false fill: true,
pointRadius: showChartPoints.value ? 3 : 0, // 根据开关控制点的显示
pointHoverRadius: showChartPoints.value ? 5 : 0 // 悬停时点的大小
}); });
}); });
@@ -1393,13 +1509,14 @@ const drawMemoryChart = () => {
options: { options: {
responsive: true, responsive: true,
maintainAspectRatio: false, maintainAspectRatio: false,
animation: false, // 禁用动画以提高性能
scales: { scales: {
x: { x: {
type: 'time', type: 'time',
time: { time: {
unit: 'minute', unit: 'minute',
displayFormats: { displayFormats: {
minute: 'HH:mm' minute: 'MM-dd HH:mm'
} }
}, },
title: { title: {
@@ -1445,7 +1562,7 @@ const getColorByIndex = (index, alpha = 1) => {
// 关闭监控大屏 // 关闭监控大屏
const closeMonitor = () => { const closeMonitor = () => {
monitorVisible.value = false; showMonitor.value = false;
// 销毁图表实例 // 销毁图表实例
if (cpuChartInstance) { if (cpuChartInstance) {
cpuChartInstance.destroy(); cpuChartInstance.destroy();
@@ -1455,12 +1572,35 @@ const closeMonitor = () => {
memoryChartInstance.destroy(); memoryChartInstance.destroy();
memoryChartInstance = null; memoryChartInstance = null;
} }
stopMonitorRefreshTimer();
};
// 添加启动监控自动刷新函数
const startMonitorRefreshTimer = () => {
// 先清除已存在的定时器
if (monitorRefreshTimer.value) {
clearInterval(monitorRefreshTimer.value);
}
// 启动新的定时器每30秒刷新一次监控数据
monitorRefreshTimer.value = setInterval(() => {
if (showMonitor.value && selectedMonitorServers.value.length > 0) {
loadMonitorData();
}
}, 30000); // 30秒刷新一次
};
// 添加停止监控自动刷新函数
const stopMonitorRefreshTimer = () => {
if (monitorRefreshTimer.value) {
clearInterval(monitorRefreshTimer.value);
monitorRefreshTimer.value = null;
}
}; };
// 在组件卸载时清理定时器 // 在组件卸载时清理定时器
onUnmounted(() => { onUnmounted(() => {
closeMonitor(); closeMonitor();
stopDeviceInfoRefreshTimer(); stopDeviceInfoRefreshTimer();
}); });
</script> </script>
@@ -1531,7 +1671,10 @@ onUnmounted(() => {
color: #1d2129; color: #1d2129;
margin: 0; margin: 0;
} }
.chart-controls {
margin-bottom: 16px;
text-align: right;
}
.add-button { .add-button {
border-radius: 6px; border-radius: 6px;
height: 40px; height: 40px;