1. 我的模型页面修改
This commit is contained in:
@@ -316,14 +316,46 @@
|
||||
// 加载模型列表
|
||||
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();
|
||||
} else {
|
||||
showMessage('错误', result.message || '加载模型失败', 'error');
|
||||
// 并行加载数据库模型和已训练模型
|
||||
const [dbResponse, trainedResponse] = await Promise.all([
|
||||
fetch(`${API_BASE}/model-manage`),
|
||||
fetch(`${API_BASE}/model-manage/trained-models`)
|
||||
]);
|
||||
|
||||
const dbResult = await dbResponse.json();
|
||||
const trainedResult = await trainedResponse.json();
|
||||
|
||||
let dbModels = [];
|
||||
let trainedModels = [];
|
||||
|
||||
// 数据库模型
|
||||
if (dbResult.code === 0) {
|
||||
dbModels = dbResult.data || [];
|
||||
}
|
||||
|
||||
// 已训练模型
|
||||
if (trainedResult.code === 0) {
|
||||
const trainedData = trainedResult.data?.models || [];
|
||||
trainedData.forEach(model => {
|
||||
if (model.train_methods && model.train_methods.length > 0) {
|
||||
model.train_methods.forEach(method => {
|
||||
trainedModels.push({
|
||||
id: `trained_${model.name}_${method.name}`.replace(/[^a-zA-Z0-9]/g, '_'),
|
||||
name: `${model.name} (${method.name})`,
|
||||
type: 'LLM',
|
||||
source: 'trained',
|
||||
model_path: model.path,
|
||||
train_method: method.name
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 合并所有模型
|
||||
allModels = [...dbModels, ...trainedModels];
|
||||
console.log('[DEBUG] 数据库模型:', dbModels.length, '已训练模型:', trainedModels.length);
|
||||
renderModels();
|
||||
} catch (error) {
|
||||
console.error('加载模型失败:', error);
|
||||
document.getElementById('modelList').innerHTML = `
|
||||
@@ -335,9 +367,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 渲染模型列表
|
||||
// 渲染模型列表 - 使用分栏显示数据库模型和已训练模型
|
||||
function renderModels() {
|
||||
const container = document.getElementById('modelList');
|
||||
|
||||
// 分离数据库模型和已训练模型
|
||||
const dbModels = allModels.filter(m => m.source !== 'trained');
|
||||
const trainedModels = allModels.filter(m => m.source === 'trained');
|
||||
|
||||
if (allModels.length === 0) {
|
||||
container.innerHTML = `
|
||||
<div class="p-8 text-center text-gray-400">
|
||||
@@ -348,42 +385,74 @@
|
||||
return;
|
||||
}
|
||||
|
||||
container.innerHTML = allModels.map(model => {
|
||||
const isSelected = selectedModels.has(model.id);
|
||||
const isDisabled = !isSelected && selectedModels.size >= 4;
|
||||
const typeText = {
|
||||
'LLM': '大语言模型',
|
||||
'CV': '计算机视觉',
|
||||
'NLP': '自然语言处理',
|
||||
'Embedding': '向量模型',
|
||||
'Other': '其他'
|
||||
}[model.type] || model.type || '其他';
|
||||
let html = '';
|
||||
|
||||
return `
|
||||
<div class="model-card border-b border-gray-100 last:border-b-0 p-4 cursor-pointer ${isSelected ? 'selected bg-blue-50/50' : ''} ${isDisabled ? 'disabled opacity-50' : ''} hover:bg-gray-50 transition-colors"
|
||||
onclick="toggleModel(${model.id})">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center flex-1 min-w-0">
|
||||
<div class="w-5 h-5 rounded-full border-2 ${isSelected ? 'border-primary bg-primary' : 'border-gray-300'} flex items-center justify-center mr-3 flex-shrink-0">
|
||||
${isSelected ? '<i class="fa fa-check text-white text-xs"></i>' : ''}
|
||||
</div>
|
||||
<div class="min-w-0">
|
||||
<div class="flex items-center mb-1">
|
||||
<span class="font-medium text-gray-800 truncate">${model.name}</span>
|
||||
<span class="ml-2 px-2 py-0.5 bg-blue-100 text-blue-700 text-xs rounded flex-shrink-0">${typeText}</span>
|
||||
</div>
|
||||
<p class="text-sm text-gray-500 truncate">${model.description || '暂无描述'}</p>
|
||||
</div>
|
||||
// 数据库模型区域
|
||||
if (dbModels.length > 0) {
|
||||
html += `
|
||||
<div class="border-b border-gray-200">
|
||||
<div class="px-4 py-2 bg-gray-50 text-xs font-medium text-gray-600 flex items-center">
|
||||
<i class="fa fa-database mr-1"></i> 数据库模型 (${dbModels.length})
|
||||
</div>
|
||||
<div class="divide-y divide-gray-100">
|
||||
`;
|
||||
html += dbModels.map(model => renderModelCard(model)).join('');
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
// 已训练模型区域
|
||||
if (trainedModels.length > 0) {
|
||||
html += `
|
||||
<div class="border-b border-gray-200">
|
||||
<div class="px-4 py-2 bg-green-50 text-xs font-medium text-green-700 flex items-center">
|
||||
<i class="fa fa-check-circle mr-1"></i> 已训练模型 (${trainedModels.length})
|
||||
</div>
|
||||
<div class="divide-y divide-gray-100">
|
||||
`;
|
||||
html += trainedModels.map(model => renderModelCard(model)).join('');
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
container.innerHTML = html;
|
||||
updateSelectedCount();
|
||||
}
|
||||
|
||||
// 渲染单个模型卡片
|
||||
function renderModelCard(model) {
|
||||
const isSelected = selectedModels.has(model.id);
|
||||
const isDisabled = !isSelected && selectedModels.size >= 4;
|
||||
const typeText = {
|
||||
'LLM': '大语言模型',
|
||||
'CV': '计算机视觉',
|
||||
'NLP': '自然语言处理',
|
||||
'Embedding': '向量模型',
|
||||
'Other': '其他'
|
||||
}[model.type] || model.type || '其他';
|
||||
// 处理字符串类型的ID
|
||||
const modelId = typeof model.id === 'string' ? `'${model.id.replace(/'/g, "\\'")}'` : model.id;
|
||||
|
||||
return `
|
||||
<div class="model-card border-b border-gray-100 last:border-b-0 p-4 cursor-pointer ${isSelected ? 'selected bg-blue-50/50' : ''} ${isDisabled ? 'disabled opacity-50' : ''} hover:bg-gray-50 transition-colors"
|
||||
onclick="toggleModel(${modelId})">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center flex-1 min-w-0">
|
||||
<div class="w-5 h-5 rounded-full border-2 ${isSelected ? 'border-primary bg-primary' : 'border-gray-300'} flex items-center justify-center mr-3 flex-shrink-0">
|
||||
${isSelected ? '<i class="fa fa-check text-white text-xs"></i>' : ''}
|
||||
</div>
|
||||
<div class="ml-4 text-xs text-gray-400 flex-shrink-0">
|
||||
${model.model_source === 'local' ? '本地模型' : '在线模型'}
|
||||
<div class="min-w-0">
|
||||
<div class="flex items-center mb-1">
|
||||
<span class="font-medium text-gray-800 truncate">${model.name}</span>
|
||||
<span class="ml-2 px-2 py-0.5 bg-blue-100 text-blue-700 text-xs rounded flex-shrink-0">${typeText}</span>
|
||||
</div>
|
||||
<p class="text-sm text-gray-500 truncate">${model.description || '暂无描述'}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ml-4 text-xs text-gray-400 flex-shrink-0">
|
||||
${model.source === 'trained' || model.model_source === 'local' ? '本地模型' : '在线模型'}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}).join('');
|
||||
|
||||
updateSelectedCount();
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
// 切换模型选择
|
||||
@@ -399,6 +468,11 @@
|
||||
renderModels();
|
||||
}
|
||||
|
||||
// 暴露到全局作用域供 onclick 调用
|
||||
window.toggleModel = toggleModel;
|
||||
window.goBack = goBack;
|
||||
window.submitForm = submitForm;
|
||||
|
||||
// 筛选模型
|
||||
function filterModels(keyword) {
|
||||
const filtered = allModels.filter(model => {
|
||||
@@ -411,7 +485,7 @@
|
||||
renderFilteredModels(filtered);
|
||||
}
|
||||
|
||||
// 渲染筛选后的模型列表
|
||||
// 渲染筛选后的模型列表 - 使用分栏显示
|
||||
function renderFilteredModels(models) {
|
||||
const container = document.getElementById('modelList');
|
||||
if (models.length === 0) {
|
||||
@@ -424,40 +498,39 @@
|
||||
return;
|
||||
}
|
||||
|
||||
container.innerHTML = models.map(model => {
|
||||
const isSelected = selectedModels.has(model.id);
|
||||
const isDisabled = !isSelected && selectedModels.size >= 4;
|
||||
const typeText = {
|
||||
'LLM': '大语言模型',
|
||||
'CV': '计算机视觉',
|
||||
'NLP': '自然语言处理',
|
||||
'Embedding': '向量模型',
|
||||
'Other': '其他'
|
||||
}[model.type] || model.type || '其他';
|
||||
// 分离数据库模型和已训练模型
|
||||
const dbModels = models.filter(m => m.source !== 'trained');
|
||||
const trainedModels = models.filter(m => m.source === 'trained');
|
||||
|
||||
return `
|
||||
<div class="model-card border-b border-gray-100 last:border-b-0 p-4 cursor-pointer ${isSelected ? 'selected bg-blue-50/50' : ''} ${isDisabled ? 'disabled opacity-50' : ''} hover:bg-gray-50 transition-colors"
|
||||
onclick="toggleModel(${model.id})">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center flex-1 min-w-0">
|
||||
<div class="w-5 h-5 rounded-full border-2 ${isSelected ? 'border-primary bg-primary' : 'border-gray-300'} flex items-center justify-center mr-3 flex-shrink-0">
|
||||
${isSelected ? '<i class="fa fa-check text-white text-xs"></i>' : ''}
|
||||
</div>
|
||||
<div class="min-w-0">
|
||||
<div class="flex items-center mb-1">
|
||||
<span class="font-medium text-gray-800 truncate">${model.name}</span>
|
||||
<span class="ml-2 px-2 py-0.5 bg-blue-100 text-blue-700 text-xs rounded flex-shrink-0">${typeText}</span>
|
||||
</div>
|
||||
<p class="text-sm text-gray-500 truncate">${model.description || '暂无描述'}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ml-4 text-xs text-gray-400 flex-shrink-0">
|
||||
${model.model_source === 'local' ? '本地模型' : '在线模型'}
|
||||
</div>
|
||||
let html = '';
|
||||
|
||||
// 数据库模型区域
|
||||
if (dbModels.length > 0) {
|
||||
html += `
|
||||
<div class="border-b border-gray-200">
|
||||
<div class="px-4 py-2 bg-gray-50 text-xs font-medium text-gray-600 flex items-center">
|
||||
<i class="fa fa-database mr-1"></i> 数据库模型 (${dbModels.length})
|
||||
</div>
|
||||
</div>
|
||||
<div class="divide-y divide-gray-100">
|
||||
`;
|
||||
}).join('');
|
||||
html += dbModels.map(model => renderModelCard(model)).join('');
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
// 已训练模型区域
|
||||
if (trainedModels.length > 0) {
|
||||
html += `
|
||||
<div class="border-b border-gray-200">
|
||||
<div class="px-4 py-2 bg-green-50 text-xs font-medium text-green-700 flex items-center">
|
||||
<i class="fa fa-check-circle mr-1"></i> 已训练模型 (${trainedModels.length})
|
||||
</div>
|
||||
<div class="divide-y divide-gray-100">
|
||||
`;
|
||||
html += trainedModels.map(model => renderModelCard(model)).join('');
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
container.innerHTML = html;
|
||||
}
|
||||
|
||||
// 更新已选数量
|
||||
|
||||
Reference in New Issue
Block a user