From 469f89e1fc17d54efc71e592a89bef0dfb9dacf3 Mon Sep 17 00:00:00 2001 From: pccs Date: Thu, 13 Aug 2026 11:45:43 +0800 Subject: [PATCH] fix: overview cards render text not otext --- wwwroot/index.ui | 4 +- wwwroot/overview.dspy | 101 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 wwwroot/overview.dspy diff --git a/wwwroot/index.ui b/wwwroot/index.ui index b606297..21ed57f 100644 --- a/wwwroot/index.ui +++ b/wwwroot/index.ui @@ -109,7 +109,7 @@ "name": "dashboard", "label": "📊 概览", "icon": "", - "url": "{{entire_url('/pccs/overview.ui')}}", + "url": "{{entire_url('/pccs/overview.dspy')}}", "target": "app.pccs_main_content" }, { @@ -204,7 +204,7 @@ { "widgettype": "urlwidget", "options": { - "url": "{{entire_url('/pccs/overview.ui')}}", + "url": "{{entire_url('/pccs/overview.dspy')}}", "width": "100%", "height": "100%" } diff --git a/wwwroot/overview.dspy b/wwwroot/overview.dspy new file mode 100644 index 0000000..0cc6326 --- /dev/null +++ b/wwwroot/overview.dspy @@ -0,0 +1,101 @@ +def card(title, value, subtitle, bgcolor, color): + return { + 'widgettype': 'VBox', + 'options': {'bgcolor': bgcolor, 'padding': '16px', 'width': '25%', 'border': '1px solid ' + color, 'borderRadius': '8px'}, + 'subwidgets': [ + {'widgettype': 'Text', 'options': {'text': title, 'cfontsize': 0.7, 'color': '#64748b'}}, + {'widgettype': 'Text', 'options': {'text': value, 'cfontsize': 2, 'fontWeight': 'bold', 'color': color}}, + {'widgettype': 'Text', 'options': {'text': subtitle, 'cfontsize': 0.7, 'color': '#94a3b8'}} + ] + } + +pool_cards = [] +cluster_cards = [] +storage_txt = '' +image_txt = '' + +# 算力池统计 +try: + async with DBPools().sqlorContext(get_module_dbname('pcpool')) as sor: + pools = await sor.R('compute_pool', {}) + total_pools = len(pools or []) + active = sum(1 for p in (pools or []) if str(getattr(p,'status','')) == '1') + total_cpu = 0; total_gpu = 0; total_mem = 0 + alloc_cpu = 0; alloc_gpu = 0; alloc_mem = 0 + for p in (pools or []): + nodes = await sor.R('compute_node', {'pool_id': p.id}) + for n in (nodes or []): + cpu = int(getattr(n,'cpu_cores',0) or 0) + gpu = int(getattr(n,'gpu_count',0) or 0) + mem = int(getattr(n,'memory_gb',0) or 0) + total_cpu += cpu; total_gpu += gpu; total_mem += mem + if getattr(n,'status','') == 'allocated': + alloc_cpu += cpu; alloc_gpu += gpu; alloc_mem += mem + pool_cards = [ + card('算力池', str(total_pools) + ' 个', str(active) + ' 运行中', '#eff6ff', '#2563eb'), + card('CPU', str(total_cpu) + ' 核', '已分配 ' + str(alloc_cpu) + ' 核', '#f5f3ff', '#7c3aed'), + card('GPU', str(total_gpu) + ' 个', '已分配 ' + str(alloc_gpu) + ' 个', '#ecfdf5', '#059669'), + card('内存', str(total_mem) + ' GB', '已分配 ' + str(alloc_mem) + ' GB', '#fff7ed', '#ea580c') + ] +except: + pool_cards = [card('算力池', '0', '加载失败', '#f1f5f9', '#94a3b8') for _ in range(4)] + +# 集群统计 +try: + async with DBPools().sqlorContext(get_module_dbname('pcc')) as sor: + clusters = await sor.R('cluster', {}) + total = len(clusters or []) + running = sum(1 for c in (clusters or []) if getattr(c,'status','') == 'running') + deploying = sum(1 for c in (clusters or []) if getattr(c,'status','') == 'deploying') + stopped = sum(1 for c in (clusters or []) if getattr(c,'status','') == 'stopped') + total_nodes = 0; total_cpu = 0; total_gpu = 0; total_mem = 0 + for c in (clusters or []): + nodes = await sor.R('cluster_node', {'cluster_id': c.id}) + total_nodes += len(nodes or []) + for n in (nodes or []): + total_cpu += int(getattr(n,'cpu_cores',0) or 0) + total_gpu += int(getattr(n,'gpu_count',0) or 0) + total_mem += int(getattr(n,'memory_gb',0) or 0) + cluster_cards = [ + card('集群', str(total) + ' 个', str(running) + '运行/' + str(deploying) + '部署/' + str(stopped) + '停', '#eff6ff', '#2563eb'), + card('节点', str(total_nodes) + ' 个', '已加入集群', '#f5f3ff', '#7c3aed'), + card('CPU', str(total_cpu) + ' 核', '集群总核数', '#ecfdf5', '#059669'), + card('GPU/内存', str(total_gpu) + ' GPU', str(total_mem) + ' GB 内存', '#fff7ed', '#ea580c') + ] +except: + cluster_cards = [card('集群', '0', '加载失败', '#f1f5f9', '#94a3b8') for _ in range(4)] + +# 存储统计 +try: + async with DBPools().sqlorContext(get_module_dbname('storage_mgr')) as sor: + servers = await sor.R('storage_server', {}) + exports = await sor.R('storage_export', {}) + storage_txt = '存储: ' + str(len(servers or [])) + ' 服务器, ' + str(len(exports or [])) + ' 导出' +except: + storage_txt = '存储: 加载失败' + +# 镜像统计 +try: + async with DBPools().sqlorContext(get_module_dbname('image_mgr')) as sor: + registries = await sor.R('image_registry', {}) + images = await sor.R('container_image', {}) + image_txt = '镜像: ' + str(len(registries or [])) + ' 仓库, ' + str(len(images or [])) + ' 镜像' +except: + image_txt = '镜像: 加载失败' + +return { + 'widgettype': 'VBox', + 'options': {'padding': '24px', 'spacing': '24px', 'bgcolor': '#f8fafc'}, + 'subwidgets': [ + {'widgettype': 'Text', 'options': {'text': '资源概览', 'cfontsize': 1.3, 'fontWeight': 'bold', 'color': '#1e293b'}}, + {'widgettype': 'Text', 'options': {'text': '算力池', 'cfontsize': 0.9, 'fontWeight': 'bold', 'color': '#64748b'}}, + {'widgettype': 'HBox', 'options': {'gap': '16px'}, 'subwidgets': pool_cards}, + {'widgettype': 'Text', 'options': {'text': '集群', 'cfontsize': 0.9, 'fontWeight': 'bold', 'color': '#64748b'}}, + {'widgettype': 'HBox', 'options': {'gap': '16px'}, 'subwidgets': cluster_cards}, + {'widgettype': 'Text', 'options': {'text': '存储与镜像', 'cfontsize': 0.9, 'fontWeight': 'bold', 'color': '#64748b'}}, + {'widgettype': 'HBox', 'options': {'gap': '16px'}, 'subwidgets': [ + {'widgettype': 'Text', 'options': {'text': storage_txt, 'cfontsize': 0.9, 'color': '#1e293b'}}, + {'widgettype': 'Text', 'options': {'text': image_txt, 'cfontsize': 0.9, 'color': '#1e293b'}} + ]} + ] +}