From 1a847996c89589fe05477ad12fb7a6e93a36354d Mon Sep 17 00:00:00 2001 From: leokaka1 Date: Thu, 22 Jan 2026 16:46:12 +0800 Subject: [PATCH] =?UTF-8?q?1=20.=E4=BF=AE=E6=94=B9=E4=BA=86=E6=96=B0?= =?UTF-8?q?=E5=BB=BA=E8=AF=84=E4=BC=B0=E6=8C=87=E6=A0=87=E5=88=A0=E9=99=A4?= =?UTF-8?q?=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .claude/settings.local.json | 4 +- src/api/__init__.py | 2 + src/api/dimension.py | 144 +++++++++++++ src/api/model_manage.py | 14 +- src/main.py | 27 ++- web/pages/main.html | 117 +++++++++-- web/pages/model-dimension-create.html | 133 ++++++++++-- web/pages/model-eval.html | 108 +++++++--- web/pages/model-manage-create.html | 36 +++- web/pages/model-manage.html | 286 ++++++++++++++++++++++++++ 10 files changed, 800 insertions(+), 71 deletions(-) create mode 100644 src/api/dimension.py create mode 100644 web/pages/model-manage.html diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 0df895a..ab8d2ca 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -39,7 +39,9 @@ "Bash(xargs:*)", "Bash(/root/miniconda3/bin/pip install psutil)", "Bash(python -m py_compile:*)", - "Bash(git checkout:*)" + "Bash(git checkout:*)", + "Bash(pip show:*)", + "Bash(pip install:*)" ] } } diff --git a/src/api/__init__.py b/src/api/__init__.py index abb8722..32ce6b2 100644 --- a/src/api/__init__.py +++ b/src/api/__init__.py @@ -4,6 +4,7 @@ API 路由包 from .datasets import datasets_bp from .model_manage import model_manage_bp from .model_chat import model_chat_bp +from .dimension import dimension_bp # 注册所有蓝图 def register_blueprints(app): @@ -11,3 +12,4 @@ def register_blueprints(app): app.register_blueprint(datasets_bp) app.register_blueprint(model_manage_bp) app.register_blueprint(model_chat_bp) + app.register_blueprint(dimension_bp) diff --git a/src/api/dimension.py b/src/api/dimension.py new file mode 100644 index 0000000..a46474d --- /dev/null +++ b/src/api/dimension.py @@ -0,0 +1,144 @@ +""" +评测维度管理 API 路由 +""" +import os +import pymysql +import yaml +from flask import Blueprint, request, jsonify + +# 获取项目根目录 +PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +# 创建蓝图 +dimension_bp = Blueprint('dimension', __name__, url_prefix='/api/dimension') + + +def get_db_connection(): + """获取数据库连接""" + CONFIG_PATH = os.path.join(PROJECT_ROOT, 'config.yaml') + with open(CONFIG_PATH, 'r', encoding='utf-8') as f: + CONFIG = yaml.safe_load(f) + db_config = CONFIG['database'] + return pymysql.connect( + host=db_config['host'], + port=db_config['port'], + user=db_config['username'], + password=db_config['password'], + database=db_config['name'], + charset=db_config.get('charset', 'utf8mb4'), + cursorclass=pymysql.cursors.DictCursor + ) + + +def generic_get_all(table_name, order_by='create_time DESC'): + """通用查询所有""" + conn = get_db_connection() + cursor = conn.cursor() + cursor.execute(f"SELECT * FROM {table_name} ORDER BY {order_by}") + result = cursor.fetchall() + cursor.close() + conn.close() + return result + + +def generic_create(table_name, data): + """通用创建""" + conn = get_db_connection() + cursor = conn.cursor() + columns = ', '.join(data.keys()) + placeholders = ', '.join(['%s'] * len(data)) + sql = f"INSERT INTO {table_name} ({columns}) VALUES ({placeholders})" + cursor.execute(sql, list(data.values())) + conn.commit() + new_id = cursor.lastrowid + cursor.close() + conn.close() + return new_id + + +def generic_update(table_name, id_val, data): + """通用更新""" + conn = get_db_connection() + cursor = conn.cursor() + set_clause = ', '.join([f"{k} = %s" for k in data.keys()]) + sql = f"UPDATE {table_name} SET {set_clause} WHERE id = %s" + values = list(data.values()) + [id_val] + cursor.execute(sql, values) + conn.commit() + cursor.close() + conn.close() + + +def generic_delete(table_name, id_val): + """通用删除""" + conn = get_db_connection() + cursor = conn.cursor() + cursor.execute(f"DELETE FROM {table_name} WHERE id = %s", (id_val,)) + conn.commit() + cursor.close() + conn.close() + + +def generic_get_by_id(table_name, id_val): + """通用按ID查询""" + conn = get_db_connection() + cursor = conn.cursor() + cursor.execute(f"SELECT * FROM {table_name} WHERE id = %s", (id_val,)) + result = cursor.fetchone() + cursor.close() + conn.close() + return result + + +# ============ 评测维度 CRUD ============ + +@dimension_bp.route('', methods=['GET']) +def get_dimensions(): + """获取所有评测维度""" + return jsonify({'code': 0, 'data': generic_get_all('model_dimension')}) + + +@dimension_bp.route('/', methods=['GET']) +def get_dimension_by_id(id): + """获取单个评测维度""" + dimension = generic_get_by_id('model_dimension', id) + if dimension: + return jsonify({'code': 0, 'data': dimension}) + return jsonify({'code': 1, 'message': '维度不存在'}) + + +@dimension_bp.route('', methods=['POST']) +def create_dimension(): + """创建评测维度""" + data = request.json + insert_data = { + 'name': data.get('name'), + 'type': data.get('type'), + 'description': data.get('description', '') + } + new_id = generic_create('model_dimension', insert_data) + return jsonify({'code': 0, 'message': '创建成功', 'id': new_id}) + + +@dimension_bp.route('/', methods=['PUT']) +def update_dimension(id): + """更新评测维度""" + data = request.json + update_data = {} + if 'name' in data: + update_data['name'] = data['name'] + if 'type' in data: + update_data['type'] = data['type'] + if 'description' in data: + update_data['description'] = data['description'] + + if update_data: + generic_update('model_dimension', id, update_data) + return jsonify({'code': 0, 'message': '更新成功'}) + + +@dimension_bp.route('/', methods=['DELETE']) +def delete_dimension(id): + """删除评测维度""" + generic_delete('model_dimension', id) + return jsonify({'code': 0, 'message': '删除成功'}) diff --git a/src/api/model_manage.py b/src/api/model_manage.py index 6453052..55830b3 100644 --- a/src/api/model_manage.py +++ b/src/api/model_manage.py @@ -116,7 +116,8 @@ def create_model_manage(): 'name': data.get('name'), 'type': data.get('type'), 'model_source': data.get('model_source', 'local'), - 'description': data.get('description') + 'description': data.get('description'), + 'purpose': data.get('purpose', 'inference') # 默认推理用途 } if data.get('model_source') == 'local': @@ -138,6 +139,17 @@ def update_model_manage(id): return jsonify({'code': 0, 'message': '更新成功'}) +@model_manage_bp.route('//purpose', methods=['PUT']) +def update_model_purpose(id): + """更新模型用途""" + data = request.json + purpose = data.get('purpose') + if purpose not in ['training', 'inference', 'evaluation']: + return jsonify({'code': 1, 'message': '无效的用途类型'}) + generic_update('model_manage', id, {'purpose': purpose}) + return jsonify({'code': 0, 'message': '更新成功'}) + + @model_manage_bp.route('/', methods=['DELETE']) def delete_model_manage(id): """删除模型""" diff --git a/src/main.py b/src/main.py index 012bf31..851f7da 100644 --- a/src/main.py +++ b/src/main.py @@ -174,6 +174,17 @@ def init_database(): api_key VARCHAR(500), model_name VARCHAR(255), description TEXT, + purpose VARCHAR(50) DEFAULT 'inference', + create_time DATETIME DEFAULT CURRENT_TIMESTAMP, + update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4""", + + # 评测维度表 + """CREATE TABLE IF NOT EXISTS model_dimension ( + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(255) NOT NULL, + type VARCHAR(100), + description TEXT, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4""", @@ -204,6 +215,13 @@ def init_database(): except Exception as e: print(f" 表 {i+1} 创建失败: {e}") + # 为已存在的 model_manage 表添加 purpose 列 + try: + cursor.execute("ALTER TABLE model_manage ADD COLUMN purpose VARCHAR(50) DEFAULT 'inference'") + print(" model_manage 表添加 purpose 列成功") + except Exception as e: + print(f" model_manage 表 purpose 列处理: {e}") + # 插入默认管理员用户 cursor.execute("SELECT * FROM users WHERE username = 'admin'") if not cursor.fetchone(): @@ -221,7 +239,14 @@ def init_database(): app = Flask(__name__) app.config['SECRET_KEY'] = CONFIG['secret_key'] -CORS(app, resources={r"/api/*": {"origins": "*"}}) +app.config['CORS_HEADERS'] = 'Content-Type' +CORS(app, resources={ + r"/api/*": { + "origins": "*", + "methods": ["GET", "POST", "PUT", "DELETE", "OPTIONS"], + "allow_headers": ["Content-Type", "Authorization"] + } +}, supports_credentials=False) # 注册蓝图 register_blueprints(app) diff --git a/web/pages/main.html b/web/pages/main.html index f3a5e37..63f7f24 100644 --- a/web/pages/main.html +++ b/web/pages/main.html @@ -482,13 +482,23 @@ 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 + ''; + return '' + displayText + ''; }}, { title: '描述', key: 'description', render: (val) => val || '-' }, { title: '创建时间', key: 'create_time', render: (val) => val ? new Date(val).toLocaleString('zh-CN') : '-' } @@ -778,6 +788,29 @@ }); } + // 更新模型用途 + async function updateModelPurpose(id, purpose) { + try { + const response = await fetch(`${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) { + loadPage(activeLink.dataset.page); + } + } else { + showMessage('错误', result.message || '更新失败', 'error'); + } + } catch (error) { + showMessage('错误', '更新失败: ' + error.message, 'error'); + } + } + // 切换单个项的选中状态 function toggleItemSelection(id, api) { if (selectedItems.has(id)) { @@ -2229,8 +2262,8 @@ } // ============ 自定义消息弹窗 ============ - // 显示消息弹窗 - function showMessage(title, message, type = 'info', onConfirm) { + // 显示消息弹窗 - 使用 window 确保全局可访问 + window.showMessage = function(title, message, type = 'info', onConfirm) { const modal = document.getElementById('customModal'); const modalTitle = document.getElementById('modalTitle'); const modalMessage = document.getElementById('modalMessage'); @@ -2263,7 +2296,7 @@ modalSingleBtnGroup.classList.remove('hidden'); const confirmBtn = modalConfirmBtn2; if (type === 'error') { - confirmBtn.className = 'px-6 py-2 bg-danger text-white rounded-lg hover:bg-danger/90 transition-colors'; + confirmBtn.className = 'px-6 py-2 bg-red-500 text-white rounded-lg hover:bg-red-600 transition-colors'; } else { confirmBtn.className = 'px-6 py-2 bg-primary text-white rounded-lg hover:bg-primary/90 transition-colors'; } @@ -2272,10 +2305,17 @@ modal.classList.remove('hidden'); document.body.style.overflow = 'hidden'; - // 绑定确认按钮事件 - confirmBtn.onclick = () => { + // 保存回调到按钮属性 + confirmBtn._onConfirm = onConfirm; + + // 使用 function 而不是箭头函数 + confirmBtn.onclick = function() { closeModal(); - if (onConfirm) onConfirm(); + const callback = this._onConfirm; + if (typeof callback === 'function') { + callback(); + } + this._onConfirm = null; }; } @@ -2286,8 +2326,8 @@ document.body.style.overflow = ''; } - // 确认弹窗(两个按钮) - function showConfirm(title, message, onConfirm, onCancel) { + // 确认弹窗(两个按钮)- 使用 window 确保全局可访问 + window.showConfirm = function(title, message, onConfirm, onCancel, type = 'info') { const modal = document.getElementById('customModal'); const modalTitle = document.getElementById('modalTitle'); const modalMessage = document.getElementById('modalMessage'); @@ -2296,9 +2336,20 @@ const modalCancelBtn = document.getElementById('modalCancelBtn'); const modalBtnGroup = document.getElementById('modalBtnGroup'); + if (!modalConfirmBtn) { + console.error('modalConfirmBtn not found'); + return; + } + modalTitle.textContent = title; modalMessage.innerHTML = message; - modalIcon.innerHTML = '
'; + + // 根据类型设置图标 + if (type === 'warning') { + modalIcon.innerHTML = '
'; + } else { + modalIcon.innerHTML = '
'; + } modalBtnGroup.classList.remove('hidden'); modalConfirmBtn.textContent = '确定'; @@ -2307,14 +2358,29 @@ modal.classList.remove('hidden'); document.body.style.overflow = 'hidden'; - modalConfirmBtn.onclick = () => { + // 保存回调到按钮属性 + modalConfirmBtn._onConfirm = onConfirm; + modalConfirmBtn._onCancel = onCancel; + + // 使用 function 而不是箭头函数,确保 this 指向正确 + modalConfirmBtn.onclick = function() { closeModal(); - if (onConfirm) onConfirm(); + const callback = this._onConfirm; + if (typeof callback === 'function') { + callback(); + } + this._onConfirm = null; + this._onCancel = null; }; - modalCancelBtn.onclick = () => { + modalCancelBtn.onclick = function() { closeModal(); - if (onCancel) onCancel(); + const callback = this._onCancel || modalConfirmBtn._onCancel; + if (typeof callback === 'function') { + callback(); + } + modalConfirmBtn._onConfirm = null; + modalConfirmBtn._onCancel = null; }; } @@ -2382,6 +2448,29 @@ window.location.href = 'model-dimension-create.html'; } + // 删除评测维度 + async function deleteDimension(id) { + showConfirm('确认删除', '确定要删除此评测维度吗?', async () => { + try { + const response = await fetch(`${API_BASE}/dimension/${id}`, { + method: 'DELETE' + }); + const result = await response.json(); + if (result.code === 0) { + showMessage('成功', '删除成功', 'success', () => { + // 刷新维度列表 - 切换到 dimensions tab + switchTab(document.querySelector('[data-tab="dimensions"]'), 'dimensions'); + }); + } else { + showMessage('错误', result.message || '删除失败', 'error'); + } + } catch (error) { + console.error('删除维度失败:', error); + showMessage('错误', '删除失败: ' + error.message, 'error'); + } + }); + } + // 切换 Tab function switchTab(btn, tabId) { // 更新按钮状态 diff --git a/web/pages/model-dimension-create.html b/web/pages/model-dimension-create.html index 4bfcf1f..f8ef180 100644 --- a/web/pages/model-dimension-create.html +++ b/web/pages/model-dimension-create.html @@ -337,12 +337,8 @@ +

请在模型管理中配置"评测模型"用途的模型

@@ -462,6 +458,35 @@ }; const API_BASE = getApiBase(); + // 加载评测模型列表(用途为 evaluation 的模型) + async function loadEvalModels() { + try { + const response = await fetch(`${API_BASE}/model-manage`); + const result = await response.json(); + + if (result.code === 0 && result.data) { + const evalModels = result.data.filter(m => m.purpose === 'evaluation'); + const select = document.getElementById('evalModel'); + + if (evalModels.length === 0) { + select.innerHTML = ''; + return; + } + + select.innerHTML = ''; + evalModels.forEach(model => { + const option = document.createElement('option'); + option.value = model.id; + option.textContent = `${model.name} (${model.model_source === 'api' ? 'API' : '本地'})`; + select.appendChild(option); + }); + } + } catch (error) { + console.error('加载评测模型列表失败:', error); + document.getElementById('evalModel').innerHTML = ''; + } + } + // 返回列表页 function goBack() { window.location.href = 'main.html?page=model-eval'; @@ -935,13 +960,16 @@ }; // 初始化函数 - function initPage() { + async function initPage() { // 绑定指标类型下拉框事件 const dimensionType = document.getElementById('dimensionType'); if (dimensionType) { dimensionType.addEventListener('change', toggleEvalConfig); } + // 加载评测模型列表 + await loadEvalModels(); + // 绑定 Markdown 编辑器事件 const evalPromptEditor = document.getElementById('evalPromptEditor'); if (evalPromptEditor) { @@ -1076,13 +1104,14 @@ const name = formData.get('name').trim(); const type = formData.get('type').trim(); + // 验证 if (!name) { - alert('请输入维度名称'); + showMessage('提示', '请输入维度名称', 'warning'); return; } if (!type) { - alert('请选择指标类型'); + showMessage('提示', '请选择指标类型', 'warning'); return; } @@ -1093,22 +1122,22 @@ const evalMethod = formData.get('eval_method'); if (!evalModel) { - alert('请选择评估使用的大模型'); + showMessage('提示', '请选择评估使用的大模型', 'warning'); return; } if (!evalPrompt) { - alert('请输入评估 Prompt'); + showMessage('提示', '请输入评估 Prompt', 'warning'); return; } if (!evalMethod) { - alert('请选择评估方式'); + showMessage('提示', '请选择评估方式', 'warning'); return; } } else if (type === 'text_similarity') { // 规则评估:获取所有选中的评估方式 const checkedMethods = Array.from(document.querySelectorAll('input[name="eval_method"]:checked')).map(cb => cb.value); if (checkedMethods.length === 0) { - alert('请至少选择一个评估方式'); + showMessage('提示', '请至少选择一个评估方式', 'warning'); return; } } @@ -1147,22 +1176,92 @@ } try { - const response = await fetch(`${API_BASE}/model-eval/dimension`, { + const response = await fetch(`${API_BASE}/dimension`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); const result = await response.json(); if (result.code === 0) { - alert('评测维度创建成功!'); - goBack(); + showMessage('成功', '评测维度创建成功!', 'success', () => { + goBack(); + }); } else { - alert('创建失败: ' + (result.message || '未知错误')); + showMessage('错误', result.message || '创建失败', 'error'); } } catch (error) { - alert('创建失败: ' + error.message); + showMessage('错误', '创建失败: ' + error.message, 'error'); } } + + // ============ 自定义消息弹窗 ============ + 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 modalBtnGroup = document.getElementById('modalBtnGroup'); + const modalSingleBtnGroup = document.getElementById('modalSingleBtnGroup'); + + if (!modalConfirmBtn) { + console.error('modalConfirmBtn2 not found'); + return; + } + + modalTitle.textContent = title; + modalMessage.innerHTML = message; + + if (type === 'success') { + modalIcon.innerHTML = '
'; + } else if (type === 'error') { + modalIcon.innerHTML = '
'; + } else if (type === 'warning') { + modalIcon.innerHTML = '
'; + } else { + modalIcon.innerHTML = '
'; + } + + // 隐藏双按钮组,显示单按钮组 + if (modalBtnGroup) modalBtnGroup.classList.add('hidden'); + modalSingleBtnGroup.classList.remove('hidden'); + modalConfirmBtn.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'; + + // 保存回调到按钮属性 + modalConfirmBtn._onConfirm = onConfirm; + + // 使用 function 确保 this 指向正确 + modalConfirmBtn.onclick = function() { + modal.classList.add('hidden'); + document.body.style.overflow = ''; + const callback = this._onConfirm; + if (typeof callback === 'function') { + callback(); + } + this._onConfirm = null; + }; + } + + + diff --git a/web/pages/model-eval.html b/web/pages/model-eval.html index c636f41..b39516d 100644 --- a/web/pages/model-eval.html +++ b/web/pages/model-eval.html @@ -114,6 +114,10 @@ activeContent.classList.remove('hidden'); activeContent.classList.add('active'); } + // 切换到评测维度tab时刷新数据 + if (tabId === 'dimensions') { + loadDimensions(); + } }; // 加载评测任务数据 @@ -195,38 +199,49 @@ `).join(''); } - // 加载评测维度数据(模拟数据) + // 加载评测维度数据 async function loadDimensions() { - // 模拟评测维度数据 - const mockData = [ - { id: 1, name: '准确率', type: 'accuracy', desc: '模型预测正确的比例', status: '启用' }, - { id: 2, name: '召回率', type: 'recall', desc: '找出所有正例的能力', status: '启用' }, - { id: 3, name: 'F1分数', type: 'f1', desc: '准确率和召回率的调和平均', status: '启用' }, - { id: 4, name: '推理速度', type: 'speed', desc: '模型推理响应时间', status: '启用' }, - { id: 5, name: 'BLEU分数', type: 'bleu', desc: '机器翻译质量评估', status: '停用' } - ]; + try { + const response = await fetch(`${API_BASE}/dimension`); + const result = await response.json(); - const tbody = document.getElementById('dimensionsBody'); - tbody.innerHTML = mockData.map(item => ` - - ${item.name} - ${item.type} - ${item.desc} - - - ${item.status} - - - - - - - - `).join(''); + const tbody = document.getElementById('dimensionsBody'); + + if (result.code !== 0 || !result.data || result.data.length === 0) { + tbody.innerHTML = ` + + + +

暂无评测维度

+ + + `; + return; + } + + tbody.innerHTML = result.data.map(item => ` + + ${item.name || '-'} + ${item.type || '-'} + ${item.description || '-'} + + + 启用 + + + + + + + + `).join(''); + } catch (error) { + console.error('加载评测维度失败:', error); + } } // 状态样式 @@ -267,9 +282,36 @@ alert('编辑维度功能开发中'); } - function deleteDimension(id) { - if (confirm('确定要删除此维度吗?')) { - alert('删除维度功能开发中'); + async function deleteDimension(id) { + showConfirm('确认删除', '确定要删除此评测维度吗?', () => { + executeDelete(id); + }); + } + + async function executeDelete(id) { + try { + const response = await fetch(`${API_BASE}/dimension/${id}`, { + method: 'DELETE' + }); + const result = await response.json(); + if (result.code === 0) { + // 切换到评测维度tab并刷新列表 + switchToDimensionsTab(); + showMessage('成功', '删除成功', 'success'); + } else { + showMessage('错误', result.message || '删除失败', 'error'); + } + } catch (error) { + console.error('删除维度失败:', error); + showMessage('错误', '删除失败: ' + error.message, 'error'); + } + } + + // 切换到评测维度tab + function switchToDimensionsTab() { + const dimBtn = document.querySelector('[data-tab="dimensions"]'); + if (dimBtn) { + dimBtn.click(); } } diff --git a/web/pages/model-manage-create.html b/web/pages/model-manage-create.html index dd0e1fa..060754d 100644 --- a/web/pages/model-manage-create.html +++ b/web/pages/model-manage-create.html @@ -204,7 +204,7 @@

基本信息

-
+
-
+
@@ -225,6 +225,26 @@
+ +
+ +
+ + + +
+
@@ -430,6 +450,12 @@ const descInput = document.querySelector('textarea[name="description"]'); descInput.value = model.description || ''; document.getElementById('descCount').textContent = descInput.value.length; + + // 填充用途(兼容旧数据没有 purpose 字段的情况) + const purpose = model.purpose || 'inference'; + document.querySelectorAll('input[name="purpose"]').forEach(radio => { + radio.checked = radio.value === purpose; + }); } } catch (error) { console.error('加载模型数据失败:', error); @@ -458,13 +484,15 @@ const form = document.getElementById('modelForm'); const formData = new FormData(form); const modelSource = formData.get('model_source'); - console.log('modelSource:', modelSource); + const purpose = formData.get('purpose'); + console.log('modelSource:', modelSource, 'purpose:', purpose); const data = { name: formData.get('name'), type: formData.get('type'), model_source: modelSource, - description: formData.get('description') + description: formData.get('description'), + purpose: purpose }; // 根据模型来源设置不同的字段 diff --git a/web/pages/model-manage.html b/web/pages/model-manage.html new file mode 100644 index 0000000..525bc81 --- /dev/null +++ b/web/pages/model-manage.html @@ -0,0 +1,286 @@ + + + + + + 模型管理 / 远光软件微调平台 + + + + + + +
+

模型管理

+
+ + + + +
+
+ + +
+ +
+
+ +
+ +
+ + +
+
+ + + + + + + + + + + + + + + +
模型名称模型类型用途模型来源描述创建时间操作
+
+ + +
+
+ + + +