285 lines
8.3 KiB
HTML
285 lines
8.3 KiB
HTML
<!DOCTYPE html>
|
||
<html>
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<title>性能监控</title>
|
||
<style>
|
||
body {
|
||
width: 100%;
|
||
height: 100%;
|
||
margin: 0;
|
||
padding: 20px;
|
||
font-family: 'Segoe UI', Arial, sans-serif;
|
||
color: black;
|
||
border-radius: 8px;
|
||
transition: all 0.3s ease;
|
||
user-select: none;
|
||
box-sizing: border-box;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.drag-bar {
|
||
height: 32px;
|
||
background: rgba(255, 255, 255, 0.5);
|
||
border-radius: 4px;
|
||
-webkit-app-region: drag;
|
||
margin-bottom: 15px;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 0 10px;
|
||
backdrop-filter: blur(10px);
|
||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||
}
|
||
|
||
.controls {
|
||
-webkit-app-region: no-drag;
|
||
}
|
||
|
||
button {
|
||
background: rgba(255, 255, 255, 0.8);
|
||
border: 1px solid rgba(0, 0, 0, 0.2);
|
||
color: black;
|
||
padding: 5px 10px;
|
||
border-radius: 4px;
|
||
cursor: pointer;
|
||
backdrop-filter: blur(5px);
|
||
transition: all 0.2s ease;
|
||
font-family: 'Segoe UI', Arial, sans-serif;
|
||
font-size: 12px;
|
||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
button:hover {
|
||
background: rgba(255, 255, 255, 1);
|
||
border: 1px solid rgba(0, 0, 0, 0.3);
|
||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
|
||
}
|
||
|
||
button:active {
|
||
background: rgba(240, 240, 240, 1);
|
||
border: 1px solid rgba(0, 0, 0, 0.25);
|
||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05) inset;
|
||
}
|
||
|
||
.metrics {
|
||
font-size: 14px;
|
||
display: grid;
|
||
grid-template-columns: 1fr;
|
||
gap: 10px;
|
||
}
|
||
|
||
.metric-item {
|
||
margin: 0;
|
||
padding: 4px 0;
|
||
border-radius: 6px;
|
||
transition: all 0.2s ease;
|
||
padding: 10px 14px;
|
||
background: rgba(255, 255, 255, 0.7);
|
||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.metric-item:hover {
|
||
background: rgba(255, 255, 255, 0.9);
|
||
border: 1px solid rgba(0, 0, 0, 0.2);
|
||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.metric-label {
|
||
font-weight: 500;
|
||
}
|
||
|
||
.metric-value {
|
||
font-weight: 600;
|
||
font-size: 1.1em;
|
||
}
|
||
|
||
.settings {
|
||
margin-top: 15px;
|
||
padding-top: 10px;
|
||
border-top: 1px solid rgba(200, 200, 200, 0.1);
|
||
}
|
||
|
||
input[type='checkbox'] {
|
||
accent-color: #0078d4;
|
||
}
|
||
|
||
/* 动画类 */
|
||
.window-container {
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.minimized {
|
||
transform: scale(0.1);
|
||
opacity: 0.7;
|
||
}
|
||
|
||
/* 响应式设计 */
|
||
@media (max-width: 350px) {
|
||
.metrics {
|
||
grid-template-columns: 1fr 1fr;
|
||
}
|
||
.metric-value {
|
||
font-weight: 600;
|
||
font-size: 0.8em;
|
||
}
|
||
|
||
body {
|
||
padding: 20px;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 150px) {
|
||
.metrics {
|
||
grid-template-columns: 1fr;
|
||
font-size: 10px;
|
||
gap: 3px;
|
||
}
|
||
body {
|
||
padding: 5px;
|
||
}
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="window-container" id="window-container">
|
||
<!-- 拖动区域 -->
|
||
<div style="-webkit-app-region: drag; position: absolute; top: 0; left: 0; right: 0; height: 30px; z-index: -1"></div>
|
||
|
||
<div class="metrics">
|
||
<div class="metric-item">
|
||
<span class="metric-label">CPU</span>
|
||
<span class="metric-value" id="cpu">0%</span>
|
||
</div>
|
||
<div class="metric-item">
|
||
<span class="metric-label">内存</span>
|
||
<span class="metric-value" id="memory">0%</span>
|
||
</div>
|
||
<div class="metric-item">
|
||
<span class="metric-label">网络</span>
|
||
<span class="metric-value" id="network">0 KB/s</span>
|
||
</div>
|
||
<div class="metric-item">
|
||
<span class="metric-label">GPU</span>
|
||
<span class="metric-value" id="gpu">0%</span>
|
||
</div>
|
||
<div class="metric-item">
|
||
<span class="metric-label">显存</span>
|
||
<span class="metric-value" id="gpu-memory">0 MB</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
// 引入性能监控库
|
||
const si = require('systeminformation')
|
||
const pidusage = require('pidusage')
|
||
const process = require('process')
|
||
|
||
// 获取元素
|
||
const cpuEl = document.getElementById('cpu')
|
||
const memoryEl = document.getElementById('memory')
|
||
const networkEl = document.getElementById('network')
|
||
const gpuEl = document.getElementById('gpu')
|
||
const gpuMemoryEl = document.getElementById('gpu-memory')
|
||
|
||
// 跟踪窗口聚焦状态
|
||
let isWindowFocused = true
|
||
let lastUpdateTime = 0
|
||
|
||
// 监听窗口聚焦/失焦事件
|
||
const { ipcRenderer } = require('electron')
|
||
|
||
ipcRenderer.on('window-focused', () => {
|
||
isWindowFocused = true
|
||
})
|
||
|
||
ipcRenderer.on('window-blurred', () => {
|
||
isWindowFocused = false
|
||
})
|
||
|
||
// 监听主进程发送的GPU信息
|
||
ipcRenderer.on('gpu-info', (event, gpuInfo) => {
|
||
// 过滤掉任何剩余的虚拟GPU信息
|
||
const physicalGpus = gpuInfo.filter(gpu => {
|
||
// 检查是否为虚拟GPU
|
||
const isVirtual = gpu.name.includes('Oray') || gpu.name.includes('Virtual') || gpu.name.includes('Software')
|
||
return !isVirtual && (gpu.memoryUsed !== undefined || gpu.vram !== undefined)
|
||
})
|
||
|
||
if (physicalGpus && physicalGpus.length > 0) {
|
||
// 显示第一个物理GPU的信息
|
||
const gpu = physicalGpus[0]
|
||
|
||
// 显示GPU使用率(如果可用)
|
||
if (gpu.utilizationGpu !== undefined) {
|
||
gpuEl.textContent = Math.round(gpu.utilizationGpu) + '%'
|
||
}
|
||
|
||
// 显示显存使用情况
|
||
// 优先使用memoryUsed和memoryTotal,如果没有则使用vram
|
||
if (gpu.memoryUsed !== undefined && gpu.memoryTotal !== undefined) {
|
||
// 转换为MB单位
|
||
const usedMB = Math.round(gpu.memoryUsed)
|
||
const totalMB = Math.round(gpu.memoryTotal)
|
||
gpuMemoryEl.textContent = usedMB + ' / ' + totalMB + ' MB'
|
||
} else if (gpu.vram !== undefined) {
|
||
// 如果只有vram信息,显示总显存
|
||
const totalMB = Math.round(gpu.vram)
|
||
gpuMemoryEl.textContent = '总共: ' + totalMB + ' MB'
|
||
} else {
|
||
gpuMemoryEl.textContent = 'N/A'
|
||
}
|
||
} else {
|
||
gpuMemoryEl.textContent = 'N/A'
|
||
}
|
||
})
|
||
|
||
// 使用requestAnimationFrame实现性能数据更新,但增加最小更新间隔
|
||
let lastTime = 0
|
||
const focusedUpdateInterval = 200 // 聚焦时1秒更新一次
|
||
const blurredUpdateInterval = 1500 // 失焦时5秒更新一次
|
||
|
||
async function updatePerformanceData(timestamp) {
|
||
// 根据窗口聚焦状态确定更新间隔
|
||
const updateInterval = isWindowFocused ? focusedUpdateInterval : blurredUpdateInterval
|
||
|
||
if (timestamp - lastTime >= updateInterval) {
|
||
try {
|
||
// 获取CPU使用率
|
||
const cpuData = await si.currentLoad()
|
||
const cpuUsage = Math.round(cpuData.currentLoad)
|
||
|
||
// 获取内存使用率
|
||
const memData = await si.mem()
|
||
const memoryUsage = Math.round((memData.active / memData.total) * 100)
|
||
|
||
// 获取网络使用情况
|
||
const networkData = await si.networkStats()
|
||
const networkUsage = networkData[0] ? Math.round((networkData[0].rx_sec + networkData[0].tx_sec) / 1024) : 0 // KB/s
|
||
|
||
// 更新UI
|
||
cpuEl.textContent = cpuUsage + '%'
|
||
memoryEl.textContent = memoryUsage + '%'
|
||
networkEl.textContent = networkUsage + ' KB/s'
|
||
} catch (error) {
|
||
console.error('获取性能数据时出错:', error)
|
||
}
|
||
|
||
lastTime = timestamp
|
||
}
|
||
|
||
requestAnimationFrame(updatePerformanceData)
|
||
}
|
||
|
||
// 启动动画循环
|
||
requestAnimationFrame(updatePerformanceData)
|
||
</script>
|
||
</body>
|
||
</html>
|