Files
YG_FT_Platform/web/js/pages/render.js
WIN-JHFT4D3SIVT\caoxiaozhu 513e96082c 重构了main.html的主函数
重构了大量的页面的sidebar
优化了代码结构
2026-02-02 09:22:52 +08:00

724 lines
39 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 页面渲染模块
* 包含各类型页面的渲染函数
*/
// 渲染日志查看页面
function renderLogViewerPage(config) {
return `
<div class="bg-white rounded-lg shadow-sm mb-6">
<div class="flex items-center justify-between p-4 border-b border-gray-100">
<h2 class="text-lg font-medium">${config.title}</h2>
<div class="flex items-center space-x-2">
<button onclick="SystemService.refreshLogs()" class="px-3 py-1.5 text-sm bg-primary text-white rounded hover:bg-primary/90 transition-colors">
<i class="fa fa-refresh mr-1"></i>刷新
</button>
</div>
</div>
<div class="p-4">
<!-- 日志类型切换 -->
<div class="flex items-center mb-4">
<div class="flex bg-gray-100 rounded-lg p-1">
<button id="logTabSystem" onclick="SystemService.switchLogTab('system')" class="px-4 py-1.5 text-sm rounded-md transition-colors bg-white shadow-sm text-primary">
系统日志
</button>
<button id="logTabTraining" onclick="SystemService.switchLogTab('training')" class="px-4 py-1.5 text-sm rounded-md transition-colors text-gray-600 hover:text-gray-800">
训练日志
</button>
</div>
</div>
<!-- 系统日志选项 -->
<div id="systemLogOptions">
<!-- 日期选择 -->
<div class="flex items-center flex-wrap gap-4 mb-4">
<div class="flex items-center">
<label class="text-sm text-gray-600 mr-3">选择日期:</label>
<input type="date" id="logDatePicker" class="px-3 py-1.5 border border-gray-300 rounded text-sm focus:border-primary focus:outline-none" onchange="SystemService.loadLogFiles()">
</div>
<div class="flex items-center">
<label class="text-sm text-gray-600 mr-3">自动刷新:</label>
<select id="logRefreshInterval" onchange="SystemService.setRefreshInterval()" class="px-3 py-1.5 border border-gray-300 rounded text-sm focus:border-primary focus:outline-none">
<option value="0">关闭</option>
<option value="5">5秒</option>
<option value="10" selected>10秒</option>
<option value="30">30秒</option>
<option value="60">60秒</option>
</select>
</div>
<div id="logRefreshCountdown" class="text-sm text-gray-500 hidden">
<i class="fa fa-clock-o mr-1"></i><span>下次刷新: <span id="countdownNumber">10</span>秒</span>
</div>
</div>
<!-- 日志类型选择 -->
<div class="flex items-center mb-4">
<label class="text-sm text-gray-600 mr-3">日志类型:</label>
<select id="logTypeSelect" onchange="SystemService.loadSelectedLog()" class="px-3 py-1.5 border border-gray-300 rounded text-sm focus:border-primary focus:outline-none">
<option value="">请选择日志文件</option>
</select>
</div>
</div>
<!-- 训练日志选项(初始隐藏) -->
<div id="trainingLogOptions" class="hidden">
<div class="flex items-center mb-4">
<label class="text-sm text-gray-600 mr-3">训练日志:</label>
<select id="trainingLogSelect" onchange="SystemService.loadSelectedTrainingLog()" class="px-3 py-1.5 border border-gray-300 rounded text-sm focus:border-primary focus:outline-none flex-1">
<option value="">请选择训练日志</option>
</select>
</div>
</div>
<!-- 日志内容显示 -->
<div class="border border-gray-200 rounded-lg">
<div class="flex items-center justify-between px-4 py-2 bg-gray-50 border-b border-gray-200">
<span class="text-sm text-gray-600" id="logFileInfo">请选择日志文件</span>
<div class="flex items-center space-x-3">
<input type="text" id="logSearchInput" placeholder="搜索日志..." oninput="SystemService.filterLogContent()" class="px-2 py-1 text-xs border border-gray-300 rounded focus:border-primary focus:outline-none">
<span id="logMatchCount" class="text-xs text-gray-500"></span>
<button onclick="SystemService.clearLogContent()" class="text-xs text-gray-500 hover:text-primary">清空</button>
</div>
</div>
<pre id="logContent" class="p-4 text-xs font-mono bg-gray-900 text-gray-100 overflow-auto max-h-[600px]" style="white-space: pre-wrap; word-wrap: break-word;">日志内容将在这里显示...</pre>
</div>
</div>
</div>
`;
}
// 渲染工具卡片页面
function renderToolsPage(config) {
const renderToolCard = (tool, canDelete = false, isCustom = false) => `
<div class="relative group border border-gray-200 rounded-lg p-6 cursor-pointer hover:border-primary hover:shadow-md transition-all" onclick="navigateToTool('${tool.id}', '${tool.url || ''}', ${isCustom})">
<div class="w-12 h-12 rounded-lg bg-primary/10 flex items-center justify-center mb-4 group-hover:bg-primary/20 transition-colors">
<i class="fa ${tool.icon} text-xl text-primary"></i>
</div>
<h3 class="text-base font-medium text-gray-800 mb-2">${tool.name}</h3>
<p class="text-sm text-gray-500">${tool.description}</p>
${canDelete ? `
<button onclick="event.stopPropagation(); editCustomTool('${tool.id}')" class="absolute top-2 right-10 w-6 h-6 rounded-full bg-gray-100 hover:bg-blue-100 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity z-10" title="修改">
<i class="fa fa-pencil text-gray-400 hover:text-blue-500 text-xs"></i>
</button>
<button onclick="event.stopPropagation(); deleteCustomTool('${tool.id}')" class="absolute top-2 right-2 w-6 h-6 rounded-full bg-gray-100 hover:bg-red-100 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity z-10" title="删除">
<i class="fa fa-times text-gray-400 hover:text-red-500 text-xs"></i>
</button>
` : ''}
</div>
`;
const defaultCards = config.defaultTools.map(t => renderToolCard(t, false, false)).join('');
const customCards = config.customTools.map(t => renderToolCard(t, true, true)).join('');
return `
<div class="bg-white rounded-lg shadow-sm mb-6">
<div class="flex items-center justify-between p-4 border-b border-gray-100">
<h2 class="text-lg font-medium">${config.title}</h2>
<button onclick="showCreateToolModal()" class="bg-primary text-white px-3 py-1.5 rounded text-sm hover:bg-primary/90 transition-colors">
<i class="fa fa-plus mr-1"></i>添加自定义工具
</button>
</div>
<div class="p-6">
<!-- 默认工具 -->
<h3 class="text-sm font-semibold text-gray-700 mb-4 pb-2 border-b border-gray-100">默认工具</h3>
<div class="grid grid-cols-3 gap-6 mb-8">
${defaultCards || '<p class="text-gray-400 text-sm col-span-3">暂无默认工具</p>'}
</div>
<!-- 自定义工具 -->
<h3 class="text-sm font-semibold text-gray-700 mb-4 pb-2 border-b border-gray-100">自定义工具</h3>
<div class="grid grid-cols-3 gap-6">
${customCards || '<p class="text-gray-400 text-sm col-span-3">暂无自定义工具,点击右上角添加</p>'}
</div>
</div>
</div>
`;
}
// 删除自定义工具
function deleteCustomTool(toolId) {
window.showConfirm('确认删除', '确定要删除这个自定义工具吗?', () => {
const config = window.tableConfigs['data-generate'];
config.customTools = config.customTools.filter(t => t.id !== toolId);
localStorage.setItem('customTools', JSON.stringify(config.customTools));
document.getElementById('page-content').innerHTML = renderToolsPage(config);
});
}
// 修改自定义工具
function editCustomTool(toolId) {
const config = window.tableConfigs['data-generate'];
const tool = config.customTools.find(t => t.id === toolId);
if (tool) {
localStorage.setItem('editTool', JSON.stringify(tool));
window.location.href = 'custom-tool-create.html?edit=true';
}
}
// 显示创建工具弹窗
function showCreateToolModal() {
window.location.href = 'custom-tool-create.html';
}
// 跳转到工具页面
function navigateToTool(toolId, url, isCustom = false) {
if (isCustom && url) {
if (url.startsWith('http')) {
window.open(url, '_blank');
} else {
window.location.href = url;
}
} else if (toolId === 'data-generate') {
window.location.href = 'data-generate.html';
} else if (toolId === 'json2jsonl') {
window.showMessage('提示', 'JSON转JSONL 工具开发中...', 'info');
} else if (toolId === 'md-convert') {
window.showMessage('提示', '转换Markdown 工具开发中...', 'info');
} else {
window.showMessage('提示', `${toolId} 功能开发中...`, 'info');
}
}
// 渲染配置页面(硬件监控)
function renderConfigPage(config, data) {
return `
<div class="bg-white rounded-lg shadow-sm mb-6">
<div class="flex items-center justify-between p-4 border-b border-gray-100">
<h2 class="text-lg font-medium">${config.title}</h2>
<div class="flex items-center text-sm text-gray-500">
<i class="fa fa-refresh mr-2"></i>
<span class="mr-2">刷新频率:</span>
<select id="refreshInterval" onchange="changeRefreshRate()" class="px-2 py-1 border border-gray-300 rounded text-sm focus:border-primary focus:outline-none">
<option value="1000">1秒</option>
<option value="3000">3秒</option>
<option value="5000" selected>5秒</option>
</select>
</div>
</div>
<div class="p-6">
<!-- 第一行CPU、内存、磁盘 -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-6">
<!-- CPU监控 -->
<div class="border border-gray-200 rounded-lg p-5">
<div class="flex items-center justify-between mb-4">
<div class="flex items-center">
<div class="w-10 h-10 rounded-lg bg-blue-100 flex items-center justify-center mr-3">
<i class="fa fa-microchip text-blue-600 text-lg"></i>
</div>
<div>
<h3 class="text-sm font-medium text-gray-800">CPU 使用率</h3>
<p class="text-xs text-gray-500" id="cpuCores">4 核心</p>
</div>
</div>
<div class="text-right">
<span class="text-2xl font-medium text-gray-800" id="cpuPercent">0%</span>
</div>
</div>
<div class="relative h-4 bg-gray-100 rounded-full overflow-hidden">
<div id="cpuBar" class="absolute left-0 top-0 h-full bg-gradient-to-r from-green-400 to-blue-500 transition-all duration-500" style="width: 0%"></div>
</div>
<div class="mt-4 grid grid-cols-4 gap-2" id="cpuCoresList">
<div class="text-center p-2 bg-gray-50 rounded">
<div class="text-xs text-gray-500">核心1</div>
<div class="text-sm font-medium text-gray-800" id="core1">0%</div>
</div>
<div class="text-center p-2 bg-gray-50 rounded">
<div class="text-xs text-gray-500">核心2</div>
<div class="text-sm font-medium text-gray-800" id="core2">0%</div>
</div>
<div class="text-center p-2 bg-gray-50 rounded">
<div class="text-xs text-gray-500">核心3</div>
<div class="text-sm font-medium text-gray-800" id="core3">0%</div>
</div>
<div class="text-center p-2 bg-gray-50 rounded">
<div class="text-xs text-gray-500">核心4</div>
<div class="text-sm font-medium text-gray-800" id="core4">0%</div>
</div>
</div>
</div>
<!-- 内存监控 -->
<div class="border border-gray-200 rounded-lg p-5">
<div class="flex items-center justify-between mb-4">
<div class="flex items-center">
<div class="w-10 h-10 rounded-lg bg-purple-100 flex items-center justify-center mr-3">
<i class="fa fa-database text-purple-600 text-lg"></i>
</div>
<div>
<h3 class="text-sm font-medium text-gray-800">内存使用</h3>
<p class="text-xs text-gray-500" id="memoryTotal">总计: 16 GB</p>
</div>
</div>
<div class="text-right">
<span class="text-2xl font-medium text-gray-800" id="memoryPercent">0%</span>
</div>
</div>
<div class="relative h-4 bg-gray-100 rounded-full overflow-hidden">
<div id="memoryBar" class="absolute left-0 top-0 h-full bg-gradient-to-r from-yellow-400 to-orange-500 transition-all duration-500" style="width: 0%"></div>
</div>
<div class="mt-4 flex justify-between text-sm">
<div class="text-center">
<div class="text-xs text-gray-500">已用</div>
<div class="text-sm font-medium text-gray-800" id="memoryUsed">0 GB</div>
</div>
<div class="text-center">
<div class="text-xs text-gray-500">可用</div>
<div class="text-sm font-medium text-gray-800" id="memoryAvailable">0 GB</div>
</div>
<div class="text-center">
<div class="text-xs text-gray-500">缓存</div>
<div class="text-sm font-medium text-gray-800" id="memoryCached">0 GB</div>
</div>
</div>
</div>
<!-- 磁盘监控 -->
<div class="border border-gray-200 rounded-lg p-5">
<div class="flex items-center justify-between mb-4">
<div class="flex items-center">
<div class="w-10 h-10 rounded-lg bg-green-100 flex items-center justify-center mr-3">
<i class="fa fa-hdd-o text-green-600 text-lg"></i>
</div>
<div>
<h3 class="text-sm font-medium text-gray-800">磁盘使用</h3>
<p class="text-xs text-gray-500" id="diskTotal">SSD 512 GB</p>
</div>
</div>
<div class="text-right">
<span class="text-2xl font-medium text-gray-800" id="diskPercent">0%</span>
</div>
</div>
<div class="relative h-4 bg-gray-100 rounded-full overflow-hidden">
<div id="diskBar" class="absolute left-0 top-0 h-full bg-gradient-to-r from-emerald-400 to-teal-500 transition-all duration-500" style="width: 0%"></div>
</div>
<div class="mt-4 grid grid-cols-3 gap-2 text-center">
<div class="p-2 bg-gray-50 rounded">
<div class="text-xs text-gray-500">已用空间</div>
<div class="text-sm font-medium text-gray-800" id="diskUsed">0 GB</div>
</div>
<div class="p-2 bg-gray-50 rounded">
<div class="text-xs text-gray-500">可用空间</div>
<div class="text-sm font-medium text-gray-800" id="diskAvailable">0 GB</div>
</div>
<div class="p-2 bg-gray-50 rounded">
<div class="text-xs text-gray-500">读写速度</div>
<div class="text-sm font-medium text-gray-800" id="diskSpeed">0 MB/s</div>
</div>
</div>
</div>
</div>
<!-- 第二行GPU监控 -->
<div class="mb-6">
<div class="flex items-center mb-4">
<div class="w-10 h-10 rounded-lg bg-red-100 flex items-center justify-center mr-3">
<i class="fa fa-microchip text-red-600 text-lg"></i>
</div>
<div>
<h3 class="text-sm font-medium text-gray-800">GPU监控</h3>
<p class="text-xs text-gray-500" id="gpuCount">多GPU并行监控</p>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4" id="gpuList" style="max-height: 400px; overflow-y: auto;">
<!-- GPU卡片由initGPUList()动态生成 -->
</div>
</div>
<!-- 第三行:网络和系统 -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<!-- 网络流量 -->
<div class="border border-gray-200 rounded-lg p-5">
<div class="flex items-center mb-4">
<div class="w-10 h-10 rounded-lg bg-cyan-100 flex items-center justify-center mr-3">
<i class="fa fa-globe text-cyan-600 text-lg"></i>
</div>
<div>
<h3 class="text-sm font-medium text-gray-800">网络流量</h3>
<p class="text-xs text-gray-500">实时带宽使用</p>
</div>
</div>
<div class="grid grid-cols-2 gap-4">
<div class="p-3 bg-blue-50 rounded-lg">
<div class="flex items-center mb-2">
<i class="fa fa-arrow-down text-blue-600 mr-2"></i>
<span class="text-xs text-gray-600">下载速度</span>
</div>
<div class="text-lg font-medium text-gray-800" id="downloadSpeed">0 MB/s</div>
</div>
<div class="p-3 bg-green-50 rounded-lg">
<div class="flex items-center mb-2">
<i class="fa fa-arrow-up text-green-600 mr-2"></i>
<span class="text-xs text-gray-600">上传速度</span>
</div>
<div class="text-lg font-medium text-gray-800" id="uploadSpeed">0 MB/s</div>
</div>
</div>
<div class="mt-4 flex justify-between text-xs text-gray-500">
<span>总流入: <span id="totalDownload">0 GB</span></span>
<span>总流出: <span id="totalUpload">0 GB</span></span>
</div>
</div>
<!-- 系统信息 -->
<div class="border border-gray-200 rounded-lg p-5">
<div class="flex items-center mb-4">
<div class="w-10 h-10 rounded-lg bg-indigo-100 flex items-center justify-center mr-3">
<i class="fa fa-info-circle text-indigo-600 text-lg"></i>
</div>
<div>
<h3 class="text-sm font-medium text-gray-800">系统信息</h3>
<p class="text-xs text-gray-500">服务器状态</p>
</div>
</div>
<div class="space-y-3 text-sm">
<div class="flex justify-between py-2 border-b border-gray-100">
<span class="text-gray-500">操作系统</span>
<span class="text-gray-800 font-medium" id="osInfo">Ubuntu 22.04 LTS</span>
</div>
<div class="flex justify-between py-2 border-b border-gray-100">
<span class="text-gray-500">运行时间</span>
<span class="text-gray-800 font-medium" id="uptime">0 天 0 时 0 分</span>
</div>
<div class="flex justify-between py-2 border-b border-gray-100">
<span class="text-gray-500">进程数</span>
<span class="text-gray-800 font-medium" id="processCount">0</span>
</div>
<div class="flex justify-between py-2">
<span class="text-gray-500">负载均值</span>
<span class="text-gray-800 font-medium" id="loadAvg">0.00, 0.00, 0.00</span>
</div>
</div>
</div>
</div>
</div>
</div>
`;
}
// 刷新间隔定时器
let refreshTimer = null;
let currentRefreshInterval = 5000;
// 刷新硬件信息
async function refreshHardwareInfo() {
try {
const response = await fetch(`${window.API_BASE}/system-info`);
const result = await response.json();
if (result.code === 0 && result.data) {
const data = result.data;
// 更新CPU
const cpu = data.cpu || {};
const cpuPercent = cpu.percent || 0;
const cpuEl = document.getElementById('cpuPercent');
if (cpuEl) {
cpuEl.textContent = cpuPercent + '%';
document.getElementById('cpuBar').style.width = cpuPercent + '%';
document.getElementById('cpuCores').textContent = (cpu.cores || 0) + ' 核心';
}
// 更新内存
const mem = data.memory || {};
const memUsed = mem.used_gb || 0;
const memTotal = mem.total_gb || 0;
const memPercent = mem.percent || 0;
document.getElementById('memoryPercent').textContent = memPercent + '%';
document.getElementById('memoryBar').style.width = memPercent + '%';
document.getElementById('memoryUsed').textContent = memUsed + ' GB';
document.getElementById('memoryAvailable').textContent = (mem.available_gb || 0) + ' GB';
document.getElementById('memoryCached').textContent = (mem.cached_gb || 0) + ' GB';
// 更新磁盘
const disk = data.disk || {};
const diskUsed = disk.used_gb || 0;
const diskTotal = disk.total_gb || 0;
const diskPercent = disk.percent || 0;
document.getElementById('diskPercent').textContent = diskPercent + '%';
document.getElementById('diskBar').style.width = diskPercent + '%';
document.getElementById('diskUsed').textContent = diskUsed + ' GB';
document.getElementById('diskAvailable').textContent = (diskTotal - diskUsed) + ' GB';
// 更新网络
const net = data.network || {};
document.getElementById('totalDownload').textContent = (net.download_mb || 0) + ' GB';
document.getElementById('totalUpload').textContent = (net.upload_mb || 0) + ' GB';
// 更新系统信息
const sys = data.system || {};
const uptime = sys.uptime_seconds || 0;
const days = Math.floor(uptime / 86400);
const hours = Math.floor((uptime % 86400) / 3600);
const mins = Math.floor((uptime % 3600) / 60);
document.getElementById('uptime').textContent = days + ' 天 ' + hours + ' 时 ' + mins + ' 分';
document.getElementById('processCount').textContent = sys.process_count || 0;
// 更新GPU信息
updateGPUInfo(data.gpu || []);
}
} catch (error) {
console.error('获取系统信息失败:', error);
useMockData();
}
}
// 使用模拟数据
function useMockData() {
const cpuUsage = Math.floor(Math.random() * 30) + 20;
document.getElementById('cpuPercent').textContent = cpuUsage + '%';
document.getElementById('cpuBar').style.width = cpuUsage + '%';
document.getElementById('core1').textContent = Math.floor(Math.random() * 40 + 20) + '%';
document.getElementById('core2').textContent = Math.floor(Math.random() * 40 + 15) + '%';
document.getElementById('core3').textContent = Math.floor(Math.random() * 40 + 25) + '%';
document.getElementById('core4').textContent = Math.floor(Math.random() * 40 + 10) + '%';
const memUsed = (Math.random() * 4 + 6).toFixed(1);
const memTotal = 16;
const memPercent = Math.floor((memUsed / memTotal) * 100);
document.getElementById('memoryPercent').textContent = memPercent + '%';
document.getElementById('memoryBar').style.width = memPercent + '%';
document.getElementById('memoryUsed').textContent = memUsed + ' GB';
document.getElementById('memoryAvailable').textContent = (memTotal - memUsed).toFixed(1) + ' GB';
document.getElementById('memoryCached').textContent = (Math.random() * 3 + 1).toFixed(1) + ' GB';
const diskUsed = Math.floor(Math.random() * 100 + 150);
const diskTotal = 512;
const diskPercent = Math.floor((diskUsed / diskTotal) * 100);
document.getElementById('diskPercent').textContent = diskPercent + '%';
document.getElementById('diskBar').style.width = diskPercent + '%';
document.getElementById('diskUsed').textContent = diskUsed + ' GB';
document.getElementById('diskAvailable').textContent = (diskTotal - diskUsed) + ' GB';
document.getElementById('diskSpeed').textContent = (Math.random() * 500 + 100).toFixed(0) + ' MB/s';
updateGPUInfo();
document.getElementById('downloadSpeed').textContent = (Math.random() * 100 + 10).toFixed(1) + ' MB/s';
document.getElementById('uploadSpeed').textContent = (Math.random() * 50 + 5).toFixed(1) + ' MB/s';
document.getElementById('totalDownload').textContent = (Math.random() * 500 + 100).toFixed(1) + ' GB';
document.getElementById('totalUpload').textContent = (Math.random() * 200 + 50).toFixed(1) + ' GB';
const days = Math.floor(Math.random() * 30);
const hours = Math.floor(Math.random() * 24);
const mins = Math.floor(Math.random() * 60);
document.getElementById('uptime').textContent = days + ' 天 ' + hours + ' 时 ' + mins + ' 分';
document.getElementById('processCount').textContent = Math.floor(Math.random() * 200 + 100);
document.getElementById('loadAvg').textContent = (Math.random() * 2).toFixed(2) + ', ' + (Math.random() * 1.5).toFixed(2) + ', ' + (Math.random() * 1).toFixed(2);
}
// GPU配置
const GPU_COUNT = 4;
const gpuConfigs = [
{ name: 'NVIDIA RTX 3090', memory: 24 },
{ name: 'NVIDIA RTX 4090', memory: 24 },
{ name: 'NVIDIA A100', memory: 80 },
{ name: 'NVIDIA V100', memory: 32 },
{ name: 'NVIDIA T4', memory: 16 },
{ name: 'NVIDIA L40S', memory: 48 },
{ name: 'NVIDIA H100', memory: 80 },
{ name: 'NVIDIA RTX 4080', memory: 16 }
];
// 初始化GPU列表
async function initGPUList() {
try {
const response = await fetch(`${window.API_BASE}/system-info`);
const result = await response.json();
const gpuData = (result.data && result.data.gpu) || [];
updateGPUInfo(gpuData);
} catch (error) {
console.error('初始化GPU列表失败:', error);
useMockGPUData();
}
}
// 更新GPU信息
function updateGPUInfo(gpuData) {
if (gpuData && gpuData.length > 0) {
const gpuCount = gpuData.length;
document.getElementById('gpuCount').textContent = `检测到 ${gpuCount} 块 GPU`;
let totalUsedMemory = 0;
let totalMemory = 0;
const gpuList = document.getElementById('gpuList');
if (gpuList) {
let gpuCardsHTML = '';
for (let i = 0; i < gpuCount; i++) {
const gpu = gpuData[i];
totalUsedMemory += gpu.memory_used_gb;
totalMemory += gpu.memory_total_gb;
gpuCardsHTML += `
<div class="border border-gray-200 rounded-lg p-2 bg-gradient-to-br from-gray-50 to-gray-100" id="gpuCard${i}">
<div class="flex items-center justify-between mb-1">
<div class="flex items-center min-w-0">
<div class="w-6 h-6 rounded bg-red-100 flex items-center justify-center mr-2 flex-shrink-0">
<i class="fa fa-microchip text-red-600 text-xs"></i>
</div>
<div class="min-w-0">
<div class="text-xs font-medium text-gray-800 truncate" id="gpuName${i}" title="${gpu.name}">${gpu.name}</div>
<div class="text-[10px] text-gray-400">PCIe</div>
</div>
</div>
<div class="text-right flex-shrink-0 ml-2">
<span class="text-sm font-bold text-gray-800" id="gpuPercent${i}">${gpu.gpu_percent}%</span>
</div>
</div>
<div class="relative h-1.5 bg-gray-200 rounded-full overflow-hidden mb-2">
<div id="gpuBar${i}" class="absolute left-0 top-0 h-full bg-gradient-to-r from-green-400 via-yellow-400 to-red-400 transition-all duration-500" style="width: ${gpu.gpu_percent}%"></div>
</div>
<div class="grid grid-cols-4 gap-1 text-center text-[10px]">
<div class="bg-white/80 rounded py-0.5">
<div class="text-gray-400">显存</div>
<div class="font-medium text-gray-700" id="gpuMem${i}">${gpu.memory_used_gb}/${gpu.memory_total_gb} GB</div>
</div>
<div class="bg-white/80 rounded py-0.5">
<div class="text-gray-400">温度</div>
<div class="font-medium ${gpu.temperature >= 80 ? 'text-red-600' : gpu.temperature >= 70 ? 'text-yellow-600' : 'text-gray-800'}" id="gpuTemp${i}">${gpu.temperature}°C</div>
</div>
<div class="bg-white/80 rounded py-0.5">
<div class="text-gray-400">功耗</div>
<div class="font-medium text-gray-700" id="gpuPower${i}">${gpu.power_w} W</div>
</div>
<div class="bg-white/80 rounded py-0.5">
<div class="text-gray-400">Fan</div>
<div class="font-medium text-gray-700" id="gpuFan${i}">${gpu.fan_speed || 0}%</div>
</div>
</div>
<div class="mt-1 grid grid-cols-2 gap-1 text-center text-[9px] text-gray-400">
<div>Clock: <span id="gpuClock${i}">${gpu.clock_mhz || 0} MHz</span></div>
<div>Driver: <span id="gpuDriver${i}">${gpu.driver_version || '-'}</span></div>
</div>
</div>
`;
}
// 如果GPU数量不足4个补充显示总显存
if (gpuCount < 4) {
gpuCardsHTML += `
<div class="border border-gray-200 rounded-lg p-2 bg-gradient-to-br from-gray-50 to-gray-100">
<div class="flex items-center justify-between">
<div class="flex items-center">
<div class="w-6 h-6 rounded bg-gray-200 flex items-center justify-center mr-2">
<i class="fa fa-server text-gray-600 text-xs"></i>
</div>
<div class="text-xs font-medium text-gray-600">总显存使用</div>
</div>
<div class="text-right">
<span class="text-sm font-bold text-gray-800" id="gpuTotalMemory">${totalUsedMemory}/${totalMemory} GB</span>
</div>
</div>
</div>
`;
}
gpuList.innerHTML = gpuCardsHTML;
}
return;
}
useMockGPUData();
}
// 使用模拟GPU数据
function useMockGPUData() {
const gpuCount = Math.min(GPU_COUNT, 8);
let totalUsedMemory = 0;
let totalMemory = 0;
const gpuList = document.getElementById('gpuList');
if (gpuList) {
let gpuCardsHTML = '';
for (let i = 0; i < gpuCount; i++) {
const config = gpuConfigs[i % gpuConfigs.length];
const gpuUsage = Math.floor(Math.random() * 60 + 20);
const memUsed = (Math.random() * config.memory * 0.7 + config.memory * 0.1).toFixed(1);
const temp = Math.floor(Math.random() * 30 + 40);
const power = Math.floor(Math.random() * 150 + 100);
const fan = Math.floor(gpuUsage + Math.random() * 10);
totalUsedMemory += parseFloat(memUsed);
totalMemory += config.memory;
gpuCardsHTML += `
<div class="border border-gray-200 rounded-lg p-2 bg-gradient-to-br from-gray-50 to-gray-100" id="gpuCard${i}">
<div class="flex items-center justify-between mb-1">
<div class="flex items-center min-w-0">
<div class="w-6 h-6 rounded bg-red-100 flex items-center justify-center mr-2 flex-shrink-0">
<i class="fa fa-microchip text-red-600 text-xs"></i>
</div>
<div class="min-w-0">
<div class="text-xs font-medium text-gray-800 truncate" id="gpuName${i}" title="${config.name}">${config.name}</div>
<div class="text-[10px] text-gray-400">PCIe ${Math.floor(Math.random() * 4 + 1)}:00.0</div>
</div>
</div>
<div class="text-right flex-shrink-0 ml-2">
<span class="text-sm font-bold text-gray-800" id="gpuPercent${i}">${gpuUsage}%</span>
</div>
</div>
<div class="relative h-1.5 bg-gray-200 rounded-full overflow-hidden mb-2">
<div id="gpuBar${i}" class="absolute left-0 top-0 h-full bg-gradient-to-r from-green-400 via-yellow-400 to-red-400 transition-all duration-500" style="width: ${gpuUsage}%"></div>
</div>
<div class="grid grid-cols-4 gap-1 text-center text-[10px]">
<div class="bg-white/80 rounded py-0.5">
<div class="text-gray-400">显存</div>
<div class="font-medium text-gray-700" id="gpuMem${i}">${parseFloat(memUsed).toFixed(1)}/${config.memory} GB</div>
</div>
<div class="bg-white/80 rounded py-0.5">
<div class="text-gray-400">温度</div>
<div class="font-medium ${temp >= 80 ? 'text-red-600' : temp >= 70 ? 'text-yellow-600' : 'text-gray-800'}" id="gpuTemp${i}">${temp}°C</div>
</div>
<div class="bg-white/80 rounded py-0.5">
<div class="text-gray-400">功耗</div>
<div class="font-medium text-gray-700" id="gpuPower${i}">${power} W</div>
</div>
<div class="bg-white/80 rounded py-0.5">
<div class="text-gray-400">Fan</div>
<div class="font-medium text-gray-700" id="gpuFan${i}">${fan}%</div>
</div>
</div>
</div>
`;
}
gpuList.innerHTML = gpuCardsHTML;
document.getElementById('gpuCount').textContent = `检测到 ${gpuCount} 块 GPU`;
}
const gpuTotalMem = document.getElementById('gpuTotalMemory');
if (gpuTotalMem) {
gpuTotalMem.textContent = `${totalUsedMemory.toFixed(1)}/${totalMemory} GB`;
}
}
// 启动硬件监控自动刷新
function startRefreshTimer() {
if (refreshTimer) {
clearInterval(refreshTimer);
}
refreshTimer = setInterval(refreshHardwareInfo, currentRefreshInterval);
}
// 改变刷新频率
function changeRefreshRate() {
const select = document.getElementById('refreshInterval');
currentRefreshInterval = parseInt(select.value);
startRefreshTimer();
}
// 保存配置
function saveConfig() {
window.showMessage('提示', '配置保存功能开发中...', 'info');
}
// 导出页面渲染模块
window.PageRenderer = {
renderLogViewerPage,
renderToolsPage,
renderConfigPage,
refreshHardwareInfo,
useMockData,
initGPUList,
updateGPUInfo,
useMockGPUData,
startRefreshTimer,
changeRefreshRate,
saveConfig
};