294 lines
17 KiB
HTML
294 lines
17 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>
|
|
:root { --primary: #1890ff; --danger: #f5222d; --success: #52c41a; }
|
|
.bg-primary { background-color: var(--primary); }
|
|
.text-primary { color: var(--primary); }
|
|
.border-primary { border-color: var(--primary); }
|
|
.hover\:bg-primary\/90:hover { background-color: rgba(24, 144, 255, 0.9); }
|
|
.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; }
|
|
.sidebar-item-active { background-color: rgba(24, 144, 255, 0.1); color: #1890ff; border-left: 4px solid #1890ff; }
|
|
</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;">
|
|
<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><a href="main.html?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><a href="main.html?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><a href="main.html?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><a href="main.html?page=model-manage" class="nav-link 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><a href="main.html?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><a href="tools.html" class="nav-link sidebar-item-active flex items-center px-4 py-2.5"><i class="fa fa-wrench w-5 text-center"></i><span class="ml-2">其他工具</span></a></div>
|
|
<div class="sidebar-section-title mt-6">系统设置</div>
|
|
<div><a href="hardware.html" 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><a href="logs.html" 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-4">
|
|
<a href="main.html" class="text-gray-500 hover:text-gray-700 flex items-center">
|
|
<i class="fa fa-arrow-left"></i>
|
|
<span class="ml-1">返回首页</span>
|
|
</a>
|
|
</div>
|
|
<div class="flex items-center space-x-4">
|
|
<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 mt-2 bg-white rounded shadow-lg py-1 hidden group-hover:block 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>
|
|
</header>
|
|
|
|
<main class="flex-1 overflow-y-auto p-6 bg-gray-50" id="page-content">
|
|
<!-- 工具卡片内容由JS渲染 -->
|
|
</main>
|
|
</div>
|
|
|
|
<!-- 消息弹窗 -->
|
|
<div id="customModal" class="fixed inset-0 bg-gray-900/50 hidden flex items-center justify-center z-50">
|
|
<div class="bg-white rounded-xl shadow-xl w-full max-w-md mx-4 overflow-hidden">
|
|
<div class="p-6 text-center">
|
|
<div id="modalIcon"></div>
|
|
<h3 id="modalTitle" class="text-lg font-medium text-gray-800 mb-2"></h3>
|
|
<p id="modalMessage" class="text-gray-500 text-sm mb-6"></p>
|
|
<div id="modalBtnGroup" class="flex justify-center space-x-3 hidden">
|
|
<button id="modalCancelBtn" class="px-6 py-2 bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200 transition-colors">取消</button>
|
|
<button id="modalConfirmBtn" class="px-6 py-2 bg-primary text-white rounded-lg hover:bg-primary/90 transition-colors">确定</button>
|
|
</div>
|
|
<div id="modalSingleBtnGroup" class="flex justify-center">
|
|
<button id="modalConfirmBtn2" class="px-6 py-2 bg-primary text-white rounded-lg hover:bg-primary/90 transition-colors">确定</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// API 配置
|
|
const API_BASE = (() => {
|
|
const protocol = window.location.protocol;
|
|
const hostname = window.location.hostname;
|
|
return `${protocol}//${hostname}:7861/api`;
|
|
})();
|
|
|
|
// 默认工具配置
|
|
const defaultTools = [
|
|
{ id: 'data-generate', name: '数据生成', icon: 'fa-database', description: '基于LLM生成微调数据集' },
|
|
{ id: 'json2jsonl', name: 'JSON转JSONL', icon: 'fa-code', description: '将JSON文件转换为JSONL格式' },
|
|
{ id: 'md-convert', name: '转换Markdown', icon: 'fa-file-text', description: '将Markdown文件转换为训练数据' }
|
|
];
|
|
|
|
// 获取自定义工具
|
|
function getCustomTools() {
|
|
const saved = localStorage.getItem('customTools');
|
|
return saved ? JSON.parse(saved) : [];
|
|
}
|
|
|
|
// 保存自定义工具
|
|
function saveCustomTools(tools) {
|
|
localStorage.setItem('customTools', JSON.stringify(tools));
|
|
}
|
|
|
|
// 渲染单个工具卡片
|
|
function renderToolCard(tool, canDelete = false, isCustom = false) {
|
|
return `
|
|
<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>
|
|
`;
|
|
}
|
|
|
|
// 渲染页面
|
|
function renderPage() {
|
|
const customTools = getCustomTools();
|
|
const defaultCards = defaultTools.map(t => renderToolCard(t, false, false)).join('');
|
|
const customCards = customTools.map(t => renderToolCard(t, true, true)).join('');
|
|
|
|
document.getElementById('page-content').innerHTML = `
|
|
<div class="bg-white rounded-lg shadow-sm">
|
|
<div class="flex items-center justify-between p-4 border-b border-gray-100">
|
|
<h2 class="text-lg font-medium">其他工具</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) {
|
|
showConfirm('确认删除', '确定要删除这个自定义工具吗?', () => {
|
|
const tools = getCustomTools().filter(t => t.id !== toolId);
|
|
saveCustomTools(tools);
|
|
renderPage();
|
|
});
|
|
}
|
|
|
|
// 修改自定义工具
|
|
function editCustomTool(toolId) {
|
|
const tool = getCustomTools().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') {
|
|
showMessage('提示', 'JSON转JSONL 工具开发中...', 'info');
|
|
} else if (toolId === 'md-convert') {
|
|
showMessage('提示', '转换Markdown 工具开发中...', 'info');
|
|
} else {
|
|
showMessage('提示', `${toolId} 功能开发中...`, 'info');
|
|
}
|
|
}
|
|
|
|
// ============ 消息弹窗 ============
|
|
function showMessage(title, message, type = 'info', onConfirm) {
|
|
const modal = document.getElementById('customModal');
|
|
const modalTitle = document.getElementById('modalTitle');
|
|
const modalMessage = document.getElementById('modalMessage');
|
|
const modalIcon = document.getElementById('modalIcon');
|
|
const modalConfirmBtn = document.getElementById('modalConfirmBtn2');
|
|
const modalSingleBtnGroup = document.getElementById('modalSingleBtnGroup');
|
|
|
|
modalTitle.textContent = title;
|
|
modalMessage.innerHTML = message;
|
|
|
|
if (type === 'success') {
|
|
modalIcon.innerHTML = '<div class="w-12 h-12 mx-auto mb-4 rounded-full bg-green-100 flex items-center justify-center"><i class="fa fa-check text-xl text-green-600"></i></div>';
|
|
} else if (type === 'error') {
|
|
modalIcon.innerHTML = '<div class="w-12 h-12 mx-auto mb-4 rounded-full bg-red-100 flex items-center justify-center"><i class="fa fa-times text-xl text-red-600"></i></div>';
|
|
} else if (type === 'warning') {
|
|
modalIcon.innerHTML = '<div class="w-12 h-12 mx-auto mb-4 rounded-full bg-yellow-100 flex items-center justify-center"><i class="fa fa-exclamation text-xl text-yellow-600"></i></div>';
|
|
} else {
|
|
modalIcon.innerHTML = '<div class="w-12 h-12 mx-auto mb-4 rounded-full bg-blue-100 flex items-center justify-center"><i class="fa fa-info text-xl text-blue-600"></i></div>';
|
|
}
|
|
|
|
modalSingleBtnGroup.classList.remove('hidden');
|
|
modalSingleBtnGroup.querySelector('#modalConfirmBtn2').className = type === 'error' ? 'px-6 py-2 bg-red-500 text-white rounded-lg hover:bg-red-600 transition-colors' : 'px-6 py-2 bg-primary text-white rounded-lg hover:bg-primary/90 transition-colors';
|
|
|
|
modal.classList.remove('hidden');
|
|
document.body.style.overflow = 'hidden';
|
|
|
|
modalSingleBtnGroup.onclick = function() {
|
|
closeModal();
|
|
if (typeof onConfirm === 'function') onConfirm();
|
|
};
|
|
}
|
|
|
|
function showConfirm(title, message, onConfirm, onCancel) {
|
|
const modal = document.getElementById('customModal');
|
|
const modalTitle = document.getElementById('modalTitle');
|
|
const modalMessage = document.getElementById('modalMessage');
|
|
const modalIcon = document.getElementById('modalIcon');
|
|
const modalConfirmBtn = document.getElementById('modalConfirmBtn');
|
|
const modalCancelBtn = document.getElementById('modalCancelBtn');
|
|
const modalBtnGroup = document.getElementById('modalBtnGroup');
|
|
const modalSingleBtnGroup = document.getElementById('modalSingleBtnGroup');
|
|
|
|
modalTitle.textContent = title;
|
|
modalMessage.innerHTML = message;
|
|
|
|
if (type === 'warning') {
|
|
modalIcon.innerHTML = '<div class="w-12 h-12 mx-auto mb-4 rounded-full bg-yellow-100 flex items-center justify-center"><i class="fa fa-exclamation text-xl text-yellow-600"></i></div>';
|
|
} else {
|
|
modalIcon.innerHTML = '<div class="w-12 h-12 mx-auto mb-4 rounded-full bg-blue-100 flex items-center justify-center"><i class="fa fa-question text-xl text-blue-600"></i></div>';
|
|
}
|
|
|
|
modalBtnGroup.classList.remove('hidden');
|
|
modalSingleBtnGroup.classList.add('hidden');
|
|
modalConfirmBtn.textContent = '确定';
|
|
modalConfirmBtn.className = 'px-6 py-2 bg-primary text-white rounded-lg hover:bg-primary/90 transition-colors';
|
|
|
|
modal.classList.remove('hidden');
|
|
document.body.style.overflow = 'hidden';
|
|
|
|
modalConfirmBtn.onclick = function() {
|
|
closeModal();
|
|
if (typeof onConfirm === 'function') onConfirm();
|
|
};
|
|
|
|
modalCancelBtn.onclick = function() {
|
|
closeModal();
|
|
if (typeof onCancel === 'function') onCancel();
|
|
};
|
|
}
|
|
|
|
function closeModal() {
|
|
const modal = document.getElementById('customModal');
|
|
modal.classList.add('hidden');
|
|
document.body.style.overflow = '';
|
|
}
|
|
|
|
// 页面加载完成后初始化
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
renderPage();
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|