fix dashboard: clickable quick entries, full-data ranking tables, dark bg

1. Quick entry shortcuts: Replace VBox (no native click) with Button
   widgets for reliable click event handling. Each entry wraps its
   icon+text in a VBox inside the Button for layout.

2. Ranking tables: Changed get_top_users/ providers queries from
   today-only (WHERE use_date=today) to all-time data so transaction
   counts and amounts always display meaningful results.

3. Background color: Added bgcolor=#0B1120 to top-level index.ui VBox
   so the page background matches the shell theme, eliminating the
   white-vs-dark-blue contrast.
This commit is contained in:
yumoqing 2026-05-26 16:06:54 +08:00
parent 95d18e7ce0
commit 8ceb769356
3 changed files with 99 additions and 74 deletions

View File

@ -95,9 +95,8 @@ async def get_accounting_errors(request):
async def get_top_users_by_amount(request): async def get_top_users_by_amount(request):
"""获取当天用户金额前5""" """获取用户金额前5(全量)"""
env = request._run_ns env = request._run_ns
today = env.curDateString()
async with get_sor_context(env, 'sage') as sor: async with get_sor_context(env, 'sage') as sor:
sql = """ sql = """
SELECT SELECT
@ -106,12 +105,11 @@ async def get_top_users_by_amount(request):
COUNT(*) as cnt COUNT(*) as cnt
FROM llmusage a FROM llmusage a
LEFT JOIN users b ON a.userid = b.id LEFT JOIN users b ON a.userid = b.id
WHERE a.use_date = ${today}$
GROUP BY a.userid, b.nick_name, b.username GROUP BY a.userid, b.nick_name, b.username
ORDER BY total_amount DESC ORDER BY total_amount DESC
LIMIT 5 LIMIT 5
""" """
recs = await sor.sqlExe(sql, {'today': today}) recs = await sor.sqlExe(sql, {})
result = [] result = []
for r in recs: for r in recs:
result.append({ result.append({
@ -123,9 +121,8 @@ async def get_top_users_by_amount(request):
async def get_top_users_by_count(request): async def get_top_users_by_count(request):
"""获取当天用户笔数前5""" """获取用户笔数前5(全量)"""
env = request._run_ns env = request._run_ns
today = env.curDateString()
async with get_sor_context(env, 'sage') as sor: async with get_sor_context(env, 'sage') as sor:
sql = """ sql = """
SELECT SELECT
@ -134,12 +131,11 @@ async def get_top_users_by_count(request):
COALESCE(SUM(a.amount), 0) as total_amount COALESCE(SUM(a.amount), 0) as total_amount
FROM llmusage a FROM llmusage a
LEFT JOIN users b ON a.userid = b.id LEFT JOIN users b ON a.userid = b.id
WHERE a.use_date = ${today}$
GROUP BY a.userid, b.nick_name, b.username GROUP BY a.userid, b.nick_name, b.username
ORDER BY cnt DESC ORDER BY cnt DESC
LIMIT 5 LIMIT 5
""" """
recs = await sor.sqlExe(sql, {'today': today}) recs = await sor.sqlExe(sql, {})
result = [] result = []
for r in recs: for r in recs:
result.append({ result.append({
@ -151,9 +147,8 @@ async def get_top_users_by_count(request):
async def get_top_providers_by_amount(request): async def get_top_providers_by_amount(request):
"""获取模型供应商金额前5""" """获取模型供应商金额前5(全量)"""
env = request._run_ns env = request._run_ns
today = env.curDateString()
async with get_sor_context(env, 'sage') as sor: async with get_sor_context(env, 'sage') as sor:
sql = """ sql = """
SELECT SELECT
@ -163,12 +158,11 @@ async def get_top_providers_by_amount(request):
FROM llmusage a FROM llmusage a
LEFT JOIN llm b ON a.llmid = b.id LEFT JOIN llm b ON a.llmid = b.id
LEFT JOIN organization c ON b.providerid = c.id LEFT JOIN organization c ON b.providerid = c.id
WHERE a.use_date = ${today}$
GROUP BY b.providerid, c.orgname GROUP BY b.providerid, c.orgname
ORDER BY total_amount DESC ORDER BY total_amount DESC
LIMIT 5 LIMIT 5
""" """
recs = await sor.sqlExe(sql, {'today': today}) recs = await sor.sqlExe(sql, {})
result = [] result = []
for r in recs: for r in recs:
result.append({ result.append({
@ -180,9 +174,8 @@ async def get_top_providers_by_amount(request):
async def get_top_providers_by_count(request): async def get_top_providers_by_count(request):
"""获取模型供应商笔数前5""" """获取模型供应商笔数前5(全量)"""
env = request._run_ns env = request._run_ns
today = env.curDateString()
async with get_sor_context(env, 'sage') as sor: async with get_sor_context(env, 'sage') as sor:
sql = """ sql = """
SELECT SELECT
@ -192,12 +185,11 @@ async def get_top_providers_by_count(request):
FROM llmusage a FROM llmusage a
LEFT JOIN llm b ON a.llmid = b.id LEFT JOIN llm b ON a.llmid = b.id
LEFT JOIN organization c ON b.providerid = c.id LEFT JOIN organization c ON b.providerid = c.id
WHERE a.use_date = ${today}$
GROUP BY b.providerid, c.orgname GROUP BY b.providerid, c.orgname
ORDER BY cnt DESC ORDER BY cnt DESC
LIMIT 5 LIMIT 5
""" """
recs = await sor.sqlExe(sql, {'today': today}) recs = await sor.sqlExe(sql, {})
result = [] result = []
for r in recs: for r in recs:
result.append({ result.append({

View File

@ -11,7 +11,7 @@
"name": "dashboard", "name": "dashboard",
"label": "仪表盘", "label": "仪表盘",
"icon": "fa fa-dashboard", "icon": "fa fa-dashboard",
"url": "{{entire_url('index.ui')}}", "url": "{{entire_url('/dashboard_for_sage/index.ui')}}",
"target": "app.sage_main_content" "target": "app.sage_main_content"
}, },
{% if get_user() %} {% if get_user() %}

View File

@ -2,7 +2,8 @@
"widgettype": "VBox", "widgettype": "VBox",
"options": { "options": {
"width": "100%", "width": "100%",
"height": "100%" "height": "100%",
"bgcolor": "#0B1120"
}, },
"subwidgets": [ "subwidgets": [
{ {
@ -216,12 +217,12 @@
}, },
"subwidgets": [ "subwidgets": [
{ {
"widgettype": "VBox", "widgettype": "Button",
"options": { "options": {
"bgcolor": "#334155", "bgcolor": "#334155",
"padding": "16px", "padding": "16px",
"borderRadius": "8px", "borderRadius": "8px",
"cursor": "pointer", "border": "none",
"textAlign": "center" "textAlign": "center"
}, },
"binds": [ "binds": [
@ -236,6 +237,12 @@
"mode": "replace" "mode": "replace"
} }
], ],
"subwidgets": [
{
"widgettype": "VBox",
"options": {
"alignItems": "center"
},
"subwidgets": [ "subwidgets": [
{ {
"widgettype": "Svg", "widgettype": "Svg",
@ -253,14 +260,16 @@
} }
} }
] ]
}
]
}, },
{ {
"widgettype": "VBox", "widgettype": "Button",
"options": { "options": {
"bgcolor": "#334155", "bgcolor": "#334155",
"padding": "16px", "padding": "16px",
"borderRadius": "8px", "borderRadius": "8px",
"cursor": "pointer", "border": "none",
"textAlign": "center" "textAlign": "center"
}, },
"binds": [ "binds": [
@ -275,6 +284,12 @@
"mode": "replace" "mode": "replace"
} }
], ],
"subwidgets": [
{
"widgettype": "VBox",
"options": {
"alignItems": "center"
},
"subwidgets": [ "subwidgets": [
{ {
"widgettype": "Svg", "widgettype": "Svg",
@ -292,14 +307,16 @@
} }
} }
] ]
}
]
}, },
{ {
"widgettype": "VBox", "widgettype": "Button",
"options": { "options": {
"bgcolor": "#334155", "bgcolor": "#334155",
"padding": "16px", "padding": "16px",
"borderRadius": "8px", "borderRadius": "8px",
"cursor": "pointer", "border": "none",
"textAlign": "center" "textAlign": "center"
}, },
"binds": [ "binds": [
@ -314,6 +331,12 @@
"mode": "replace" "mode": "replace"
} }
], ],
"subwidgets": [
{
"widgettype": "VBox",
"options": {
"alignItems": "center"
},
"subwidgets": [ "subwidgets": [
{ {
"widgettype": "Svg", "widgettype": "Svg",
@ -331,14 +354,16 @@
} }
} }
] ]
}
]
}, },
{ {
"widgettype": "VBox", "widgettype": "Button",
"options": { "options": {
"bgcolor": "#334155", "bgcolor": "#334155",
"padding": "16px", "padding": "16px",
"borderRadius": "8px", "borderRadius": "8px",
"cursor": "pointer", "border": "none",
"textAlign": "center" "textAlign": "center"
}, },
"binds": [ "binds": [
@ -353,6 +378,12 @@
"mode": "replace" "mode": "replace"
} }
], ],
"subwidgets": [
{
"widgettype": "VBox",
"options": {
"alignItems": "center"
},
"subwidgets": [ "subwidgets": [
{ {
"widgettype": "Svg", "widgettype": "Svg",
@ -376,6 +407,8 @@
] ]
} }
] ]
}
]
}, },
{ {
"widgettype": "VBox", "widgettype": "VBox",