feat: 子类别管理界面重构,支持可视化增删改

- 添加子类别编辑弹框(subcategoryModal)
- 子类别列表可视化显示(卡片样式)
- 支持添加、编辑、删除子类别
- 表单输入关键特性字段和标签
- 替换原来的JSON文本编辑方式
This commit is contained in:
2026-04-28 00:28:36 +08:00
parent a9cbd1b2ba
commit 9cd9ccd8e0

View File

@@ -369,6 +369,23 @@
</div>
</div>
<!-- 子类别编辑弹框 -->
<div id="subcategoryModal" class="fixed inset-0 bg-black/50 z-50 hidden flex items-center justify-center">
<div class="bg-white rounded-xl max-w-lg w-full mx-4">
<div class="p-6 border-b flex justify-between items-center">
<h2 class="text-xl font-bold text-gray-800" id="subcategoryModalTitle"><i class="ri-folder-line mr-2"></i>添加子类别</h2>
<button onclick="closeSubcategoryModal()" class="text-gray-400 hover:text-gray-600"><i class="ri-close-line text-2xl"></i></button>
</div>
<div id="subcategoryModalContent" class="p-6">
<!-- 动态内容 -->
</div>
<div class="p-6 border-t flex justify-end gap-4">
<button onclick="closeSubcategoryModal()" class="px-4 py-2 bg-gray-200 text-gray-600 rounded-lg hover:bg-gray-300">取消</button>
<button onclick="saveSubcategory()" class="px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700"><i class="ri-save-line mr-1"></i>保存</button>
</div>
</div>
</div>
<script>
let currentType = '';
let currentId = '';
@@ -1104,7 +1121,8 @@
// 表单模板
function getCategoryForm(data = {}) {
const subcategories = data.subcategories || [];
const subcategoriesJson = JSON.stringify(subcategories, null, 2);
// 存储到全局变量,便于管理
window.currentEditingSubcategories = JSON.parse(JSON.stringify(subcategories));
return `<form id="itemForm" class="space-y-4">
<div class="grid grid-cols-2 gap-4">
@@ -1127,25 +1145,156 @@
</div>
<div><label class="text-sm text-gray-600 mb-1 block">描述</label><textarea name="description" rows="2" class="w-full px-3 py-2 border rounded-lg">${data.description || ''}</textarea></div>
<!-- 子类别配置 -->
<!-- 子类别管理 -->
<div class="border-t pt-4">
<label class="text-sm text-gray-600 mb-2 block"><i class="ri-folder-line mr-1"></i>子类别配置JSON格式</label>
<div class="bg-blue-50 rounded-lg p-3 mb-2 text-xs text-blue-700">
<p class="mb-2">子类别配置示例:</p>
<pre class="bg-blue-100 p-2 rounded overflow-x-auto">[
{
"id": "chat",
"name": "对话模型",
"icon": "ri-chat-3-line",
"key_features": ["context_length", "mmlu"],
"feature_labels": {"context_length": "上下文", "mmlu": "MMLU"}
}
]</pre>
<div class="flex justify-between items-center mb-3">
<label class="text-sm font-medium text-gray-700"><i class="ri-folder-line mr-1"></i>子类别管理</label>
<button onclick="openSubcategoryAddModal()" class="px-3 py-1.5 bg-green-600 text-white rounded-lg text-sm hover:bg-green-700">
<i class="ri-add-line mr-1"></i>添加子类别
</button>
</div>
<textarea name="subcategories" rows="8" class="w-full px-3 py-2 border rounded-lg font-mono text-sm" placeholder='[]'>${subcategoriesJson}</textarea>
<div id="subcategoriesList" class="space-y-2">
${renderSubcategoriesList(subcategories)}
</div>
<input type="hidden" name="subcategories" id="subcategoriesHidden" value='${JSON.stringify(subcategories)}'>
</div>
</form>`;
}
// 渲染子类别列表
function renderSubcategoriesList(subcategories) {
if (!subcategories || subcategories.length === 0) {
return '<div class="text-gray-400 text-sm py-4 text-center bg-gray-50 rounded-lg">暂无子类别,点击上方按钮添加</div>';
}
return subcategories.map((sub, index) => `
<div class="bg-gray-50 rounded-lg p-3 flex justify-between items-center group hover:bg-gray-100">
<div class="flex items-center gap-3">
<div class="w-8 h-8 rounded-lg bg-indigo-100 flex items-center justify-center">
<i class="${sub.icon || 'ri-folder-line'} text-indigo-600"></i>
</div>
<div>
<div class="font-medium text-gray-800">${sub.name}</div>
<div class="text-xs text-gray-500">ID: ${sub.id} | 特性: ${(sub.key_features || []).join(', ')}</div>
</div>
</div>
<div class="flex gap-2 opacity-0 group-hover:opacity-100 transition">
<button onclick="editSubcategory(${index})" class="px-2 py-1 text-blue-600 hover:bg-blue-50 rounded text-sm">
<i class="ri-edit-line"></i>
</button>
<button onclick="deleteSubcategory(${index})" class="px-2 py-1 text-red-600 hover:bg-red-50 rounded text-sm">
<i class="ri-delete-bin-line"></i>
</button>
</div>
</div>
`).join('');
}
// 打开子类别添加弹框
function openSubcategoryAddModal() {
document.getElementById('subcategoryModalTitle').textContent = '添加子类别';
document.getElementById('subcategoryModalContent').innerHTML = getSubcategoryForm();
document.getElementById('subcategoryModal').classList.remove('hidden');
window.editingSubcategoryIndex = -1;
}
// 编辑子类别
function editSubcategory(index) {
const sub = window.currentEditingSubcategories[index];
document.getElementById('subcategoryModalTitle').textContent = '编辑子类别';
document.getElementById('subcategoryModalContent').innerHTML = getSubcategoryForm(sub);
document.getElementById('subcategoryModal').classList.remove('hidden');
window.editingSubcategoryIndex = index;
}
// 删除子类别
function deleteSubcategory(index) {
if (!confirm('确定删除此子类别?')) return;
window.currentEditingSubcategories.splice(index, 1);
document.getElementById('subcategoriesList').innerHTML = renderSubcategoriesList(window.currentEditingSubcategories);
document.getElementById('subcategoriesHidden').value = JSON.stringify(window.currentEditingSubcategories);
}
// 子类别表单
function getSubcategoryForm(data = {}) {
const keyFeatures = (data.key_features || []).join(', ');
const featureLabels = data.feature_labels || {};
const featureLabelsStr = Object.entries(featureLabels).map(([k, v]) => `${k}:${v}`).join(', ');
return `<div class="space-y-4">
<div class="grid grid-cols-2 gap-4">
<div><label class="text-sm text-gray-600 mb-1 block">ID *</label><input type="text" id="sub_id" value="${data.id || ''}" required class="w-full px-3 py-2 border rounded-lg"></div>
<div><label class="text-sm text-gray-600 mb-1 block">名称 *</label><input type="text" id="sub_name" value="${data.name || ''}" required class="w-full px-3 py-2 border rounded-lg"></div>
<div><label class="text-sm text-gray-600 mb-1 block">图标</label><input type="text" id="sub_icon" value="${data.icon || 'ri-folder-line'}" class="w-full px-3 py-2 border rounded-lg" placeholder="ri-folder-line"></div>
</div>
<div>
<label class="text-sm text-gray-600 mb-1 block">关键特性字段</label>
<input type="text" id="sub_key_features" value="${keyFeatures}" class="w-full px-3 py-2 border rounded-lg" placeholder="context_length, mmlu, input_price">
<p class="text-xs text-gray-500 mt-1">逗号分隔context_length, mmlu, input_price</p>
</div>
<div>
<label class="text-sm text-gray-600 mb-1 block">特性标签(显示名)</label>
<input type="text" id="sub_feature_labels" value="${featureLabelsStr}" class="w-full px-3 py-2 border rounded-lg" placeholder="context_length:上下文, mmlu:MMLU">
<p class="text-xs text-gray-500 mt-1">格式:字段名:显示名,逗号分隔</p>
</div>
</div>`;
}
// 保存子类别
function saveSubcategory() {
const id = document.getElementById('sub_id').value.trim();
const name = document.getElementById('sub_name').value.trim();
const icon = document.getElementById('sub_icon').value.trim() || 'ri-folder-line';
const keyFeaturesStr = document.getElementById('sub_key_features').value.trim();
const featureLabelsStr = document.getElementById('sub_feature_labels').value.trim();
if (!id || !name) {
alert('ID和名称不能为空');
return;
}
// 解析 key_features
const key_features = keyFeaturesStr ? keyFeaturesStr.split(',').map(s => s.trim()).filter(s => s) : [];
// 解析 feature_labels
const feature_labels = {};
if (featureLabelsStr) {
featureLabelsStr.split(',').forEach(pair => {
const [key, value] = pair.split(':').map(s => s.trim());
if (key && value) {
feature_labels[key] = value;
}
});
}
const subcategory = {
id,
name,
icon,
key_features,
feature_labels
};
if (window.editingSubcategoryIndex === -1) {
// 添加新子类别
window.currentEditingSubcategories.push(subcategory);
} else {
// 编辑现有子类别
window.currentEditingSubcategories[window.editingSubcategoryIndex] = subcategory;
}
// 更新显示
document.getElementById('subcategoriesList').innerHTML = renderSubcategoriesList(window.currentEditingSubcategories);
document.getElementById('subcategoriesHidden').value = JSON.stringify(window.currentEditingSubcategories);
// 关闭弹框
closeSubcategoryModal();
}
// 关闭子类别弹框
function closeSubcategoryModal() {
document.getElementById('subcategoryModal').classList.add('hidden');
}
function getKnowledgeForm(data = {}) {
return `<form id="itemForm" class="space-y-4">
@@ -1263,6 +1412,7 @@
document.getElementById('editModal').addEventListener('click', function(e) { if (e.target === this) closeModal(); });
document.getElementById('smartAddModal').addEventListener('click', function(e) { if (e.target === this) closeSmartAddModal(); });
document.getElementById('rawDataModal').addEventListener('click', function(e) { if (e.target === this) closeRawDataModal(); });
document.getElementById('subcategoryModal').addEventListener('click', function(e) { if (e.target === this) closeSubcategoryModal(); });
// ============ 智能添加功能 ============