Files
YG_FT_Platform/web/pages/model-manage.html

430 lines
20 KiB
HTML

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>模型管理 / 远光软件微调平台</title>
<script src="../lib/tailwindcss/tailwind.js"></script>
<link href="../lib/font-awesome/css/font-awesome.min.css" rel="stylesheet">
<style>
.sidebar-section-title {
padding: 0.5rem 1rem;
font-size: 0.75rem;
color: rgba(191, 203, 217, 0.7);
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.nav-link:hover {
background-color: rgba(0, 21, 41, 0.2);
}
.sidebar-item-active {
background-color: rgba(24, 144, 255, 0.1);
color: #1890ff;
border-left: 4px solid #1890ff;
}
.bg-primary { background-color: #1890ff; }
.text-primary { color: #1890ff; }
.border-primary { border-color: #1890ff; }
:root { --primary: #1890ff; }
.form-input {
display: block;
width: 100%;
padding: 0.5rem 0.75rem;
font-size: 0.875rem;
line-height: 1.25rem;
color: #1f2937;
background-color: #fff;
border: 1px solid #d1d5db;
border-radius: 0.375rem;
transition: border-color 0.15s ease-in-out;
}
.form-input:focus {
outline: none;
border-color: #1890ff;
box-shadow: 0 0 0 3px rgba(24, 144, 255, 0.1);
}
.form-label {
display: block;
font-size: 0.875rem;
font-weight: 500;
color: #374151;
margin-bottom: 0.25rem;
}
.tab-btn {
padding: 8px 20px !important;
font-size: 14px !important;
font-weight: 500 !important;
border-radius: 6px !important;
border: 1px solid transparent !important;
cursor: pointer !important;
background: transparent !important;
color: #4b5563 !important;
transition: all 0.2s !important;
min-width: 100px;
}
.tab-btn:hover {
background: white !important;
border-color: #d1d5db !important;
}
.tab-btn.tab-active {
background: white !important;
color: #1890ff !important;
border-color: #1890ff !important;
box-shadow: 0 1px 3px rgba(24,144,255,0.2) !important;
}
</style>
</head>
<body class="antialiased bg-gray-50 flex h-screen overflow-hidden">
<!-- 侧边导航 -->
<aside class="w-64 text-[#bfcbd9] flex-shrink-0 hidden md:block flex flex-col h-full" style="background-color: #001529;">
<!-- 平台LOGO区域 -->
<div class="pt-5 pb-3 border-b border-[#001529]/30 flex items-center justify-center pl-2">
<img src="../assets/logo/logo.png" alt="Logo" class="w-8 h-8 object-contain mr-2">
<span class="text-white font-medium text-base">远光软件微调平台</span>
</div>
<!-- 导航主区域 -->
<nav class="flex-1 overflow-y-auto py-2 relative">
<!-- 第一分区:模型服务 -->
<div class="sidebar-section-title">模型服务</div>
<div class="nav-item-wrapper">
<a href="#" data-page="fine-tune" class="nav-link flex items-center px-4 py-2.5 hover:bg-[#001529]/20 transition-colors">
<i class="fa fa-cogs w-5 text-center"></i>
<span class="ml-2">模型调优</span>
</a>
</div>
<div class="nav-item-wrapper">
<a href="#" data-page="my-models" class="nav-link flex items-center px-4 py-2.5 hover:bg-[#001529]/20 transition-colors">
<i class="fa fa-database w-5 text-center"></i>
<span class="ml-2">我的模型</span>
</a>
</div>
<div class="nav-item-wrapper">
<a href="#" data-page="model-eval" class="nav-link flex items-center px-4 py-2.5 hover:bg-[#001529]/20 transition-colors">
<i class="fa fa-line-chart w-5 text-center"></i>
<span class="ml-2">模型评测</span>
</a>
</div>
<div class="nav-item-wrapper">
<a href="#" data-page="model-compare" class="nav-link flex items-center px-4 py-2.5 hover:bg-[#001529]/20 transition-colors">
<i class="fa fa-server w-5 text-center"></i>
<span class="ml-2">模型对比</span>
</a>
</div>
<!-- 第二分区:资源管理 -->
<div class="sidebar-section-title mt-6">资源管理</div>
<div class="nav-item-wrapper">
<a href="#" data-page="model-manage" class="nav-link sidebar-item-active flex items-center px-4 py-2.5 hover:bg-[#001529]/20 transition-colors">
<i class="fa fa-cube w-5 text-center"></i>
<span class="ml-2">模型管理</span>
</a>
</div>
<div class="nav-item-wrapper">
<a href="#" data-page="dataset-manage" class="nav-link flex items-center px-4 py-2.5 hover:bg-[#001529]/20 transition-colors">
<i class="fa fa-file-text w-5 text-center"></i>
<span class="ml-2">数据集管理</span>
</a>
</div>
<div class="nav-item-wrapper">
<a href="#" data-page="data-generate" class="nav-link flex items-center px-4 py-2.5 hover:bg-[#001529]/20 transition-colors">
<i class="fa fa-database w-5 text-center"></i>
<span class="ml-2">其他工具</span>
</a>
</div>
<!-- 第三分区:系统设置 -->
<div class="sidebar-section-title mt-6">系统设置</div>
<div class="nav-item-wrapper">
<a href="#" data-page="config" class="nav-link flex items-center px-4 py-2.5 hover:bg-[#001529]/20 transition-colors">
<i class="fa fa-bar-chart w-5 text-center"></i>
<span class="ml-2">平台性能</span>
</a>
</div>
<div class="nav-item-wrapper">
<a href="#" data-page="logs" class="nav-link flex items-center px-4 py-2.5 hover:bg-[#001529]/20 transition-colors">
<i class="fa fa-file-text w-5 text-center"></i>
<span class="ml-2">查看日志</span>
</a>
</div>
</nav>
<!-- 底部信息区域 -->
<div class="p-4 border-t border-[#001529]/30 text-xs mt-auto">
<div class="mb-2 text-[#bfcbd9]/80">默认业务空间</div>
<div class="flex items-center justify-between">
<span class="text-[#bfcbd9]">版本 v1.0.0</span>
<i class="fa fa-question-circle-o text-[#bfcbd9]/70"></i>
</div>
</div>
</aside>
<!-- 主内容区 -->
<div class="flex-1 flex flex-col overflow-hidden">
<!-- 顶部导航 -->
<header class="bg-white border-b border-gray-200 shadow-sm">
<div class="flex items-center justify-between px-6 h-14">
<div class="flex items-center space-x-6">
<button class="md:hidden text-gray-500 hover:text-gray-700">
<i class="fa fa-bars"></i>
</button>
</div>
<div class="flex items-center space-x-4">
<!-- 系统性能监控 -->
<a href="?page=config" class="flex items-center space-x-4 text-xs text-gray-500 hover:text-primary transition-colors">
<div class="flex items-center" title="CPU使用率">
<i class="fa fa-microchip mr-1 text-blue-500"></i>
<span id="cpuUsage">--</span>%
</div>
<div class="flex items-center" title="内存使用率">
<i class="fa fa-database mr-1 text-green-500"></i>
<span id="memUsage">--</span>%
</div>
<div class="flex items-center" title="磁盘使用率">
<i class="fa fa-hdd-o mr-1 text-orange-500"></i>
<span id="diskUsage">--</span>%
</div>
</a>
<div class="h-6 w-px bg-gray-200"></div>
<div class="relative group">
<img src="https://picsum.photos/id/1005/32/32" class="w-8 h-8 rounded-full cursor-pointer" alt="用户头像">
<div class="absolute right-0 top-full pt-2 hidden group-hover:block z-50">
<div class="bg-white rounded shadow-lg py-1 border border-gray-100 min-w-[140px]">
<a href="login.html" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 whitespace-nowrap">
<i class="fa fa-sign-out mr-1"></i>退出登录
</a>
</div>
</div>
</div>
</div>
</div>
</header>
<!-- 页面内容 -->
<main class="flex-1 overflow-y-auto">
<!-- Tab 导航 -->
<div style="background: #fff; border-bottom: 1px solid #e5e7eb; padding: 16px;">
<h2 style="font-size: 18px; font-weight: 600; color: #1f2937; margin-bottom: 12px;">模型管理</h2>
<div style="display: flex; gap: 8px; background: #f3f4f6; padding: 6px; border-radius: 8px;">
<button onclick="switchTab('all')" id="tab-all" class="tab-btn tab-active" style="display: inline-flex; align-items: center; justify-content: center;">
全部模型
</button>
<button onclick="switchTab('training')" id="tab-training" class="tab-btn" style="display: inline-flex; align-items: center; justify-content: center;">
训练基座
</button>
<button onclick="switchTab('inference')" id="tab-inference" class="tab-btn" style="display: inline-flex; align-items: center; justify-content: center;">
推理对比
</button>
<button onclick="switchTab('evaluation')" id="tab-evaluation" class="tab-btn" style="display: inline-flex; align-items: center; justify-content: center;">
评测模型
</button>
</div>
</div>
<!-- 表格容器 -->
<div style="padding: 16px;">
<!-- 工具栏 -->
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px;">
<div style="display: flex; align-items: center; gap: 16px;">
<input type="text" id="searchInput" placeholder="搜索模型名称..." style="width: 256px; padding: 8px 12px; border: 1px solid #d1d5db; border-radius: 6px; font-size: 14px;" oninput="filterModels()">
</div>
<button onclick="window.location.href='model-manage-create.html'" style="padding: 8px 16px; background: #1890ff; color: white; border: none; border-radius: 6px; cursor: pointer; display: flex; align-items: center; font-size: 14px;">
<span style="margin-right: 8px;">+</span>添加模型
</button>
</div>
<!-- 模型表格 -->
<div class="bg-white rounded-lg shadow-sm">
<div class="overflow-x-auto">
<table class="w-full">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">模型名称</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">模型类型</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">用途</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">模型来源</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">描述</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">创建时间</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">操作</th>
</tr>
</thead>
<tbody id="modelsBody" class="bg-white divide-y divide-gray-200">
<!-- 动态加载 -->
</tbody>
</table>
</div>
<!-- 空状态 -->
<div id="emptyState" class="hidden px-6 py-12 text-center">
<i class="fa fa-inbox text-4xl text-gray-300 mb-3"></i>
<p class="text-gray-500">暂无模型数据</p>
</div>
</div>
</div>
<script>
// API 基础地址 - 使用 config.yaml 中的 app.port (7861)
const getApiBase = () => {
const protocol = window.location.protocol;
const hostname = window.location.hostname;
return `${protocol}//${hostname}:7861/api`;
};
const API_BASE = getApiBase();
let allModels = [];
let currentTab = 'all';
// Tab 切换
function switchTab(tab) {
currentTab = tab;
// 更新按钮样式
document.querySelectorAll('.tab-btn').forEach(btn => {
btn.classList.remove('tab-active');
});
const activeTab = document.getElementById(`tab-${tab}`);
activeTab.classList.add('tab-active');
renderModels();
}
// 加载模型数据
async function loadModels() {
try {
const response = await fetch(`${API_BASE}/model-manage`);
const result = await response.json();
if (result.code === 0) {
allModels = result.data || [];
renderModels();
}
} catch (error) {
console.error('加载模型失败:', error);
}
}
// 筛选模型
function filterModels() {
renderModels();
}
// 渲染模型列表
function renderModels() {
const searchValue = document.getElementById('searchInput').value.toLowerCase();
let filteredModels = allModels;
// 按 Tab 筛选
if (currentTab !== 'all') {
filteredModels = filteredModels.filter(m => m.purpose === currentTab);
}
// 按搜索关键词筛选
if (searchValue) {
filteredModels = filteredModels.filter(m =>
m.name?.toLowerCase().includes(searchValue) ||
m.description?.toLowerCase().includes(searchValue)
);
}
const tbody = document.getElementById('modelsBody');
const emptyState = document.getElementById('emptyState');
if (filteredModels.length === 0) {
tbody.innerHTML = '';
emptyState.classList.remove('hidden');
return;
}
emptyState.classList.add('hidden');
tbody.innerHTML = filteredModels.map(item => {
// 模型类型
const typeMap = {
'LLM': '大语言模型',
'CV': '计算机视觉',
'NLP': '自然语言处理',
'Embedding': '向量模型',
'Other': '其他'
};
const typeDisplay = typeMap[item.type] || item.type || '-';
// 用途
const purposeMap = {
'training': { text: '训练', class: 'bg-blue-100 text-blue-700' },
'inference': { text: '推理', class: 'bg-green-100 text-green-700' },
'evaluation': { text: '评测', class: 'bg-purple-100 text-purple-700' }
};
const purposeDisplay = purposeMap[item.purpose] || purposeMap['inference'];
// 模型来源
const sourceMap = {
'local': '本地模型',
'api': '在线模型',
'online': '在线模型'
};
const sourceDisplay = sourceMap[item.model_source] || item.model_source || '-';
return `
<tr class="hover:bg-gray-50">
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm font-medium text-gray-900">${item.name || '-'}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="px-2 py-1 text-xs font-medium rounded bg-blue-100 text-blue-700">${typeDisplay}</span>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="px-2 py-1 text-xs font-medium rounded ${purposeDisplay.class}">${purposeDisplay.text}</span>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="px-2 py-1 text-xs font-medium rounded bg-gray-100 text-gray-700">${sourceDisplay}</span>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm text-gray-500 max-w-xs truncate">${item.description || '-'}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
${item.create_time ? new Date(item.create_time).toLocaleString('zh-CN') : '-'}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
<button onclick="editModel(${item.id})" class="text-primary hover:text-primary/80 mr-3">
<i class="fa fa-edit"></i> 编辑
</button>
<button onclick="deleteModel(${item.id})" class="text-red-500 hover:text-red-600">
<i class="fa fa-trash"></i> 删除
</button>
</td>
</tr>
`;
}).join('');
}
// 编辑模型
function editModel(id) {
window.location.href = `model-manage-create.html?id=${id}`;
}
// 删除模型
async function deleteModel(id) {
if (!confirm('确定要删除此模型吗?')) return;
try {
const response = await fetch(`${API_BASE}/model-manage/${id}`, {
method: 'DELETE'
});
const result = await response.json();
if (result.code === 0) {
loadModels();
} else {
alert('删除失败: ' + result.message);
}
} catch (error) {
console.error('删除模型失败:', error);
alert('删除失败');
}
}
// 页面加载时初始化
loadModels();
</script>
</body>
</html>