diff --git a/wwwroot/knowledge_bases_list/index.ui b/wwwroot/knowledge_bases_list/index.ui index d7f60c5..0d92287 100644 --- a/wwwroot/knowledge_bases_list/index.ui +++ b/wwwroot/knowledge_bases_list/index.ui @@ -22,27 +22,15 @@ }, { "widgettype": "VBox", - "options": { - "width": "100%", - "bgcolor": "#f8f9fa", - "padding": "16px", - "css": "card", - "spacing": "8px" - }, + "options": {"width": "100%", "bgcolor": "#f8f9fa", "padding": "16px", "css": "card", "spacing": "4px"}, "subwidgets": [ - {"widgettype": "Text", "options": {"text": "💾 存储容量", "cfontsize": 14, "fontWeight": "bold", "color": "#333"}}, - { - "widgettype": "ProgressBar", - "options": { - "value": 15, - "total": 100, - "width": "100%", - "cheight": 1.2, - "bgcolor": "#e0e0e0", - "color": "#4a90d9" - } - }, - {"widgettype": "Text", "options": {"text": "已用 15MB / 总计 100MB · 15%", "cfontsize": 12, "color": "#888"}} + {"widgettype": "HBox", "options": {"width": "100%", "justifyContent": "space-between"}, "subwidgets": [ + {"widgettype": "Text", "options": {"text": "💾 存储容量", "cfontsize": 14, "fontWeight": "bold", "color": "#333"}}, + {"widgettype": "Text", "options": {"text": "已用 0MB / 总计 100MB", "cfontsize": 12, "color": "#888"}} + ]}, + {"widgettype": "VBox", "options": {"width": "100%", "cheight": 0.5, "bgcolor": "#e0e0e0", "css": ""}, "subwidgets": [ + {"widgettype": "VBox", "options": {"cwidth": 0, "cheight": 0.5, "bgcolor": "#4a90d9", "css": ""}} + ]} ] }, { diff --git a/wwwroot/tag_assign/index.dspy b/wwwroot/tag_assign/index.dspy index 5e103a3..261f554 100644 --- a/wwwroot/tag_assign/index.dspy +++ b/wwwroot/tag_assign/index.dspy @@ -125,8 +125,8 @@ async with db.sqlorContext(dbname) as sor: "options": {"url": base_url + '?kb_id=' + kb_id + '&media_type=' + mt}}]} ) -result = { - "widgettype": "VBox", + result = { + "widgettype": "VBox", "options": {"cheight": 40, "width": "100%", "padding": "16px", "spacing": "12px"}, "subwidgets": [ {"widgettype": "Text", "options": {"text": "🏷️ 标签分配", "cfontsize": 20, "fontWeight": "bold", "color": "#333"}}, @@ -154,4 +154,4 @@ result = { ] ] } -result + result diff --git a/wwwroot/tag_search/index.dspy b/wwwroot/tag_search/index.dspy index f54120b..3f1ca38 100644 --- a/wwwroot/tag_search/index.dspy +++ b/wwwroot/tag_search/index.dspy @@ -5,22 +5,20 @@ match_mode = ns.get('match_mode', 'any') query = ns.get('query', '') tag_ids = [t for t in tag_ids_str.split(',') if t] if tag_ids_str else [] +import json db = DBPools() dbname = get_module_dbname('rag') base_url = entire_url('/tag_search/index.dspy') async with db.sqlorContext(dbname) as sor: - # Get knowledge bases kbs = await sor.R('knowledge_bases', {}) kb_options = [{"value": k.id, "text": k.name} for k in kbs] - # Get all tags for selected kb tags = [] if kb_id: tag_recs = await sor.R('tags', {'kb_id': kb_id}) tags = [{"id": t.id, "name": t.name, "color": t.color} for t in tag_recs] - # --- Build tag pills --- tag_pills = [] for t in tags: is_selected = t['id'] in tag_ids @@ -35,26 +33,20 @@ async with db.sqlorContext(dbname) as sor: "widgettype": "Button", "options": { "label": ('✓ ' if is_selected else '') + t['name'], - "bgcolor": pill_bg, - "color": pill_color, - "cfontsize": 12, - "padding": "4px 12px", - "marginRight": "6px", - "marginBottom": "6px" + "bgcolor": pill_bg, "color": pill_color, + "cfontsize": 12, "padding": "4px 12px", + "marginRight": "6px", "marginBottom": "6px" }, "binds": [{ "wid": "self", "event": "click", "actiontype": "urlwidget", - "target": "app.rag_main_content", - "mode": "replace", + "target": "app.rag_main_content", "mode": "replace", "options": {"url": url} }] }) - # --- Build results --- results_widgets = [] - if tag_ids and not query: - # Tag-only search via our API + if tag_ids: from urllib.request import Request, urlopen try: api_url = 'http://localhost:9181/api/tag/search?kb_id=' + kb_id + '&tag_ids=' + tag_ids_str + '&match_mode=' + match_mode @@ -64,76 +56,47 @@ async with db.sqlorContext(dbname) as sor: if isinstance(res, list): for r in res: icon = '📄' if r['type'] == 'document' else ('👤' if r['type'] == 'face' else '🎤') - results_widgets.append({ + entry = { "widgettype": "HBox", "options": {"padding": "8px 12px", "bgcolor": "#f9fafb", "marginBottom": "4px", "alignItems": "center"}, "subwidgets": [ {"widgettype": "Text", "options": {"text": icon + ' ' + (r.get('name') or r.get('id','')), "cfontsize": 13, "color": "#333"}}, {"widgettype": "Text", "options": {"text": r['type'] + ' · ' + (r.get('file_type','')), "cfontsize": 11, "color": "#888", "css": "filler"}} ] - }) - # Show tags for this item - try: - tag_url = 'http://localhost:9181/api/tag/media_tags?media_type=' + ('document' if r['type'] in ('document','text','image','audio','video') else r['type']) + '&media_id=' + r['id'] - tag_data = json.loads(urlopen(Request(tag_url), timeout=3).read()) - item_tags = tag_data.get('tags', []) - for it in item_tags: - results_widgets[-1]['subwidgets'].append({ - "widgettype": "Text", - "options": {"text": it['name'], "cfontsize": 10, "color": "#fff", "bgcolor": it['color'], "padding": "1px 6px", "marginLeft": "4px"} - }) - except: - pass - else: - for key, items in res.items(): - if isinstance(items, list): - for r in items: - results_widgets.append({ - "widgettype": "Text", - "options": {"text": str(r.get('name') or r.get('id','')), "cfontsize": 13, "color": "#333", "padding": "4px 0"} - }) + } + results_widgets.append(entry) except Exception as e: results_widgets.append({"widgettype": "Text", "options": {"text": '搜索出错: ' + str(e), "color": "#e53e3e"}}) - if not results_widgets and (tag_ids or query): + if not results_widgets and tag_ids: results_widgets.append({"widgettype": "Text", "options": {"text": '未找到匹配的媒体文件', "color": "#888", "cfontsize": 14, "halign": "center", "padding": "40px 0"}}) - # --- Match mode toggle --- any_url = base_url + '?kb_id=' + kb_id + '&tag_ids=' + tag_ids_str + '&match_mode=any&query=' + query all_url = base_url + '?kb_id=' + kb_id + '&tag_ids=' + tag_ids_str + '&match_mode=all&query=' + query -result = { - "widgettype": "VBox", - "options": {"cheight": 40, "width": "100%", "padding": "16px", "spacing": "12px"}, - "subwidgets": [ - {"widgettype": "Text", "options": {"text": "🔍 标签检索", "cfontsize": 20, "fontWeight": "bold", "color": "#333"}}, - {"widgettype": "Text", "options": {"text": "组合标签筛选 + 语义搜索", "cfontsize": 13, "color": "#888"}}, - # KB selector - {"widgettype": "Text", "options": {"text": "知识库:", "cfontsize": 13, "color": "#555", "marginTop": "8px"}}, - {"widgettype": "HBox", "options": {"spacing": "6px", "wrap": True}, "subwidgets": [ - {"widgettype": "Button", "options": {"label": k['text'], "bgcolor": "#3b82f6" if k['value']==kb_id else "#e5e7eb", "color": "#fff" if k['value']==kb_id else "#666", "cfontsize": 12, "padding": "6px 14px", "marginRight": "4px", "marginBottom": "4px"}, - "binds": [{"wid": "self", "event": "click", "actiontype": "urlwidget", "target": "app.rag_main_content", "mode": "replace", - "options": {"url": base_url + '?kb_id=' + k['value'] + '&match_mode=' + match_mode}}]} - for k in kb_options]}, - # Tags - {"widgettype": "Text", "options": {"text": '标签 (' + str(len(tag_ids)) + ' 已选):', "cfontsize": 13, "color": "#555", "marginTop": "8px"}}, - {"widgettype": "HBox", "options": {"spacing": "4px", "wrap": True}, "subwidgets": tag_pills}, - # Match mode - {"widgettype": "HBox", "options": {"spacing": "8px", "alignItems": "center", "marginTop": "4px"}, "subwidgets": [ - {"widgettype": "Text", "options": {"text": "匹配:", "cfontsize": 13, "color": "#555"}}, - {"widgettype": "Button", "options": {"label": "任一 (OR)", "bgcolor": "#3b82f6" if match_mode=='any' else "#e5e7eb", "color": "#fff" if match_mode=='any' else "#666", "cfontsize": 12, "padding": "4px 12px"}, - "binds": [{"wid": "self", "event": "click", "actiontype": "urlwidget", "target": "app.rag_main_content", "mode": "replace", "options": {"url": any_url}}]}, - {"widgettype": "Button", "options": {"label": "全部 (AND)", "bgcolor": "#3b82f6" if match_mode=='all' else "#e5e7eb", "color": "#fff" if match_mode=='all' else "#666", "cfontsize": 12, "padding": "4px 12px"}, - "binds": [{"wid": "self", "event": "click", "actiontype": "urlwidget", "target": "app.rag_main_content", "mode": "replace", "options": {"url": all_url}}]} - ]}, - # Query input + search button (using form or simple text) - {"widgettype": "Text", "options": {"text": "语义查询 (可选):", "cfontsize": 13, "color": "#555", "marginTop": "8px"}}, - {"widgettype": "HBox", "options": {"spacing": "8px", "alignItems": "center"}, "subwidgets": [ - {"widgettype": "Text", "options": {"text": query if query else '(输入查询关键词)', "cfontsize": 13, "color": "#333" if query else "#aaa", "bgcolor": "#f9fafb", "padding": "8px 12px", "css": "filler", "border": "1px solid #e0e0e0"}} - ]}, - # Results - {"widgettype": "Text", "options": {"text": '结果 (' + str(len(results_widgets)) + '):', "cfontsize": 14, "fontWeight": "bold", "color": "#333", "marginTop": "16px"}}, - {"widgettype": "VBox", "options": {"spacing": "4px"}, "subwidgets": results_widgets} - ] -} -result + result = { + "widgettype": "VBox", + "options": {"cheight": 40, "width": "100%", "padding": "16px", "spacing": "12px"}, + "subwidgets": [ + {"widgettype": "Text", "options": {"text": "🔍 标签检索", "cfontsize": 20, "fontWeight": "bold", "color": "#333"}}, + {"widgettype": "Text", "options": {"text": "组合标签筛选 + 语义搜索", "cfontsize": 13, "color": "#888"}}, + {"widgettype": "Text", "options": {"text": "知识库:", "cfontsize": 13, "color": "#555", "marginTop": "8px"}}, + {"widgettype": "HBox", "options": {"spacing": "6px", "wrap": True}, "subwidgets": [ + {"widgettype": "Button", "options": {"label": k['text'], "bgcolor": "#3b82f6" if k['value']==kb_id else "#e5e7eb", "color": "#fff" if k['value']==kb_id else "#666", "cfontsize": 12, "padding": "6px 14px", "marginRight": "4px", "marginBottom": "4px"}, + "binds": [{"wid": "self", "event": "click", "actiontype": "urlwidget", "target": "app.rag_main_content", "mode": "replace", + "options": {"url": base_url + '?kb_id=' + k['value'] + '&match_mode=' + match_mode}}]} + for k in kb_options]}, + {"widgettype": "Text", "options": {"text": '标签 (' + str(len(tag_ids)) + ' 已选):', "cfontsize": 13, "color": "#555", "marginTop": "8px"}}, + {"widgettype": "HBox", "options": {"spacing": "4px", "wrap": True}, "subwidgets": tag_pills}, + {"widgettype": "HBox", "options": {"spacing": "8px", "alignItems": "center", "marginTop": "4px"}, "subwidgets": [ + {"widgettype": "Text", "options": {"text": "匹配:", "cfontsize": 13, "color": "#555"}}, + {"widgettype": "Button", "options": {"label": "任一 (OR)", "bgcolor": "#3b82f6" if match_mode=='any' else "#e5e7eb", "color": "#fff" if match_mode=='any' else "#666", "cfontsize": 12, "padding": "4px 12px"}, + "binds": [{"wid": "self", "event": "click", "actiontype": "urlwidget", "target": "app.rag_main_content", "mode": "replace", "options": {"url": any_url}}]}, + {"widgettype": "Button", "options": {"label": "全部 (AND)", "bgcolor": "#3b82f6" if match_mode=='all' else "#e5e7eb", "color": "#fff" if match_mode=='all' else "#666", "cfontsize": 12, "padding": "4px 12px"}, + "binds": [{"wid": "self", "event": "click", "actiontype": "urlwidget", "target": "app.rag_main_content", "mode": "replace", "options": {"url": all_url}}]} + ]}, + {"widgettype": "Text", "options": {"text": "结果 (" + str(len(results_widgets)) + "):", "cfontsize": 14, "fontWeight": "bold", "color": "#333", "marginTop": "16px"}}, + {"widgettype": "VBox", "options": {"spacing": "4px"}, "subwidgets": results_widgets} + ] + } + result diff --git a/wwwroot/tags_list/index.ui b/wwwroot/tags_list/index.ui new file mode 100644 index 0000000..a2b6eb8 --- /dev/null +++ b/wwwroot/tags_list/index.ui @@ -0,0 +1,32 @@ +{ + "widgettype": "VBox", + "options": {"cheight": 40, "width": "100%", "padding": "16px", "spacing": "12px"}, + "subwidgets": [ + {"widgettype": "Text", "options": {"text": "🏷️ 标签管理", "cfontsize": 22, "fontWeight": "bold", "color": "#333"}}, + {"widgettype": "Text", "options": {"text": "创建和管理知识库标签", "cfontsize": 14, "color": "#888"}}, + { + "widgettype": "Tabular", + "options": { + "dataurl": "{{entire_url('/tags_list/data.dspy')}}", + "title": "标签列表", + "height": "70vh", + "browserfields": [ + {"field": "name", "title": "标签名", "width": "25%"}, + {"field": "color", "title": "颜色", "width": "15%"}, + {"field": "kb_id", "title": "知识库", "width": "30%"}, + {"field": "created_at", "title": "创建时间", "width": "30%"} + ], + "editable": { + "fields": [ + {"field": "name", "title": "标签名", "uitype": "str", "label": "标签名", "required": true}, + {"field": "kb_id", "title": "知识库ID", "uitype": "str", "label": "知识库ID", "required": true}, + {"field": "color", "title": "颜色", "uitype": "str", "label": "颜色(#hex)", "default": "#3b82f6"} + ], + "add_url": "{{entire_url('/tags_list/add.dspy')}}", + "update_url": "{{entire_url('/tags_list/update.dspy')}}", + "delete_url": "{{entire_url('/tags_list/delete.dspy')}}" + } + } + } + ] +}