/** * 表格组件 * 处理表格渲染、数据操作等 */ // 当前页面状态 window.currentPage = 'fine-tune'; window.currentParentPage = null; window.selectedItems = new Set(); // 存储选中的项ID window.currentPageData = []; // 存储当前页面数据 window.modelListCache = []; // 模型列表缓存 window.currentModelTab = 'config'; // 模型管理页面当前tab: 'config'=配置模型, 'trained'=训练模型 // 获取 API 数据 async function fetchData(url) { const response = await fetch(url); const result = await response.json(); if (result.code !== 0) { throw new Error(result.message || '获取数据失败'); } return result.data || []; } // 删除数据 async function deleteItem(api, id) { // 如果是我的模型,提示删除合并模型 const confirmMessage = api === 'model-manage/trained-models' ? '确定要删除合并模型吗?权重文件不会删除。' : '确定要删除这条记录吗?'; window.showConfirm('确认删除', confirmMessage, async () => { try { // 如果是我的模型,调用删除合并模型的API if (api === 'model-manage/trained-models') { const response = await fetch(`${window.API_BASE}/model-manage/trained-models/${id}?type=merged`, { method: 'DELETE' }); const result = await response.json(); if (result.code === 0) { window.showMessage('成功', '删除成功', 'success'); // 清除合并状态缓存 sessionStorage.removeItem('merge_status_' + id); // 刷新当前页面 clearSelection(); const activeLink = document.querySelector('.nav-link.sidebar-item-active'); if (activeLink && typeof window.loadPage === 'function') { window.loadPage(activeLink.dataset.page); } } else { window.showMessage('错误', result.message || '删除失败', 'error'); } } else { const response = await fetch(`${window.API_BASE}/${api}/${id}`, { method: 'DELETE' }); const result = await response.json(); if (result.code === 0) { // 刷新当前页面 clearSelection(); // 清除选中状态 const activeLink = document.querySelector('.nav-link.sidebar-item-active'); if (activeLink && typeof window.loadPage === 'function') { window.loadPage(activeLink.dataset.page); } } else { window.showMessage('错误', result.message || '删除失败', 'error'); } } } catch (error) { window.showMessage('错误', '删除失败: ' + error.message, 'error'); } }); } // 更新模型用途 async function updateModelPurpose(id, purpose) { try { const response = await fetch(`${window.API_BASE}/model-manage/${id}/purpose`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ purpose }) }); const result = await response.json(); if (result.code === 0) { // 刷新当前页面 const activeLink = document.querySelector('.nav-link.sidebar-item-active'); if (activeLink && typeof window.loadPage === 'function') { window.loadPage(activeLink.dataset.page); } } else { window.showMessage('错误', result.message || '更新失败', 'error'); } } catch (error) { window.showMessage('错误', '更新失败: ' + error.message, 'error'); } } // 切换单个项的选中状态 function toggleItemSelection(id, api) { if (window.selectedItems.has(id)) { window.selectedItems.delete(id); } else { window.selectedItems.add(id); } // 重新渲染当前页面以更新UI refreshCurrentPage(); } // 切换全选/取消全选 function toggleSelectAll(checkbox, api) { // 使用保存的当前页面数据 if (checkbox.checked) { // 全选当前页面的所有数据(支持 name 或 id) window.currentPageData.forEach(item => window.selectedItems.add(item.name || item.id)); } else { // 取消全选,移除当前页面所有数据的选中状态 window.currentPageData.forEach(item => window.selectedItems.delete(item.name || item.id)); } refreshCurrentPage(); } // 清除所有选中项 function clearSelection() { window.selectedItems.clear(); refreshCurrentPage(); } // 批量删除选中的项 function batchDeleteItems(api) { if (window.selectedItems.size === 0) { window.showMessage('提示', '请先选择要删除的项', 'warning'); return; } window.showConfirm('批量删除', `确定要删除选中的 ${window.selectedItems.size} 条记录吗?`, async () => { const ids = Array.from(window.selectedItems); let successCount = 0; let failCount = 0; for (const id of ids) { try { const response = await fetch(`${window.API_BASE}/${api}/${id}`, { method: 'DELETE' }); const result = await response.json(); if (result.code === 0) { successCount++; } else { failCount++; } } catch (error) { failCount++; } } clearSelection(); // 刷新当前页面 const activeLink = document.querySelector('.nav-link.sidebar-item-active'); if (activeLink && typeof window.loadPage === 'function') { window.loadPage(activeLink.dataset.page); } if (failCount === 0) { window.showMessage('成功', `成功删除 ${successCount} 条记录`, 'success'); } else { window.showMessage('部分失败', `成功删除 ${successCount} 条,${failCount} 条删除失败`, 'warning'); } }); } // 刷新当前页面(重新渲染) function refreshCurrentPage() { const activeLink = document.querySelector('.nav-link.sidebar-item-active'); if (activeLink) { const pageName = activeLink.dataset.page; const config = window.tableConfigs[pageName]; if (config && (config.hasModelTabs || config.api === 'model-manage' || config.api === 'dataset-manage')) { const container = document.getElementById('page-content'); if (container && typeof renderTablePage === 'function') { container.innerHTML = renderTablePage(config, window.currentPageData); // 恢复复选框状态 updateCheckboxStates(); } } } } // 更新复选框状态(保持选中状态) function updateCheckboxStates() { const checkboxes = document.querySelectorAll('tbody input[type="checkbox"]'); checkboxes.forEach(cb => { const match = cb.getAttribute('onchange')?.match(/toggleItemSelection\((\d+)/) || cb.getAttribute('onchange')?.match(/toggleItemSelection\(([^,]+)/); const id = match ? parseInt(match[1]) || match[1] : null; if (id !== null && window.selectedItems.has(id)) { cb.checked = true; cb.closest('tr')?.classList.add('bg-blue-50'); } else { cb.checked = false; cb.closest('tr')?.classList.remove('bg-blue-50'); } }); // 更新批量操作栏的显示状态 const batchActions = document.getElementById('batchActions'); if (batchActions) { if (window.selectedItems.size > 0) { batchActions.classList.remove('hidden'); batchActions.querySelector('strong').textContent = window.selectedItems.size; } else { batchActions.classList.add('hidden'); } } // 更新批量删除按钮 const batchDeleteBtn = document.querySelector('#batchActions button[onclick^="batchDeleteItems"]'); if (batchDeleteBtn) { if (window.selectedItems.size > 0) { batchDeleteBtn.innerHTML = `批量删除 (${window.selectedItems.size})`; } else { batchDeleteBtn.innerHTML = `批量删除 (0)`; } } } // 编辑数据集 function editItem(api, id) { if (api === 'dataset-manage') { // 跳转到数据集创建页面进行编辑 window.location.href = `dataset-create.html?id=${id}`; } else if (api === 'model-manage') { // 跳转到模型创建页面进行编辑 window.location.href = `model-manage-create.html?id=${id}`; } else { window.showMessage('提示', '编辑功能开发中...', 'info'); } } // 下载数据集(打包下载) function downloadDataset(datasetId) { const protocol = window.location.protocol; const hostname = window.location.hostname; window.open(`${protocol}//${hostname}:7861/api/dataset-manage/download/${datasetId}`, '_blank'); } // 开始模型对比 async function startCompare(id) { // 跳转到模型对比聊天页面(通过主框架加载) window.location.href = `main.html?page=model-compare-chat&id=${id}`; } // 筛选表格 function filterTable() { const searchInput = document.getElementById('tableSearchInput'); if (!searchInput) return; const keyword = searchInput.value.toLowerCase().trim(); const tbody = document.querySelector('#page-content table tbody'); if (!tbody) return; const rows = tbody.querySelectorAll('tr'); rows.forEach(row => { const text = row.querySelector('td')?.textContent?.toLowerCase() || ''; if (keyword === '' || text.includes(keyword)) { row.style.display = ''; } else { row.style.display = 'none'; } }); } // 刷新表格数据 - 重新加载当前页面 window.loadTableData = function() { const activeLink = document.querySelector('.nav-link.sidebar-item-active'); if (activeLink && typeof window.loadPage === 'function') { window.loadPage(activeLink.dataset.page); } }; // 合并模型权重(保留兼容) window.viewTrainedModel = function(name, method, path) { if (typeof window.startMerge === 'function') { window.startMerge(name, method, path); } }; // 编辑模型 window.editModel = function(modelId) { window.location.href = `model-manage-create.html?id=${modelId}`; }; // 预览数据集 window.previewDataset = function(datasetId) { window.location.href = `dataset-preview.html?id=${datasetId}`; }; // 下载数据集 window.downloadDataset = function(datasetId) { window.open(`${window.API_BASE}/dataset-manage/download/${datasetId}`, '_blank'); }; // 导出模型权重 function exportModel(modelName) { window.open(`${window.API_BASE}/model-manage/trained-models/${encodeURIComponent(modelName)}/export`, '_blank'); } // 删除已训练模型的权重 async function deleteTrainedWeight(modelName) { window.showConfirm('确认删除', `确定要删除模型 "${modelName}" 的权重文件吗?合并模型不受影响。`, async () => { try { const response = await fetch(`${window.API_BASE}/model-manage/trained-models/${encodeURIComponent(modelName)}?type=lora`, { method: 'DELETE' }); const result = await response.json(); if (result.code === 0) { // 只显示成功消息,不刷新表格(因为后端可能因为没有权重文件而不返回这条记录) window.showMessage('成功', '权重已删除', 'success'); // 清除该模型的合并状态缓存,让前端重新从后端获取状态 sessionStorage.removeItem('merge_status_' + modelName); sessionStorage.removeItem('merge_status_' + modelName + '_time'); } else { window.showMessage('错误', result.message || '删除失败', 'error'); } } catch (error) { console.error('删除权重失败:', error); window.showMessage('错误', '删除失败: ' + error.message, 'error'); } }); } // 切换模型管理tab function switchModelTab(tab) { window.currentModelTab = tab; // 清除选中状态 clearSelection(); // 重新加载模型管理页面 window.loadPage('model-manage'); } // ========== 渲染函数 ========== // 渲染表格页面 function renderTablePage(config, data) { // 如果是模型管理页面,根据tab动态决定列和配置 let columns = config.columns || []; let createButton = ''; let supportsMultiSelect = false; let currentApi = config.api; if (config.hasModelTabs) { if (window.currentModelTab === 'config') { // 配置模型tab columns = [ { title: '模型名称', key: 'name' }, { title: '模型类型', key: 'type', render: (val) => { const textMap = { 'LLM': '大语言模型', 'CV': '计算机视觉', 'NLP': '自然语言处理', 'Embedding': '向量模型', 'Other': '其他' }; const displayText = textMap[val] || val || '-'; return '' + displayText + ''; }}, { title: '用途', key: 'purpose', render: (val) => { 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 display = purposeMap[val] || { text: val || '-', class: 'bg-gray-100 text-gray-700' }; return '' + display.text + ''; }}, { title: '模型来源', key: 'model_source', render: (val) => { const textMap = { 'local': '本地模型', 'api': '在线模型', 'online': '在线模型' }; const displayText = textMap[val] || val || '-'; return '' + displayText + ''; }}, { title: '描述', key: 'description', render: (val) => val || '-' }, { title: '创建时间', key: 'create_time', render: (val) => val ? new Date(val).toLocaleString('zh-CN') : '-' } ]; createButton = ` `; supportsMultiSelect = true; currentApi = 'model-manage'; } else { // 训练模型tab columns = [ { title: '模型名称', key: 'name' }, { title: '训练方法', key: 'train_methods', render: (val) => val && val[0] ? val[0].name : '-' }, { title: '基座模型', key: 'base_model_path', render: (val) => `${val || '-'}` }, { title: '创建时间', key: 'create_time', render: (val) => val ? new Date(val).toLocaleString('zh-CN') : '-' } ]; supportsMultiSelect = true; currentApi = 'model-manage/trained-models'; } } else { // 非模型管理页面,使用原始配置 columns = config.columns || columns; createButton = config.api === 'dataset-manage' ? ` ` : (config.api === 'fine-tune' ? ` ` : (config.api === 'model-compare' ? ` ` : '')); supportsMultiSelect = config.api === 'model-manage' || config.api === 'model-manage/trained-models' || config.api === 'dataset-manage' || config.api === 'fine-tune'; } // 搜索框 const searchBox = (config.api === 'model-manage' || config.api === 'model-manage/trained-models' || config.api === 'dataset-manage' || config.api === 'fine-tune' || config.hasModelTabs) ? `
| ${col.title} | `).join('')}操作 | |
|---|---|---|
| ` : ''} ${columns.map(col => ` | ${col.render ? col.render(item[col.key], item) : (item[col.key] || '-')} | `).join('')}
${config.api === 'fine-tune' ? `
` : (currentApi === 'model-manage/trained-models' ? `
${getMergeButtonHtml(item.name, item.train_methods?.[0]?.name || 'lora', item.base_model_path || '', item.merged, item.merging)}
${item.merged ? `
` : ''}
${(item.merged && !item.merging) ? `
` : ''}
` : (currentApi === 'model-manage' ? `
` : (config.api === 'dataset-manage' ? `
` : (config.api === 'model-compare' ? `
${getCompareButtonHtml(item.id, item.status)}
` : ''))))}
|
暂无数据