Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8066fc4386 | |||
| 9cd9ccd8e0 |
4
app.py
4
app.py
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
ParamHub - 参数百科
|
||||
AI大模型与硬件参数速查平台
|
||||
v1.7.0 - 支持子类别配置和关键特性显示
|
||||
v1.7.1 - 子类别管理界面重构,支持可视化增删改
|
||||
"""
|
||||
|
||||
from flask import Flask, render_template, jsonify, request
|
||||
@@ -1402,7 +1402,7 @@ def api_delete_image(filename):
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("=" * 50)
|
||||
print("ParamHub - 参数百科 v1.7.0")
|
||||
print("ParamHub - 参数百科 v1.7.1")
|
||||
print("=" * 50)
|
||||
print(f"访问地址: http://localhost:19010")
|
||||
print(f"后台管理: http://localhost:19010/admin")
|
||||
|
||||
@@ -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(); });
|
||||
|
||||
// ============ 智能添加功能 ============
|
||||
|
||||
|
||||
Reference in New Issue
Block a user