Files
param-hub/admin/templates/gpus.html
hubian f249f28eb7 新增后台管理系统
功能模块:
- 仪表盘: 模型/GPU/CPU统计、开源/闭源分布、快速操作
- 模型管理: 查看模型列表、筛选搜索、添加新模型
- GPU管理: 查看GPU列表、厂商筛选、添加新GPU
- CPU管理: 查看CPU列表
- 数据导出: 导出模型/GPU数据为JSON

技术:
- Flask + Tailwind CSS
- 解析TypeScript数据文件
- 端口: 19006
2026-04-08 14:13:06 +08:00

252 lines
12 KiB
HTML
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.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GPU管理 - 参数百科后台</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://cdn.jsdelivr.net/npm/remixicon@3.5.0/fonts/remixicon.css" rel="stylesheet">
</head>
<body class="bg-gray-50 min-h-screen">
<div class="flex">
<aside class="w-64 bg-slate-800 min-h-screen fixed left-0 top-0">
<div class="p-6">
<h1 class="text-white text-xl font-bold flex items-center gap-2">
<i class="ri-database-2-line text-2xl text-blue-400"></i>
参数百科后台
</h1>
</div>
<nav class="mt-6">
<a href="/" class="flex items-center gap-3 px-6 py-3 text-slate-300 hover:bg-slate-700 hover:text-white">
<i class="ri-dashboard-line"></i><span>仪表盘</span>
</a>
<a href="/models" class="flex items-center gap-3 px-6 py-3 text-slate-300 hover:bg-slate-700 hover:text-white">
<i class="ri-cpu-line"></i><span>模型管理</span>
</a>
<a href="/gpus" class="flex items-center gap-3 px-6 py-3 bg-slate-700 text-white">
<i class="ri-dashboard-3-line"></i><span>GPU管理</span>
</a>
<a href="/cpus" class="flex items-center gap-3 px-6 py-3 text-slate-300 hover:bg-slate-700 hover:text-white">
<i class="ri-cpu-line"></i><span>CPU管理</span>
</a>
</nav>
</aside>
<main class="ml-64 flex-1 p-8">
<div class="flex items-center justify-between mb-6">
<h1 class="text-2xl font-bold text-gray-800">GPU管理</h1>
<button onclick="showAddModal()" class="px-4 py-2 bg-green-500 text-white rounded-lg flex items-center gap-2 hover:bg-green-600">
<i class="ri-add-line"></i> 添加GPU
</button>
</div>
<!-- 筛选 -->
<div class="bg-white rounded-lg p-4 mb-6 border border-gray-100">
<div class="flex gap-4 items-center">
<input type="text" id="searchInput" placeholder="搜索GPU名称..."
class="flex-1 px-4 py-2 border border-gray-200 rounded-lg"
onkeyup="filterGpus()">
<select id="filterManufacturer" onchange="filterGpus()" class="px-4 py-2 border border-gray-200 rounded-lg">
<option value="all">全部厂商</option>
<option value="NVIDIA">NVIDIA</option>
<option value="AMD">AMD</option>
</select>
</div>
</div>
<!-- GPU列表 -->
<div class="bg-white rounded-xl border border-gray-100 overflow-x-auto">
<table class="w-full min-w-[1000px]">
<thead class="bg-gray-50 border-b border-gray-100">
<tr>
<th class="px-4 py-3 text-left text-sm font-medium text-gray-500">GPU名称</th>
<th class="px-4 py-3 text-left text-sm font-medium text-gray-500">厂商</th>
<th class="px-4 py-3 text-left text-sm font-medium text-gray-500">架构</th>
<th class="px-4 py-3 text-left text-sm font-medium text-gray-500">显存</th>
<th class="px-4 py-3 text-left text-sm font-medium text-gray-500">CUDA核心</th>
<th class="px-4 py-3 text-left text-sm font-medium text-gray-500">FP16算力</th>
<th class="px-4 py-3 text-left text-sm font-medium text-gray-500">功耗</th>
<th class="px-4 py-3 text-left text-sm font-medium text-gray-500">价格</th>
<th class="px-4 py-3 text-left text-sm font-medium text-gray-500">操作</th>
</tr>
</thead>
<tbody id="gpuTable">
<tr><td colspan="9" class="px-6 py-8 text-center text-gray-500">加载中...</td></tr>
</tbody>
</table>
</div>
</main>
</div>
<!-- 添加GPU弹窗 -->
<div id="addModal" class="fixed inset-0 bg-black/50 hidden items-center justify-center z-50 p-4">
<div class="bg-white rounded-xl w-full max-w-2xl max-h-[90vh] overflow-hidden flex flex-col">
<div class="p-4 border-b flex justify-between items-center">
<h3 class="font-bold text-lg">添加新GPU</h3>
<button onclick="closeAddModal()" class="text-gray-400"><i class="ri-close-line text-2xl"></i></button>
</div>
<div class="p-4 overflow-auto flex-1">
<form id="addForm" class="grid grid-cols-2 gap-4">
<div>
<label class="block text-sm text-gray-600 mb-1">GPU名称 *</label>
<input type="text" name="name" required class="w-full px-3 py-2 border rounded-lg">
</div>
<div>
<label class="block text-sm text-gray-600 mb-1">厂商</label>
<select name="manufacturer" class="w-full px-3 py-2 border rounded-lg">
<option value="NVIDIA">NVIDIA</option>
<option value="AMD">AMD</option>
</select>
</div>
<div>
<label class="block text-sm text-gray-600 mb-1">架构</label>
<input type="text" name="architecture" class="w-full px-3 py-2 border rounded-lg">
</div>
<div>
<label class="block text-sm text-gray-600 mb-1">显存(GB)</label>
<input type="number" name="memoryGb" class="w-full px-3 py-2 border rounded-lg">
</div>
<div>
<label class="block text-sm text-gray-600 mb-1">显存类型</label>
<input type="text" name="memoryType" placeholder="如 HBM3, GDDR6X" class="w-full px-3 py-2 border rounded-lg">
</div>
<div>
<label class="block text-sm text-gray-600 mb-1">CUDA核心</label>
<input type="number" name="cudaCores" class="w-full px-3 py-2 border rounded-lg">
</div>
<div>
<label class="block text-sm text-gray-600 mb-1">Tensor核心</label>
<input type="number" name="tensorCores" class="w-full px-3 py-2 border rounded-lg">
</div>
<div>
<label class="block text-sm text-gray-600 mb-1">显存带宽(GB/s)</label>
<input type="number" name="memoryBandwidthGbps" class="w-full px-3 py-2 border rounded-lg">
</div>
<div>
<label class="block text-sm text-gray-600 mb-1">FP32算力(TFLOPS)</label>
<input type="number" step="0.1" name="fp32Tflops" class="w-full px-3 py-2 border rounded-lg">
</div>
<div>
<label class="block text-sm text-gray-600 mb-1">FP16算力(TFLOPS)</label>
<input type="number" step="0.1" name="fp16Tflops" class="w-full px-3 py-2 border rounded-lg">
</div>
<div>
<label class="block text-sm text-gray-600 mb-1">功耗(W)</label>
<input type="number" name="tdpWatts" class="w-full px-3 py-2 border rounded-lg">
</div>
<div>
<label class="block text-sm text-gray-600 mb-1">价格(USD)</label>
<input type="number" name="priceUsd" class="w-full px-3 py-2 border rounded-lg">
</div>
</form>
</div>
<div class="p-4 border-t flex justify-end gap-3">
<button onclick="closeAddModal()" class="px-4 py-2 border rounded-lg">取消</button>
<button onclick="submitGpu()" class="px-4 py-2 bg-green-500 text-white rounded-lg">添加</button>
</div>
</div>
</div>
<script>
let allGpus = [];
async function loadGpus() {
const res = await fetch('/api/gpus');
allGpus = await res.json();
renderGpus(allGpus);
}
function renderGpus(gpus) {
const tbody = document.getElementById('gpuTable');
if (gpus.length === 0) {
tbody.innerHTML = '<tr><td colspan="9" class="px-6 py-8 text-center text-gray-500">暂无GPU数据</td></tr>';
return;
}
tbody.innerHTML = gpus.map(g => `
<tr class="border-b border-gray-50 hover:bg-gray-50">
<td class="px-4 py-3 font-medium text-gray-800">${g.name}</td>
<td class="px-4 py-3">
<span class="px-2 py-1 rounded text-xs ${g.manufacturer === 'NVIDIA' ? 'bg-green-100 text-green-600' : 'bg-red-100 text-red-600'}">
${g.manufacturer}
</span>
</td>
<td class="px-4 py-3 text-gray-600">${g.architecture || '-'}</td>
<td class="px-4 py-3">${g.memoryGb ? g.memoryGb + 'GB ' + (g.memoryType || '') : '-'}</td>
<td class="px-4 py-3">${g.cudaCores ? g.cudaCores.toLocaleString() : '-'}</td>
<td class="px-4 py-3 text-orange-600">${g.fp16Tflops ? g.fp16Tflops.toLocaleString() + ' TF' : '-'}</td>
<td class="px-4 py-3">${g.tdpWatts ? g.tdpWatts + 'W' : '-'}</td>
<td class="px-4 py-3 text-blue-600">${g.priceUsd ? '$' + g.priceUsd.toLocaleString() : '-'}</td>
<td class="px-4 py-3">
<button class="text-blue-500 hover:text-blue-700 mr-2"><i class="ri-edit-line"></i></button>
<button class="text-red-500 hover:text-red-700"><i class="ri-delete-bin-line"></i></button>
</td>
</tr>
`).join('');
}
function filterGpus() {
const search = document.getElementById('searchInput').value.toLowerCase();
const manufacturer = document.getElementById('filterManufacturer').value;
let filtered = allGpus;
if (search) {
filtered = filtered.filter(g => g.name.toLowerCase().includes(search));
}
if (manufacturer !== 'all') {
filtered = filtered.filter(g => g.manufacturer === manufacturer);
}
renderGpus(filtered);
}
function showAddModal() {
document.getElementById('addModal').classList.remove('hidden');
document.getElementById('addModal').classList.add('flex');
}
function closeAddModal() {
document.getElementById('addModal').classList.add('hidden');
document.getElementById('addModal').classList.remove('flex');
}
async function submitGpu() {
const form = document.getElementById('addForm');
const formData = new FormData(form);
const data = {};
formData.forEach((value, key) => {
if (value) {
if (!isNaN(value) && value !== '') {
data[key] = parseFloat(value);
} else {
data[key] = value;
}
}
});
try {
const res = await fetch('/api/gpus', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
const result = await res.json();
if (result.success) {
closeAddModal();
loadGpus();
alert('GPU添加成功');
}
} catch (err) {
alert('添加失败,请重试');
}
}
loadGpus();
</script>
</body>
</html>