diff --git a/json/llmcatelog.json b/json/llmcatelog.json
index 2104fb5..8964728 100644
--- a/json/llmcatelog.json
+++ b/json/llmcatelog.json
@@ -1,15 +1,18 @@
{
"tblname": "llmcatelog",
- "title":"模型类目",
+ "alias": "llmcatelog_list",
+ "title": "模型类型管理",
"params": {
- "sortby":"name",
+ "sortby": ["name"],
+ "editable": {
+ "new_data_url": "{{entire_url('../api/llmcatelog_create.dspy')}}",
+ "update_data_url": "{{entire_url('../api/llmcatelog_update.dspy')}}",
+ "delete_data_url": "{{entire_url('../api/llmcatelog_delete.dspy')}}"
+ },
"browserfields": {
"exclouded": ["id"],
"alters": {}
},
- "editexclouded": [
- "id"
- ],
- "record_toolbar": null
+ "editexclouded": ["id"]
}
}
diff --git a/wwwroot/api/llmcatelog_create.dspy b/wwwroot/api/llmcatelog_create.dspy
new file mode 100644
index 0000000..2d01e1f
--- /dev/null
+++ b/wwwroot/api/llmcatelog_create.dspy
@@ -0,0 +1,29 @@
+#!/usr/bin/env python3
+import json
+
+result = {'widgettype': 'Message', 'options': {'title': 'Error', 'message': 'Invalid', 'type': 'error'}}
+
+try:
+ dbname = get_module_dbname('llmage')
+ name = params_kw.get('name', '').strip()
+ description = params_kw.get('description', '').strip()
+
+ if not name:
+ result['options'] = {'title': '错误', 'message': '类型名不能为空', 'type': 'error'}
+ else:
+ async with DBPools().sqlorContext(dbname) as sor:
+ # 检查名称是否已存在
+ check_sql = "select id from llmcatelog where name=${name}$"
+ exists = await sor.sqlExe(check_sql, {'name': name})
+ if exists:
+ result['options'] = {'title': '提示', 'message': f'类型名「{name}」已存在', 'type': 'warning'}
+ else:
+ new_id = getID()
+ insert_sql = "insert into llmcatelog (id, name, description) values (${id}$, ${name}$, ${description}$)"
+ await sor.sqlExe(insert_sql, {'id': new_id, 'name': name, 'description': description})
+ result = {'widgettype': 'Message', 'options': {'title': '成功', 'message': f'类型「{name}」已创建', 'type': 'info'}}
+
+except Exception as e:
+ result['options'] = {'title': '错误', 'message': str(e), 'type': 'error'}
+
+return json.dumps(result, ensure_ascii=False)
diff --git a/wwwroot/api/llmcatelog_delete.dspy b/wwwroot/api/llmcatelog_delete.dspy
new file mode 100644
index 0000000..f167612
--- /dev/null
+++ b/wwwroot/api/llmcatelog_delete.dspy
@@ -0,0 +1,33 @@
+#!/usr/bin/env python3
+import json
+
+result = {'widgettype': 'Message', 'options': {'title': 'Error', 'message': 'Invalid', 'type': 'error'}}
+
+try:
+ dbname = get_module_dbname('llmage')
+ id = params_kw.get('id', '').strip()
+
+ if not id:
+ result['options'] = {'title': '错误', 'message': '记录ID不能为空', 'type': 'error'}
+ else:
+ async with DBPools().sqlorContext(dbname) as sor:
+ # 检查是否有模型关联此类型
+ check_sql = "select count(*) as cnt from llm_catalog_rel where llmcatelogid=${id}$"
+ rel_count = await sor.sqlExe(check_sql, {'id': id})
+ cnt = rel_count[0]['cnt'] if rel_count else 0
+ if cnt > 0:
+ result['options'] = {'title': '提示', 'message': f'该类型下还有 {cnt} 个模型关联,无法删除', 'type': 'warning'}
+ else:
+ # 获取类型名用于提示
+ name_sql = "select name from llmcatelog where id=${id}$"
+ name_rows = await sor.sqlExe(name_sql, {'id': id})
+ type_name = name_rows[0]['name'] if name_rows else id
+
+ delete_sql = "delete from llmcatelog where id=${id}$"
+ await sor.sqlExe(delete_sql, {'id': id})
+ result = {'widgettype': 'Message', 'options': {'title': '成功', 'message': f'类型「{type_name}」已删除', 'type': 'info'}}
+
+except Exception as e:
+ result['options'] = {'title': '错误', 'message': str(e), 'type': 'error'}
+
+return json.dumps(result, ensure_ascii=False)
diff --git a/wwwroot/api/llmcatelog_list.dspy b/wwwroot/api/llmcatelog_list.dspy
new file mode 100644
index 0000000..8bed755
--- /dev/null
+++ b/wwwroot/api/llmcatelog_list.dspy
@@ -0,0 +1,28 @@
+#!/usr/bin/env python3
+import json
+
+result = {'success': False, 'rows': [], 'total': 0}
+
+try:
+ dbname = get_module_dbname('llmage')
+ page = int(params_kw.get('page', 1))
+ rows_per_page = int(params_kw.get('rows', 20))
+ offset = (page - 1) * rows_per_page
+
+ async with DBPools().sqlorContext(dbname) as sor:
+ # 获取总数
+ count_sql = "select count(*) as cnt from llmcatelog"
+ count_rows = await sor.sqlExe(count_sql, {})
+ total = count_rows[0]['cnt'] if count_rows else 0
+
+ # 获取分页数据
+ data_sql = "select id, name, description from llmcatelog order by name limit ${limit}$ offset ${offset}$"
+ rows = await sor.sqlExe(data_sql, {'limit': rows_per_page, 'offset': offset})
+ result['rows'] = [dict(r) for r in (rows or [])]
+ result['total'] = total
+ result['success'] = True
+
+except Exception as e:
+ result['error'] = str(e)
+
+return json.dumps(result, ensure_ascii=False, default=str)
diff --git a/wwwroot/api/llmcatelog_update.dspy b/wwwroot/api/llmcatelog_update.dspy
new file mode 100644
index 0000000..487c2c0
--- /dev/null
+++ b/wwwroot/api/llmcatelog_update.dspy
@@ -0,0 +1,31 @@
+#!/usr/bin/env python3
+import json
+
+result = {'widgettype': 'Message', 'options': {'title': 'Error', 'message': 'Invalid', 'type': 'error'}}
+
+try:
+ dbname = get_module_dbname('llmage')
+ id = params_kw.get('id', '').strip()
+ name = params_kw.get('name', '').strip()
+ description = params_kw.get('description', '').strip()
+
+ if not id:
+ result['options'] = {'title': '错误', 'message': '记录ID不能为空', 'type': 'error'}
+ elif not name:
+ result['options'] = {'title': '错误', 'message': '类型名不能为空', 'type': 'error'}
+ else:
+ async with DBPools().sqlorContext(dbname) as sor:
+ # 检查名称是否已被其他记录使用
+ check_sql = "select id from llmcatelog where name=${name}$ and id != ${id}$"
+ exists = await sor.sqlExe(check_sql, {'name': name, 'id': id})
+ if exists:
+ result['options'] = {'title': '提示', 'message': f'类型名「{name}」已被其他记录使用', 'type': 'warning'}
+ else:
+ update_sql = "update llmcatelog set name=${name}$, description=${description}$ where id=${id}$"
+ await sor.sqlExe(update_sql, {'name': name, 'description': description, 'id': id})
+ result = {'widgettype': 'Message', 'options': {'title': '成功', 'message': f'类型「{name}」已更新', 'type': 'info'}}
+
+except Exception as e:
+ result['options'] = {'title': '错误', 'message': str(e), 'type': 'error'}
+
+return json.dumps(result, ensure_ascii=False)
diff --git a/wwwroot/index.ui b/wwwroot/index.ui
new file mode 100644
index 0000000..d00ca1b
--- /dev/null
+++ b/wwwroot/index.ui
@@ -0,0 +1,177 @@
+{
+ "widgettype": "VBox",
+ "options": {
+ "width": "100%",
+ "height": "100%",
+ "padding": "20px",
+ "spacing": 16
+ },
+ "subwidgets": [
+ {
+ "widgettype": "Title2",
+ "options": {
+ "text": "LLM 模型管理",
+ "halign": "left"
+ }
+ },
+ {
+ "widgettype": "ResponsableBox",
+ "options": {
+ "gap": "16px",
+ "minWidth": "250px"
+ },
+ "subwidgets": [
+ {
+ "widgettype": "VBox",
+ "options": {
+ "backgroundColor": "#1e3a5f",
+ "padding": "24px",
+ "cursor": "pointer",
+ "borderRadius": "8px"
+ },
+ "binds": [
+ {
+ "wid": "self",
+ "event": "click",
+ "actiontype": "urlwidget",
+ "target": "app.llmage_content",
+ "options": {
+ "url": "{{entire_url('/llmage/llmcatelog_list.ui')}}"
+ },
+ "mode": "replace"
+ }
+ ],
+ "subwidgets": [
+ {
+ "widgettype": "Svg",
+ "options": {
+ "svg": "",
+ "width": "40px",
+ "height": "40px"
+ }
+ },
+ {
+ "widgettype": "Title4",
+ "options": {
+ "text": "模型类型管理",
+ "color": "#ffffff",
+ "marginTop": "12px"
+ }
+ },
+ {
+ "widgettype": "Text",
+ "options": {
+ "text": "管理模型的分类和类型",
+ "color": "#90caf9",
+ "fontSize": "14px"
+ }
+ }
+ ]
+ },
+ {
+ "widgettype": "VBox",
+ "options": {
+ "backgroundColor": "#1e3a5f",
+ "padding": "24px",
+ "cursor": "pointer",
+ "borderRadius": "8px"
+ },
+ "binds": [
+ {
+ "wid": "self",
+ "event": "click",
+ "actiontype": "urlwidget",
+ "target": "app.llmage_content",
+ "options": {
+ "url": "{{entire_url('/llmage/llm_catalog_rel_manage.ui')}}"
+ },
+ "mode": "replace"
+ }
+ ],
+ "subwidgets": [
+ {
+ "widgettype": "Svg",
+ "options": {
+ "svg": "",
+ "width": "40px",
+ "height": "40px"
+ }
+ },
+ {
+ "widgettype": "Title4",
+ "options": {
+ "text": "模型-类型关联",
+ "color": "#ffffff",
+ "marginTop": "12px"
+ }
+ },
+ {
+ "widgettype": "Text",
+ "options": {
+ "text": "管理模型与类型的多对多关系",
+ "color": "#ce93d8",
+ "fontSize": "14px"
+ }
+ }
+ ]
+ },
+ {
+ "widgettype": "VBox",
+ "options": {
+ "backgroundColor": "#1e3a5f",
+ "padding": "24px",
+ "cursor": "pointer",
+ "borderRadius": "8px"
+ },
+ "binds": [
+ {
+ "wid": "self",
+ "event": "click",
+ "actiontype": "urlwidget",
+ "target": "app.llmage_content",
+ "options": {
+ "url": "{{entire_url('/llmage/llm')}}"
+ },
+ "mode": "replace"
+ }
+ ],
+ "subwidgets": [
+ {
+ "widgettype": "Svg",
+ "options": {
+ "svg": "",
+ "width": "40px",
+ "height": "40px"
+ }
+ },
+ {
+ "widgettype": "Title4",
+ "options": {
+ "text": "模型管理",
+ "color": "#ffffff",
+ "marginTop": "12px"
+ }
+ },
+ {
+ "widgettype": "Text",
+ "options": {
+ "text": "管理 LLM 模型配置",
+ "color": "#4caf50",
+ "fontSize": "14px"
+ }
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "widgettype": "VBox",
+ "id": "llmage_content",
+ "options": {
+ "width": "100%",
+ "flex": "1",
+ "marginTop": "20px"
+ }
+ }
+ ]
+}
diff --git a/wwwroot/llmcatelog.ui b/wwwroot/llmcatelog.ui
new file mode 100644
index 0000000..c929319
--- /dev/null
+++ b/wwwroot/llmcatelog.ui
@@ -0,0 +1,16 @@
+{
+ "widgettype": "DataViewer",
+ "options": {
+ "url": "{{entire_url('/llmage/api/llmcatelog_list.dspy')}}",
+ "title": "模型类型管理",
+ "pageSize": 20,
+ "new_data_url": "{{entire_url('/llmage/api/llmcatelog_create.dspy')}}",
+ "update_data_url": "{{entire_url('/llmage/api/llmcatelog_update.dspy')}}",
+ "delete_data_url": "{{entire_url('/llmage/api/llmcatelog_delete.dspy')}}",
+ "fields": [
+ {"name": "id", "title": "ID", "hidden": true},
+ {"name": "name", "title": "类型名", "width": "200px", "editable": true},
+ {"name": "description", "title": "类型说明", "width": "50%", "editable": true}
+ ]
+ }
+}
diff --git a/wwwroot/llmcatelog_list.ui b/wwwroot/llmcatelog_list.ui
new file mode 100644
index 0000000..c929319
--- /dev/null
+++ b/wwwroot/llmcatelog_list.ui
@@ -0,0 +1,16 @@
+{
+ "widgettype": "DataViewer",
+ "options": {
+ "url": "{{entire_url('/llmage/api/llmcatelog_list.dspy')}}",
+ "title": "模型类型管理",
+ "pageSize": 20,
+ "new_data_url": "{{entire_url('/llmage/api/llmcatelog_create.dspy')}}",
+ "update_data_url": "{{entire_url('/llmage/api/llmcatelog_update.dspy')}}",
+ "delete_data_url": "{{entire_url('/llmage/api/llmcatelog_delete.dspy')}}",
+ "fields": [
+ {"name": "id", "title": "ID", "hidden": true},
+ {"name": "name", "title": "类型名", "width": "200px", "editable": true},
+ {"name": "description", "title": "类型说明", "width": "50%", "editable": true}
+ ]
+ }
+}