724 lines
39 KiB
JavaScript
724 lines
39 KiB
JavaScript
/**
|
||
* 页面渲染模块
|
||
* 包含各类型页面的渲染函数
|
||
*/
|
||
|
||
// 渲染日志查看页面
|
||
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
|
||
};
|