feat: modern UI redesign - shell layout, theme switching, dashboard revamp
Phase 1-2 deliverables: - shell.ui: Global layout framework (topbar + collapsible sidebar + main content) - shell_theme.css: Dark/light theme CSS custom properties system - shell_theme.js: Theme toggle + sidebar collapse with localStorage persistence - global_menu.ui: Unified module navigation menu with RBAC visibility - index.ui: Redesigned dashboard homepage with modern stat cards + quick links - Stat card widgets: today_usage, today_amount, total_users, concurrent, errors - chart_top_models.ui + api/top_models.dspy: ChartBar with data_url pattern - table_top_users_amount.ui: Jinja2-rendered user ranking table - build.sh: Added .css file linking support Design system: - Dark theme (default): slate color palette (#0B1120, #111827, #1E293B) - Light theme: clean white palette with matching structure - Theme persisted in localStorage, toggled via topbar button - Sidebar collapsible with icon-only mode, state persisted in localStorage - Responsive stat cards with hover effects and trend indicators - Quick link cards for model management, users, knowledge base, errors
This commit is contained in:
parent
753887a4e2
commit
ffdc7fc983
4
build.sh
4
build.sh
@ -34,8 +34,8 @@ SAGE_MODULE_WWWROOT="$WWWROOT/$MODULE_NAME"
|
|||||||
echo "Linking wwwroot..."
|
echo "Linking wwwroot..."
|
||||||
mkdir -p "$SAGE_MODULE_WWWROOT/api"
|
mkdir -p "$SAGE_MODULE_WWWROOT/api"
|
||||||
|
|
||||||
# Link all .ui and .js files (must be at wwwroot root per bricks convention)
|
# Link all .ui, .js, and .css files (must be at wwwroot root per bricks convention)
|
||||||
for f in "$MODULE_WWWROOT"/*.ui "$MODULE_WWWROOT"/*.js; do
|
for f in "$MODULE_WWWROOT"/*.ui "$MODULE_WWWROOT"/*.js "$MODULE_WWWROOT"/*.css; do
|
||||||
[ -f "$f" ] && ln -sf "$f" "$SAGE_MODULE_WWWROOT/"
|
[ -f "$f" ] && ln -sf "$f" "$SAGE_MODULE_WWWROOT/"
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|||||||
@ -2,6 +2,7 @@ README.md
|
|||||||
pyproject.toml
|
pyproject.toml
|
||||||
dashboard_for_sage/__init__.py
|
dashboard_for_sage/__init__.py
|
||||||
dashboard_for_sage/init.py
|
dashboard_for_sage/init.py
|
||||||
|
dashboard_for_sage/load_dashboard.py
|
||||||
dashboard_for_sage.egg-info/PKG-INFO
|
dashboard_for_sage.egg-info/PKG-INFO
|
||||||
dashboard_for_sage.egg-info/SOURCES.txt
|
dashboard_for_sage.egg-info/SOURCES.txt
|
||||||
dashboard_for_sage.egg-info/dependency_links.txt
|
dashboard_for_sage.egg-info/dependency_links.txt
|
||||||
|
|||||||
6
wwwroot/api/top_models.dspy
Normal file
6
wwwroot/api/top_models.dspy
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# coding=utf-8
|
||||||
|
"""Top models data API for ChartBar"""
|
||||||
|
import json
|
||||||
|
|
||||||
|
models = await get_top_models(request)
|
||||||
|
print(json.dumps(models))
|
||||||
10
wwwroot/chart_top_models.ui
Normal file
10
wwwroot/chart_top_models.ui
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"widgettype": "ChartBar",
|
||||||
|
"options": {
|
||||||
|
"height": "280px",
|
||||||
|
"width": "100%",
|
||||||
|
"data_url": "{{entire_url('api/top_models.dspy')}}",
|
||||||
|
"nameField": "model_name",
|
||||||
|
"valueFields": ["cnt", "total_amount"]
|
||||||
|
}
|
||||||
|
}
|
||||||
58
wwwroot/global_menu.ui
Normal file
58
wwwroot/global_menu.ui
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
{% set roles = get_user_roles(get_user()) %}
|
||||||
|
{
|
||||||
|
"widgettype": "Menu",
|
||||||
|
"id": "global_nav_menu",
|
||||||
|
"options": {
|
||||||
|
"width": "100%",
|
||||||
|
"height": "100%",
|
||||||
|
"bgcolor": "#111827",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"name": "dashboard",
|
||||||
|
"label": "仪表盘",
|
||||||
|
"icon": "fa fa-dashboard",
|
||||||
|
"url": "{{entire_url('index.ui')}}",
|
||||||
|
"target": "app.sage_main_content"
|
||||||
|
},
|
||||||
|
{% if get_user() %}
|
||||||
|
{
|
||||||
|
"name": "llmage",
|
||||||
|
"label": "LLM 模型管理",
|
||||||
|
"icon": "fa fa-brain",
|
||||||
|
"submenu": "{{entire_url('/llmage/menu.ui')}}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "rag",
|
||||||
|
"label": "知识库管理",
|
||||||
|
"icon": "fa fa-database",
|
||||||
|
"url": "{{entire_url('/rag/menu.ui')}}",
|
||||||
|
"target": "app.sage_main_content"
|
||||||
|
},
|
||||||
|
{% endif %}
|
||||||
|
{% if 'reseller.operator' in roles or 'owner.superuser' in roles %}
|
||||||
|
{
|
||||||
|
"name": "platformbiz",
|
||||||
|
"label": "平台业务",
|
||||||
|
"icon": "fa fa-building",
|
||||||
|
"submenu": "{{entire_url('/platformbiz/menu.ui')}}"
|
||||||
|
},
|
||||||
|
{% endif %}
|
||||||
|
{% if get_user() %}
|
||||||
|
{
|
||||||
|
"name": "rbac",
|
||||||
|
"label": "用户与权限",
|
||||||
|
"icon": "fa fa-users",
|
||||||
|
"submenu": "{{entire_url('/rbac/admin_menu.ui')}}"
|
||||||
|
},
|
||||||
|
{% endif %}
|
||||||
|
{
|
||||||
|
"name": "hermes_web_cli",
|
||||||
|
"label": "AI Agent",
|
||||||
|
"icon": "fa fa-robot",
|
||||||
|
"url": "{{entire_url('/hermes-web-cli/index.ui')}}",
|
||||||
|
"target": "app.sage_main_content"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"menuitem_css": "menuitem"
|
||||||
|
}
|
||||||
|
}
|
||||||
350
wwwroot/index.ui
350
wwwroot/index.ui
@ -2,66 +2,155 @@
|
|||||||
"widgettype": "VBox",
|
"widgettype": "VBox",
|
||||||
"options": {
|
"options": {
|
||||||
"width": "100%",
|
"width": "100%",
|
||||||
"height": "100%",
|
"height": "100%"
|
||||||
"padding": "20px",
|
|
||||||
"bgcolor": "#f0f2f5"
|
|
||||||
},
|
},
|
||||||
"subwidgets": [
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "HBox",
|
||||||
|
"options": {
|
||||||
|
"width": "100%",
|
||||||
|
"alignItems": "center",
|
||||||
|
"marginBottom": "24px"
|
||||||
|
},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "Title2",
|
||||||
|
"options": {
|
||||||
|
"text": "数据概览",
|
||||||
|
"color": "#F1F5F9",
|
||||||
|
"fontWeight": "700"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Filler"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"widgettype": "Text",
|
"widgettype": "Text",
|
||||||
"options": {
|
"options": {
|
||||||
"text": "Dashboard",
|
"text": "最后更新: {{get_today_usage(request) and request._run_ns.curDateString() or ''}}",
|
||||||
"fontSize": "24px",
|
"fontSize": "13px",
|
||||||
"fontWeight": "bold",
|
"color": "#64748B"
|
||||||
"color": "#333",
|
|
||||||
"marginBottom": "20px"
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"widgettype": "ResponsableBox",
|
"widgettype": "ResponsableBox",
|
||||||
"options": {
|
"options": {
|
||||||
"gap": "16px",
|
"gap": "16px",
|
||||||
"minWidth": "250px"
|
"minWidth": "220px",
|
||||||
|
"marginBottom": "24px"
|
||||||
},
|
},
|
||||||
"subwidgets": [
|
"subwidgets": [
|
||||||
{
|
{
|
||||||
"widgettype": "RefreshWidget",
|
"widgettype": "RefreshWidget",
|
||||||
"id": "refresh_today_usage",
|
"id": "stat_today_usage",
|
||||||
"options": {
|
"options": {
|
||||||
"period_seconds": 10,
|
"period_seconds": 30,
|
||||||
"url": "{{entire_url('today_usage.ui')}}"
|
"url": "{{entire_url('stat_today_usage.ui')}}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"widgettype": "RefreshWidget",
|
"widgettype": "RefreshWidget",
|
||||||
"id": "refresh_today_amount",
|
"id": "stat_today_amount",
|
||||||
"options": {
|
"options": {
|
||||||
"period_seconds": 10,
|
"period_seconds": 30,
|
||||||
"url": "{{entire_url('today_amount.ui')}}"
|
"url": "{{entire_url('stat_today_amount.ui')}}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"widgettype": "RefreshWidget",
|
"widgettype": "RefreshWidget",
|
||||||
"id": "refresh_total_users",
|
"id": "stat_total_users",
|
||||||
"options": {
|
"options": {
|
||||||
"period_seconds": 10,
|
"period_seconds": 60,
|
||||||
"url": "{{entire_url('total_users.ui')}}"
|
"url": "{{entire_url('stat_total_users.ui')}}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"widgettype": "RefreshWidget",
|
"widgettype": "RefreshWidget",
|
||||||
"id": "refresh_concurrent_users",
|
"id": "stat_concurrent",
|
||||||
"options": {
|
"options": {
|
||||||
"period_seconds": 10,
|
"period_seconds": 15,
|
||||||
"url": "{{entire_url('concurrent_users.ui')}}"
|
"url": "{{entire_url('stat_concurrent.ui')}}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"widgettype": "RefreshWidget",
|
"widgettype": "RefreshWidget",
|
||||||
"id": "refresh_accounting_errors",
|
"id": "stat_errors",
|
||||||
"options": {
|
"options": {
|
||||||
"period_seconds": 10,
|
"period_seconds": 30,
|
||||||
"url": "{{entire_url('accounting_errors.ui')}}"
|
"url": "{{entire_url('stat_errors.ui')}}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "HBox",
|
||||||
|
"options": {
|
||||||
|
"width": "100%",
|
||||||
|
"gap": "20px",
|
||||||
|
"height": "auto"
|
||||||
|
},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "VBox",
|
||||||
|
"options": {
|
||||||
|
"width": "60%",
|
||||||
|
"bgcolor": "#1E293B",
|
||||||
|
"borderRadius": "12px",
|
||||||
|
"padding": "20px",
|
||||||
|
"border": "1px solid #334155"
|
||||||
|
},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "HBox",
|
||||||
|
"options": {
|
||||||
|
"width": "100%",
|
||||||
|
"alignItems": "center",
|
||||||
|
"marginBottom": "16px"
|
||||||
|
},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "Title4",
|
||||||
|
"options": {
|
||||||
|
"text": "Top 3 模型(今日调用)",
|
||||||
|
"color": "#F1F5F9",
|
||||||
|
"fontWeight": "600"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Filler"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Button",
|
||||||
|
"options": {
|
||||||
|
"label": "刷新",
|
||||||
|
"bgcolor": "#334155",
|
||||||
|
"color": "#94A3B8",
|
||||||
|
"border": "none",
|
||||||
|
"borderRadius": "6px",
|
||||||
|
"padding": "4px 12px",
|
||||||
|
"fontSize": "12px"
|
||||||
|
},
|
||||||
|
"binds": [
|
||||||
|
{
|
||||||
|
"wid": "self",
|
||||||
|
"event": "click",
|
||||||
|
"actiontype": "method",
|
||||||
|
"target": "-@RefreshWidget",
|
||||||
|
"method": "render_urldata",
|
||||||
|
"params": {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "RefreshWidget",
|
||||||
|
"id": "chart_top_models",
|
||||||
|
"options": {
|
||||||
|
"period_seconds": 30,
|
||||||
|
"url": "{{entire_url('chart_top_models.ui')}}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -69,30 +158,217 @@
|
|||||||
{
|
{
|
||||||
"widgettype": "VBox",
|
"widgettype": "VBox",
|
||||||
"options": {
|
"options": {
|
||||||
"bgcolor": "#FFFFFF",
|
"width": "40%",
|
||||||
"padding": "24px",
|
"bgcolor": "#1E293B",
|
||||||
"borderRadius": "8px",
|
"borderRadius": "12px",
|
||||||
"marginTop": "20px",
|
"padding": "20px",
|
||||||
"minHeight": "350px",
|
"border": "1px solid #334155"
|
||||||
"boxShadow": "0 2px 8px rgba(0,0,0,0.1)"
|
|
||||||
},
|
},
|
||||||
"subwidgets": [
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "Title4",
|
||||||
|
"options": {
|
||||||
|
"text": "快捷入口",
|
||||||
|
"color": "#F1F5F9",
|
||||||
|
"fontWeight": "600",
|
||||||
|
"marginBottom": "16px"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "ResponsableBox",
|
||||||
|
"options": {
|
||||||
|
"gap": "12px",
|
||||||
|
"minWidth": "120px"
|
||||||
|
},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "VBox",
|
||||||
|
"options": {
|
||||||
|
"bgcolor": "#334155",
|
||||||
|
"padding": "16px",
|
||||||
|
"borderRadius": "8px",
|
||||||
|
"cursor": "pointer",
|
||||||
|
"textAlign": "center"
|
||||||
|
},
|
||||||
|
"binds": [
|
||||||
|
{
|
||||||
|
"wid": "self",
|
||||||
|
"event": "click",
|
||||||
|
"actiontype": "urlwidget",
|
||||||
|
"target": "app.sage_main_content",
|
||||||
|
"options": {
|
||||||
|
"url": "{{entire_url('/llmage/llm')}}"
|
||||||
|
},
|
||||||
|
"mode": "replace"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "Svg",
|
||||||
|
"options": {
|
||||||
|
"svg": "<svg width=\"28\" height=\"28\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#90caf9\" stroke-width=\"2\"><path d=\"M9.75 3.104v5.714a2.25 2.25 0 01-.659 1.591L5 14.5M9.75 3.104c-.251.023-.501.05-.75.082m.75-.082a24.301 24.301 0 014.5 0m0 0v5.714c0 .597.237 1.17.659 1.591L19.8 15.3M14.25 3.104c.251.023.501.05.75.082M19.8 15.3l-1.57.393A9.065 9.065 0 0112 15.75c-2.062 0-4.024-.614-5.67-1.757l-1.57-.393m15.04 0L12 21 5.25 13.893\"/></svg>"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"widgettype": "Text",
|
"widgettype": "Text",
|
||||||
"options": {
|
"options": {
|
||||||
"text": "Top 3 模型(今日)",
|
"text": "模型管理",
|
||||||
"fontSize": "18px",
|
"color": "#E2E8F0",
|
||||||
"fontWeight": "bold",
|
"fontSize": "13px",
|
||||||
"color": "#333",
|
"marginTop": "8px"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "VBox",
|
||||||
|
"options": {
|
||||||
|
"bgcolor": "#334155",
|
||||||
|
"padding": "16px",
|
||||||
|
"borderRadius": "8px",
|
||||||
|
"cursor": "pointer",
|
||||||
|
"textAlign": "center"
|
||||||
|
},
|
||||||
|
"binds": [
|
||||||
|
{
|
||||||
|
"wid": "self",
|
||||||
|
"event": "click",
|
||||||
|
"actiontype": "urlwidget",
|
||||||
|
"target": "app.sage_main_content",
|
||||||
|
"options": {
|
||||||
|
"url": "{{entire_url('/rbac/users')}}"
|
||||||
|
},
|
||||||
|
"mode": "replace"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "Svg",
|
||||||
|
"options": {
|
||||||
|
"svg": "<svg width=\"28\" height=\"28\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#4caf50\" stroke-width=\"2\"><path d=\"M15 19.128a9.38 9.38 0 002.625.372 9.337 9.337 0 004.121-.952 4.125 4.125 0 00-7.533-2.493M15 19.128v-.003c0-1.113-.285-2.16-.786-3.07M15 19.128v.106A12.318 12.318 0 018.624 21c-2.331 0-4.512-.645-6.374-1.766l-.001-.109a6.375 6.375 0 0111.964-3.07M12 6.375a3.375 3.375 0 11-6.75 0 3.375 3.375 0 016.75 0zm8.25 2.25a2.625 2.625 0 11-5.25 0 2.625 2.625 0 015.25 0z\"/></svg>"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {
|
||||||
|
"text": "用户管理",
|
||||||
|
"color": "#E2E8F0",
|
||||||
|
"fontSize": "13px",
|
||||||
|
"marginTop": "8px"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "VBox",
|
||||||
|
"options": {
|
||||||
|
"bgcolor": "#334155",
|
||||||
|
"padding": "16px",
|
||||||
|
"borderRadius": "8px",
|
||||||
|
"cursor": "pointer",
|
||||||
|
"textAlign": "center"
|
||||||
|
},
|
||||||
|
"binds": [
|
||||||
|
{
|
||||||
|
"wid": "self",
|
||||||
|
"event": "click",
|
||||||
|
"actiontype": "urlwidget",
|
||||||
|
"target": "app.sage_main_content",
|
||||||
|
"options": {
|
||||||
|
"url": "{{entire_url('/rag/kdb')}}"
|
||||||
|
},
|
||||||
|
"mode": "replace"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "Svg",
|
||||||
|
"options": {
|
||||||
|
"svg": "<svg width=\"28\" height=\"28\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#f59e0b\" stroke-width=\"2\"><path d=\"M20.25 6.375c0 2.278-3.694 4.125-8.25 4.125S3.75 8.653 3.75 6.375m16.5 0c0-2.278-3.694-4.125-8.25-4.125S3.75 4.097 3.75 6.375m16.5 0v11.25c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125V6.375m16.5 0v3.75m-16.5-3.75v3.75m16.5 0v3.75C20.25 16.153 16.556 18 12 18s-8.25-1.847-8.25-4.125v-3.75m16.5 0c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125\"/></svg>"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {
|
||||||
|
"text": "知识库",
|
||||||
|
"color": "#E2E8F0",
|
||||||
|
"fontSize": "13px",
|
||||||
|
"marginTop": "8px"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "VBox",
|
||||||
|
"options": {
|
||||||
|
"bgcolor": "#334155",
|
||||||
|
"padding": "16px",
|
||||||
|
"borderRadius": "8px",
|
||||||
|
"cursor": "pointer",
|
||||||
|
"textAlign": "center"
|
||||||
|
},
|
||||||
|
"binds": [
|
||||||
|
{
|
||||||
|
"wid": "self",
|
||||||
|
"event": "click",
|
||||||
|
"actiontype": "urlwidget",
|
||||||
|
"target": "app.sage_main_content",
|
||||||
|
"options": {
|
||||||
|
"url": "{{entire_url('/llmage/failed_accounting.ui')}}"
|
||||||
|
},
|
||||||
|
"mode": "replace"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "Svg",
|
||||||
|
"options": {
|
||||||
|
"svg": "<svg width=\"28\" height=\"28\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#ef4444\" stroke-width=\"2\"><path d=\"M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z\"/></svg>"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {
|
||||||
|
"text": "异常记录",
|
||||||
|
"color": "#E2E8F0",
|
||||||
|
"fontSize": "13px",
|
||||||
|
"marginTop": "8px"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "VBox",
|
||||||
|
"options": {
|
||||||
|
"width": "100%",
|
||||||
|
"bgcolor": "#1E293B",
|
||||||
|
"borderRadius": "12px",
|
||||||
|
"padding": "20px",
|
||||||
|
"border": "1px solid #334155",
|
||||||
|
"marginTop": "20px"
|
||||||
|
},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "Title4",
|
||||||
|
"options": {
|
||||||
|
"text": "用户消费排行(Top 5)",
|
||||||
|
"color": "#F1F5F9",
|
||||||
|
"fontWeight": "600",
|
||||||
"marginBottom": "16px"
|
"marginBottom": "16px"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"widgettype": "RefreshWidget",
|
"widgettype": "RefreshWidget",
|
||||||
"id": "refresh_top_models_chart",
|
"id": "table_top_users",
|
||||||
"options": {
|
"options": {
|
||||||
"period_seconds": 10,
|
"period_seconds": 30,
|
||||||
"url": "{{entire_url('top_models_chart.ui')}}"
|
"url": "{{entire_url('table_top_users.ui')}}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
{
|
{
|
||||||
"name": "dashboard",
|
"name": "dashboard",
|
||||||
"label": "数据看板",
|
"label": "数据看板",
|
||||||
"url": "{{entire_url('index.ui')}}"
|
"url": "{{entire_url('shell.ui')}}"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
147
wwwroot/shell.ui
Normal file
147
wwwroot/shell.ui
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
{
|
||||||
|
"widgettype": "VBox",
|
||||||
|
"options": {
|
||||||
|
"width": "100%",
|
||||||
|
"height": "100%",
|
||||||
|
"bgcolor": "#0B1120"
|
||||||
|
},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "Html",
|
||||||
|
"options": {
|
||||||
|
"html": "<link rel=\"stylesheet\" href=\"/dashboard_for_sage/shell_theme.css\"><script src=\"/dashboard_for_sage/shell_theme.js\"><\\/script>"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "HBox",
|
||||||
|
"options": {
|
||||||
|
"width": "100%",
|
||||||
|
"height": "56px",
|
||||||
|
"bgcolor": "#111827",
|
||||||
|
"borderBottom": "1px solid #334155",
|
||||||
|
"padding": "0 16px",
|
||||||
|
"alignItems": "center"
|
||||||
|
},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "Button",
|
||||||
|
"id": "sidebar_toggle_btn",
|
||||||
|
"options": {
|
||||||
|
"label": "",
|
||||||
|
"bgcolor": "transparent",
|
||||||
|
"color": "#94A3B8",
|
||||||
|
"border": "1px solid #334155",
|
||||||
|
"borderRadius": "8px",
|
||||||
|
"width": "36px",
|
||||||
|
"height": "36px",
|
||||||
|
"padding": "0"
|
||||||
|
},
|
||||||
|
"binds": [
|
||||||
|
{
|
||||||
|
"wid": "self",
|
||||||
|
"event": "click",
|
||||||
|
"actiontype": "script",
|
||||||
|
"target": "self",
|
||||||
|
"script": "sageToggleSidebar()"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Image",
|
||||||
|
"options": {
|
||||||
|
"url": "{{entire_url('/imgs/msp.png')}}",
|
||||||
|
"height": "32px",
|
||||||
|
"marginLeft": "12px"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Title4",
|
||||||
|
"options": {
|
||||||
|
"text": "Sage",
|
||||||
|
"color": "#F1F5F9",
|
||||||
|
"fontWeight": "bold",
|
||||||
|
"marginLeft": "8px"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Filler"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Button",
|
||||||
|
"id": "theme_toggle_btn",
|
||||||
|
"options": {
|
||||||
|
"label": "",
|
||||||
|
"bgcolor": "transparent",
|
||||||
|
"color": "#94A3B8",
|
||||||
|
"border": "1px solid #334155",
|
||||||
|
"borderRadius": "50%",
|
||||||
|
"width": "36px",
|
||||||
|
"height": "36px",
|
||||||
|
"padding": "0"
|
||||||
|
},
|
||||||
|
"binds": [
|
||||||
|
{
|
||||||
|
"wid": "self",
|
||||||
|
"event": "click",
|
||||||
|
"actiontype": "script",
|
||||||
|
"target": "self",
|
||||||
|
"script": "sageToggleTheme()"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "urlwidget",
|
||||||
|
"options": {
|
||||||
|
"url": "{{entire_url('/rbac/user/user_panel.ui')}}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "HBox",
|
||||||
|
"options": {
|
||||||
|
"width": "100%",
|
||||||
|
"height": "calc(100% - 56px)"
|
||||||
|
},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "VBox",
|
||||||
|
"id": "sage_sidebar",
|
||||||
|
"options": {
|
||||||
|
"width": "240px",
|
||||||
|
"height": "100%",
|
||||||
|
"bgcolor": "#111827",
|
||||||
|
"borderRight": "1px solid #334155"
|
||||||
|
},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "urlwidget",
|
||||||
|
"options": {
|
||||||
|
"url": "{{entire_url('global_menu.ui')}}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "VBox",
|
||||||
|
"id": "sage_main_content",
|
||||||
|
"options": {
|
||||||
|
"css": "filler",
|
||||||
|
"height": "100%",
|
||||||
|
"padding": "24px"
|
||||||
|
},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {
|
||||||
|
"text": "加载中...",
|
||||||
|
"fontSize": "16px",
|
||||||
|
"color": "#94A3B8"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
393
wwwroot/shell_theme.css
Normal file
393
wwwroot/shell_theme.css
Normal file
@ -0,0 +1,393 @@
|
|||||||
|
/* Sage Modern UI Theme System
|
||||||
|
Dark/Light theme switching via CSS custom properties
|
||||||
|
Applied to <html> element data-theme attribute
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* ===== Dark Theme (default) ===== */
|
||||||
|
:root,
|
||||||
|
[data-theme="dark"] {
|
||||||
|
--sage-bg-primary: #0B1120;
|
||||||
|
--sage-bg-secondary: #111827;
|
||||||
|
--sage-bg-card: #1E293B;
|
||||||
|
--sage-bg-hover: #334155;
|
||||||
|
--sage-bg-input: #0F172A;
|
||||||
|
--sage-bg-toolbar: #111827;
|
||||||
|
|
||||||
|
--sage-border-primary: #334155;
|
||||||
|
--sage-border-weak: #1E293B;
|
||||||
|
--sage-border-focus: #3B82F6;
|
||||||
|
|
||||||
|
--sage-text-primary: #F1F5F9;
|
||||||
|
--sage-text-secondary: #94A3B8;
|
||||||
|
--sage-text-muted: #64748B;
|
||||||
|
--sage-text-inverse: #0F172A;
|
||||||
|
|
||||||
|
--sage-brand: #3B82F6;
|
||||||
|
--sage-brand-hover: #2563EB;
|
||||||
|
--sage-success: #22C55E;
|
||||||
|
--sage-warning: #F59E0B;
|
||||||
|
--sage-danger: #EF4444;
|
||||||
|
--sage-info: #06B6D4;
|
||||||
|
|
||||||
|
--sage-sidebar-width: 240px;
|
||||||
|
--sage-sidebar-collapsed-width: 64px;
|
||||||
|
--sage-topbar-height: 56px;
|
||||||
|
--sage-radius: 12px;
|
||||||
|
--sage-radius-sm: 8px;
|
||||||
|
|
||||||
|
--sage-shadow-card: 0 1px 3px rgba(0,0,0,0.3);
|
||||||
|
--sage-shadow-lg: 0 4px 12px rgba(0,0,0,0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== Light Theme ===== */
|
||||||
|
[data-theme="light"] {
|
||||||
|
--sage-bg-primary: #F8FAFC;
|
||||||
|
--sage-bg-secondary: #FFFFFF;
|
||||||
|
--sage-bg-card: #FFFFFF;
|
||||||
|
--sage-bg-hover: #F1F5F9;
|
||||||
|
--sage-bg-input: #F8FAFC;
|
||||||
|
--sage-bg-toolbar: #FFFFFF;
|
||||||
|
|
||||||
|
--sage-border-primary: #E2E8F0;
|
||||||
|
--sage-border-weak: #F1F5F9;
|
||||||
|
--sage-border-focus: #3B82F6;
|
||||||
|
|
||||||
|
--sage-text-primary: #0F172A;
|
||||||
|
--sage-text-secondary: #475569;
|
||||||
|
--sage-text-muted: #94A3B8;
|
||||||
|
--sage-text-inverse: #FFFFFF;
|
||||||
|
|
||||||
|
--sage-brand: #3B82F6;
|
||||||
|
--sage-brand-hover: #2563EB;
|
||||||
|
--sage-success: #16A34A;
|
||||||
|
--sage-warning: #D97706;
|
||||||
|
--sage-danger: #DC2626;
|
||||||
|
--sage-info: #0891B2;
|
||||||
|
|
||||||
|
--sage-shadow-card: 0 1px 3px rgba(0,0,0,0.08);
|
||||||
|
--sage-shadow-lg: 0 4px 12px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== Global Overrides ===== */
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background-color: var(--sage-bg-primary);
|
||||||
|
color: var(--sage-text-primary);
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||||
|
transition: background-color 0.2s ease, color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== Shell Layout ===== */
|
||||||
|
.sage-shell {
|
||||||
|
width: 100%;
|
||||||
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sage-topbar {
|
||||||
|
height: var(--sage-topbar-height);
|
||||||
|
background-color: var(--sage-bg-secondary);
|
||||||
|
border-bottom: 1px solid var(--sage-border-primary);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 16px;
|
||||||
|
gap: 12px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sage-body {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sage-sidebar {
|
||||||
|
width: var(--sage-sidebar-width);
|
||||||
|
background-color: var(--sage-bg-secondary);
|
||||||
|
border-right: 1px solid var(--sage-border-primary);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
flex-shrink: 0;
|
||||||
|
transition: width 0.25s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sage-sidebar.collapsed {
|
||||||
|
width: var(--sage-sidebar-collapsed-width);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sage-sidebar.collapsed .sidebar-text {
|
||||||
|
opacity: 0;
|
||||||
|
width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sage-main {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
padding: 24px;
|
||||||
|
background-color: var(--sage-bg-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== Stat Cards ===== */
|
||||||
|
.stat-card {
|
||||||
|
background-color: var(--sage-bg-card);
|
||||||
|
border-radius: var(--sage-radius);
|
||||||
|
padding: 20px;
|
||||||
|
border: 1px solid var(--sage-border-primary);
|
||||||
|
box-shadow: var(--sage-shadow-card);
|
||||||
|
transition: transform 0.15s ease, box-shadow 0.15s ease, background-color 0.2s ease;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: var(--sage-shadow-lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card .stat-icon {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: var(--sage-radius-sm);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card .stat-value {
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--sage-text-primary);
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card .stat-label {
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--sage-text-secondary);
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card .stat-trend {
|
||||||
|
font-size: 13px;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card .stat-trend.up {
|
||||||
|
color: var(--sage-success);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card .stat-trend.down {
|
||||||
|
color: var(--sage-danger);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== Quick Links ===== */
|
||||||
|
.quick-link {
|
||||||
|
background-color: var(--sage-bg-card);
|
||||||
|
border-radius: var(--sage-radius);
|
||||||
|
padding: 20px;
|
||||||
|
border: 1px solid var(--sage-border-primary);
|
||||||
|
box-shadow: var(--sage-shadow-card);
|
||||||
|
transition: transform 0.15s ease, box-shadow 0.15s ease, background-color 0.2s ease;
|
||||||
|
cursor: pointer;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quick-link:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: var(--sage-shadow-lg);
|
||||||
|
border-color: var(--sage-brand);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== Section Headers ===== */
|
||||||
|
.section-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--sage-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== Buttons ===== */
|
||||||
|
.sage-btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
border-radius: var(--sage-radius-sm);
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: background-color 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sage-btn-primary {
|
||||||
|
background-color: var(--sage-brand);
|
||||||
|
color: #FFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sage-btn-primary:hover {
|
||||||
|
background-color: var(--sage-brand-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sage-btn-ghost {
|
||||||
|
background-color: transparent;
|
||||||
|
color: var(--sage-text-secondary);
|
||||||
|
border: 1px solid var(--sage-border-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sage-btn-ghost:hover {
|
||||||
|
background-color: var(--sage-bg-hover);
|
||||||
|
color: var(--sage-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== Theme Toggle Button ===== */
|
||||||
|
.theme-toggle {
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 1px solid var(--sage-border-primary);
|
||||||
|
background-color: transparent;
|
||||||
|
color: var(--sage-text-secondary);
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transition: background-color 0.15s ease, color 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-toggle:hover {
|
||||||
|
background-color: var(--sage-bg-hover);
|
||||||
|
color: var(--sage-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== Sidebar Toggle ===== */
|
||||||
|
.sidebar-toggle {
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
border-radius: var(--sage-radius-sm);
|
||||||
|
border: 1px solid var(--sage-border-primary);
|
||||||
|
background-color: transparent;
|
||||||
|
color: var(--sage-text-secondary);
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transition: background-color 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-toggle:hover {
|
||||||
|
background-color: var(--sage-bg-hover);
|
||||||
|
color: var(--sage-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== Breadcrumb ===== */
|
||||||
|
.breadcrumb {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--sage-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb a {
|
||||||
|
color: var(--sage-text-secondary);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb a:hover {
|
||||||
|
color: var(--sage-brand);
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb .separator {
|
||||||
|
color: var(--sage-text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb .current {
|
||||||
|
color: var(--sage-text-primary);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== DataViewer Overrides for theme ===== */
|
||||||
|
[data-theme="dark"] .dataviewer-toolbar,
|
||||||
|
[data-theme="dark"] .tabular,
|
||||||
|
[data-theme="dark"] .data-row {
|
||||||
|
background-color: var(--sage-bg-card);
|
||||||
|
color: var(--sage-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .tabular th {
|
||||||
|
background-color: var(--sage-bg-secondary);
|
||||||
|
color: var(--sage-text-secondary);
|
||||||
|
border-color: var(--sage-border-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .tabular td {
|
||||||
|
border-color: var(--sage-border-weak);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="light"] .dataviewer-toolbar,
|
||||||
|
[data-theme="light"] .tabular,
|
||||||
|
[data-theme="light"] .data-row {
|
||||||
|
background-color: var(--sage-bg-card);
|
||||||
|
color: var(--sage-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="light"] .tabular th {
|
||||||
|
background-color: var(--sage-bg-hover);
|
||||||
|
color: var(--sage-text-secondary);
|
||||||
|
border-color: var(--sage-border-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="light"] .tabular td {
|
||||||
|
border-color: var(--sage-border-weak);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== Menu Overrides ===== */
|
||||||
|
[data-theme="dark"] .menu-item {
|
||||||
|
color: var(--sage-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .menu-item:hover,
|
||||||
|
[data-theme="dark"] .menu-item.active {
|
||||||
|
background-color: var(--sage-bg-hover);
|
||||||
|
color: var(--sage-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="light"] .menu-item {
|
||||||
|
color: var(--sage-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="light"] .menu-item:hover,
|
||||||
|
[data-theme="light"] .menu-item.active {
|
||||||
|
background-color: var(--sage-bg-hover);
|
||||||
|
color: var(--sage-brand);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== Responsive ===== */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.sage-sidebar {
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
top: var(--sage-topbar-height);
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 200;
|
||||||
|
transform: translateX(-100%);
|
||||||
|
transition: transform 0.25s ease;
|
||||||
|
}
|
||||||
|
.sage-sidebar.mobile-open {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
87
wwwroot/shell_theme.js
Normal file
87
wwwroot/shell_theme.js
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/* Sage Modern UI Shell Theme & Layout Controller
|
||||||
|
Handles: theme switching (dark/light), sidebar collapse, localStorage persistence
|
||||||
|
*/
|
||||||
|
(function() {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var THEME_KEY = 'sage_ui_theme';
|
||||||
|
var SIDEBAR_KEY = 'sage_sidebar_collapsed';
|
||||||
|
|
||||||
|
// Initialize theme on page load
|
||||||
|
function initTheme() {
|
||||||
|
var saved = null;
|
||||||
|
try { saved = localStorage.getItem(THEME_KEY); } catch(e) {}
|
||||||
|
var theme = saved || 'dark';
|
||||||
|
document.documentElement.setAttribute('data-theme', theme);
|
||||||
|
updateThemeIcon(theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toggle between dark and light
|
||||||
|
function toggleTheme() {
|
||||||
|
var current = document.documentElement.getAttribute('data-theme') || 'dark';
|
||||||
|
var next = current === 'dark' ? 'light' : 'dark';
|
||||||
|
document.documentElement.setAttribute('data-theme', next);
|
||||||
|
try { localStorage.setItem(THEME_KEY, next); } catch(e) {}
|
||||||
|
updateThemeIcon(next);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the theme toggle icon based on current theme
|
||||||
|
function updateThemeIcon(theme) {
|
||||||
|
var btn = document.getElementById('theme_toggle_btn');
|
||||||
|
if (!btn) return;
|
||||||
|
if (theme === 'light') {
|
||||||
|
// Show moon icon (switch to dark)
|
||||||
|
btn.innerHTML = '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>';
|
||||||
|
} else {
|
||||||
|
// Show sun icon (switch to light)
|
||||||
|
btn.innerHTML = '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize sidebar state
|
||||||
|
function initSidebar() {
|
||||||
|
var collapsed = false;
|
||||||
|
try { collapsed = localStorage.getItem(SIDEBAR_KEY) === 'true'; } catch(e) {}
|
||||||
|
var sidebar = document.getElementById('sage_sidebar');
|
||||||
|
if (sidebar && collapsed) {
|
||||||
|
sidebar.classList.add('collapsed');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toggle sidebar collapse
|
||||||
|
function toggleSidebar() {
|
||||||
|
var sidebar = document.getElementById('sage_sidebar');
|
||||||
|
if (!sidebar) return;
|
||||||
|
sidebar.classList.toggle('collapsed');
|
||||||
|
var isCollapsed = sidebar.classList.contains('collapsed');
|
||||||
|
try { localStorage.setItem(SIDEBAR_KEY, isCollapsed); } catch(e) {}
|
||||||
|
|
||||||
|
// Update toggle icon
|
||||||
|
var btn = document.getElementById('sidebar_toggle_btn');
|
||||||
|
if (btn) {
|
||||||
|
if (isCollapsed) {
|
||||||
|
btn.innerHTML = '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="13 17 18 12 13 7"/><polyline points="6 17 11 12 6 7"/></svg>';
|
||||||
|
} else {
|
||||||
|
btn.innerHTML = '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="11 17 6 12 11 7"/><polyline points="18 17 13 12 18 7"/></svg>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run on DOM ready
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
initTheme();
|
||||||
|
initSidebar();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
initTheme();
|
||||||
|
initSidebar();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expose global functions for bricks bind access
|
||||||
|
window.sageToggleTheme = toggleTheme;
|
||||||
|
window.sageToggleSidebar = toggleSidebar;
|
||||||
|
window.sageInitTheme = initTheme;
|
||||||
|
window.sageInitSidebar = initSidebar;
|
||||||
|
|
||||||
|
})();
|
||||||
52
wwwroot/stat_concurrent.ui
Normal file
52
wwwroot/stat_concurrent.ui
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
{
|
||||||
|
"widgettype": "VBox",
|
||||||
|
"options": {
|
||||||
|
"bgcolor": "#1E293B",
|
||||||
|
"padding": "20px",
|
||||||
|
"borderRadius": "12px",
|
||||||
|
"border": "1px solid #334155",
|
||||||
|
"flex": "1",
|
||||||
|
"minHeight": "110px"
|
||||||
|
},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "HBox",
|
||||||
|
"options": {
|
||||||
|
"alignItems": "center",
|
||||||
|
"marginBottom": "12px"
|
||||||
|
},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "Svg",
|
||||||
|
"options": {
|
||||||
|
"svg": "<svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#06B6D4\" stroke-width=\"2\"><path d=\"M18 18.72a9.094 9.094 0 003.741-.479 3 3 0 00-4.682-2.72m.94 3.198l.001.031c0 .225-.012.447-.037.666A11.944 11.944 0 0112 21c-2.17 0-4.207-.576-5.963-1.584A6.062 6.062 0 016 18.719m12 0a5.971 5.971 0 00-.941-3.197m0 0A5.995 5.995 0 0012 12.75a5.995 5.995 0 00-5.058 2.772m0 0a3 3 0 00-4.681 2.72 8.986 8.986 0 003.74.477m.94-3.197a5.971 5.971 0 00-.94 3.197M15 6.75a3 3 0 11-6 0 3 3 0 016 0zm6 3a2.25 2.25 0 11-4.5 0 2.25 2.25 0 014.5 0zm-13.5 0a2.25 2.25 0 11-4.5 0 2.25 2.25 0 014.5 0z\"/></svg>",
|
||||||
|
"width": "24px",
|
||||||
|
"height": "24px"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Filler"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {
|
||||||
|
"text": "{{get_concurrent_users(request)}}",
|
||||||
|
"fontSize": "32px",
|
||||||
|
"fontWeight": "700",
|
||||||
|
"color": "#F1F5F9",
|
||||||
|
"lineHeight": "1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {
|
||||||
|
"text": "在线用户",
|
||||||
|
"fontSize": "14px",
|
||||||
|
"color": "#94A3B8",
|
||||||
|
"marginTop": "4px"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
52
wwwroot/stat_errors.ui
Normal file
52
wwwroot/stat_errors.ui
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
{
|
||||||
|
"widgettype": "VBox",
|
||||||
|
"options": {
|
||||||
|
"bgcolor": "#1E293B",
|
||||||
|
"padding": "20px",
|
||||||
|
"borderRadius": "12px",
|
||||||
|
"border": "1px solid #334155",
|
||||||
|
"flex": "1",
|
||||||
|
"minHeight": "110px"
|
||||||
|
},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "HBox",
|
||||||
|
"options": {
|
||||||
|
"alignItems": "center",
|
||||||
|
"marginBottom": "12px"
|
||||||
|
},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "Svg",
|
||||||
|
"options": {
|
||||||
|
"svg": "<svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#EF4444\" stroke-width=\"2\"><path d=\"M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z\"/></svg>",
|
||||||
|
"width": "24px",
|
||||||
|
"height": "24px"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Filler"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {
|
||||||
|
"text": "{{get_accounting_errors(request)}}",
|
||||||
|
"fontSize": "32px",
|
||||||
|
"fontWeight": "700",
|
||||||
|
"color": "#EF4444",
|
||||||
|
"lineHeight": "1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {
|
||||||
|
"text": "记账异常",
|
||||||
|
"fontSize": "14px",
|
||||||
|
"color": "#94A3B8",
|
||||||
|
"marginTop": "4px"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
52
wwwroot/stat_today_amount.ui
Normal file
52
wwwroot/stat_today_amount.ui
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
{
|
||||||
|
"widgettype": "VBox",
|
||||||
|
"options": {
|
||||||
|
"bgcolor": "#1E293B",
|
||||||
|
"padding": "20px",
|
||||||
|
"borderRadius": "12px",
|
||||||
|
"border": "1px solid #334155",
|
||||||
|
"flex": "1",
|
||||||
|
"minHeight": "110px"
|
||||||
|
},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "HBox",
|
||||||
|
"options": {
|
||||||
|
"alignItems": "center",
|
||||||
|
"marginBottom": "12px"
|
||||||
|
},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "Svg",
|
||||||
|
"options": {
|
||||||
|
"svg": "<svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#22C55E\" stroke-width=\"2\"><path d=\"M12 6v12m-3-2.818l.879.659c1.171.879 3.07.879 4.242 0 1.172-.879 1.172-2.303 0-3.182C13.536 12.219 12.768 12 12 12c-.725 0-1.45-.22-2.003-.659-1.106-.879-1.106-2.303 0-3.182s2.9-.879 4.006 0l.415.33M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"/></svg>",
|
||||||
|
"width": "24px",
|
||||||
|
"height": "24px"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Filler"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {
|
||||||
|
"text": "¥{{get_today_amount(request)|round(2)}}",
|
||||||
|
"fontSize": "32px",
|
||||||
|
"fontWeight": "700",
|
||||||
|
"color": "#F1F5F9",
|
||||||
|
"lineHeight": "1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {
|
||||||
|
"text": "今日消费金额",
|
||||||
|
"fontSize": "14px",
|
||||||
|
"color": "#94A3B8",
|
||||||
|
"marginTop": "4px"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
53
wwwroot/stat_today_usage.ui
Normal file
53
wwwroot/stat_today_usage.ui
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
{
|
||||||
|
"widgettype": "VBox",
|
||||||
|
"options": {
|
||||||
|
"bgcolor": "#1E293B",
|
||||||
|
"padding": "20px",
|
||||||
|
"borderRadius": "12px",
|
||||||
|
"border": "1px solid #334155",
|
||||||
|
"flex": "1",
|
||||||
|
"minHeight": "110px",
|
||||||
|
"cursor": "pointer"
|
||||||
|
},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "HBox",
|
||||||
|
"options": {
|
||||||
|
"alignItems": "center",
|
||||||
|
"marginBottom": "12px"
|
||||||
|
},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "Svg",
|
||||||
|
"options": {
|
||||||
|
"svg": "<svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#3B82F6\" stroke-width=\"2\"><path d=\"M7.5 21L3 16.5m0 0L7.5 12M12 9v7.5m0 0l4.5-4.5M12 9l4.5 4.5m0 0L12 16.5\"/><path d=\"M21 12h-4.5M12 3v4.5m0 0L7.5 12\"/></svg>",
|
||||||
|
"width": "24px",
|
||||||
|
"height": "24px"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Filler"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {
|
||||||
|
"text": "{{get_today_usage(request)}}",
|
||||||
|
"fontSize": "32px",
|
||||||
|
"fontWeight": "700",
|
||||||
|
"color": "#F1F5F9",
|
||||||
|
"lineHeight": "1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {
|
||||||
|
"text": "今日调用笔数",
|
||||||
|
"fontSize": "14px",
|
||||||
|
"color": "#94A3B8",
|
||||||
|
"marginTop": "4px"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
52
wwwroot/stat_total_users.ui
Normal file
52
wwwroot/stat_total_users.ui
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
{
|
||||||
|
"widgettype": "VBox",
|
||||||
|
"options": {
|
||||||
|
"bgcolor": "#1E293B",
|
||||||
|
"padding": "20px",
|
||||||
|
"borderRadius": "12px",
|
||||||
|
"border": "1px solid #334155",
|
||||||
|
"flex": "1",
|
||||||
|
"minHeight": "110px"
|
||||||
|
},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "HBox",
|
||||||
|
"options": {
|
||||||
|
"alignItems": "center",
|
||||||
|
"marginBottom": "12px"
|
||||||
|
},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "Svg",
|
||||||
|
"options": {
|
||||||
|
"svg": "<svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#A78BFA\" stroke-width=\"2\"><path d=\"M15 19.128a9.38 9.38 0 002.625.372 9.337 9.337 0 004.121-.952 4.125 4.125 0 00-7.533-2.493M15 19.128v-.003c0-1.113-.285-2.16-.786-3.07M15 19.128v.106A12.318 12.318 0 018.624 21c-2.331 0-4.512-.645-6.374-1.766l-.001-.109a6.375 6.375 0 0111.964-3.07M12 6.375a3.375 3.375 0 11-6.75 0 3.375 3.375 0 016.75 0zm8.25 2.25a2.625 2.625 0 11-5.25 0 2.625 2.625 0 015.25 0z\"/></svg>",
|
||||||
|
"width": "24px",
|
||||||
|
"height": "24px"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Filler"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {
|
||||||
|
"text": "{{get_total_users(request)}}",
|
||||||
|
"fontSize": "32px",
|
||||||
|
"fontWeight": "700",
|
||||||
|
"color": "#F1F5F9",
|
||||||
|
"lineHeight": "1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {
|
||||||
|
"text": "用户总数",
|
||||||
|
"fontSize": "14px",
|
||||||
|
"color": "#94A3B8",
|
||||||
|
"marginTop": "4px"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
16
wwwroot/table_top_users.ui
Normal file
16
wwwroot/table_top_users.ui
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"widgettype": "VBox",
|
||||||
|
"options": {
|
||||||
|
"width": "100%"
|
||||||
|
},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "RefreshWidget",
|
||||||
|
"id": "table_top_users_amount",
|
||||||
|
"options": {
|
||||||
|
"period_seconds": 30,
|
||||||
|
"url": "{{entire_url('table_top_users_amount.ui')}}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
77
wwwroot/table_top_users_amount.ui
Normal file
77
wwwroot/table_top_users_amount.ui
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
{% set users = get_top_users_by_amount(request) %}
|
||||||
|
{
|
||||||
|
"widgettype": "VBox",
|
||||||
|
"options": {
|
||||||
|
"width": "100%"
|
||||||
|
},
|
||||||
|
"subwidgets": [
|
||||||
|
{% for u in users %}
|
||||||
|
{
|
||||||
|
"widgettype": "HBox",
|
||||||
|
"options": {
|
||||||
|
"width": "100%",
|
||||||
|
"padding": "12px 0",
|
||||||
|
{% if not loop.first %}
|
||||||
|
"borderTop": "1px solid #334155",
|
||||||
|
{% endif %}
|
||||||
|
"alignItems": "center"
|
||||||
|
},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {
|
||||||
|
"text": "{{loop.index}}",
|
||||||
|
"width": "30px",
|
||||||
|
"color": "#64748B",
|
||||||
|
"fontSize": "14px",
|
||||||
|
"textAlign": "center"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {
|
||||||
|
"text": "{{u.user_name}}",
|
||||||
|
"flex": "1",
|
||||||
|
"color": "#F1F5F9",
|
||||||
|
"fontSize": "14px",
|
||||||
|
"fontWeight": "500"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {
|
||||||
|
"text": "{{u.cnt}} 笔",
|
||||||
|
"width": "80px",
|
||||||
|
"color": "#94A3B8",
|
||||||
|
"fontSize": "13px",
|
||||||
|
"textAlign": "right"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {
|
||||||
|
"text": "¥{{u.total_amount}}",
|
||||||
|
"width": "100px",
|
||||||
|
"color": "#22C55E",
|
||||||
|
"fontSize": "14px",
|
||||||
|
"fontWeight": "600",
|
||||||
|
"textAlign": "right"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}{% if not loop.last %},{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% if not users %}
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {
|
||||||
|
"text": "暂无数据",
|
||||||
|
"color": "#64748B",
|
||||||
|
"fontSize": "14px",
|
||||||
|
"textAlign": "center",
|
||||||
|
"padding": "20px 0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
]
|
||||||
|
}
|
||||||
57
wwwroot/top_users_amount.ui
Normal file
57
wwwroot/top_users_amount.ui
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
{
|
||||||
|
"widgettype": "VBox",
|
||||||
|
"options": {
|
||||||
|
"bgcolor": "#FFFFFF",
|
||||||
|
"padding": "20px",
|
||||||
|
"borderRadius": "8px",
|
||||||
|
"minHeight": "280px"
|
||||||
|
},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {
|
||||||
|
"text": "用户金额 TOP 5(今日)",
|
||||||
|
"fontSize": "16px",
|
||||||
|
"fontWeight": "bold",
|
||||||
|
"color": "#333",
|
||||||
|
"marginBottom": "12px"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{% set rows = get_top_users_by_amount(request) %}
|
||||||
|
{% if rows %}
|
||||||
|
{
|
||||||
|
"widgettype": "VBox",
|
||||||
|
"options": {"spacing": "8px"},
|
||||||
|
"subwidgets": [
|
||||||
|
{
|
||||||
|
"widgettype": "HBox",
|
||||||
|
"options": {"bgcolor": "#f5f5f5", "padding": "8px 12px", "borderRadius": "4px"},
|
||||||
|
"subwidgets": [
|
||||||
|
{"widgettype": "Text", "options": {"text": "排名", "fontSize": "12px", "color": "#888", "width": "50px"}},
|
||||||
|
{"widgettype": "Text", "options": {"text": "用户", "fontSize": "12px", "color": "#888", "flex": "1"}},
|
||||||
|
{"widgettype": "Text", "options": {"text": "金额", "fontSize": "12px", "color": "#888", "width": "100px", "textAlign": "right"}},
|
||||||
|
{"widgettype": "Text", "options": {"text": "笔数", "fontSize": "12px", "color": "#888", "width": "60px", "textAlign": "right"}}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{% for item in rows %}
|
||||||
|
{
|
||||||
|
"widgettype": "HBox",
|
||||||
|
"options": {"padding": "8px 12px", "borderRadius": "4px", "bgcolor": "{% if loop.index == 1 %}#fff3e0{% elif loop.index == 2 %}#fff8e1{% elif loop.index == 3 %}#fffde7{% else %}#fafafa{% endif %}"},
|
||||||
|
"subwidgets": [
|
||||||
|
{"widgettype": "Text", "options": {"text": "{{loop.index}}", "fontSize": "13px", "color": "{% if loop.index <= 3 %}#e65100{% else %}#666{% endif %}", "width": "50px", "fontWeight": "bold"}},
|
||||||
|
{"widgettype": "Text", "options": {"text": "{{item.user_name}}", "fontSize": "13px", "color": "#333", "flex": "1"}},
|
||||||
|
{"widgettype": "Text", "options": {"text": "{{item.total_amount|round(2)}}", "fontSize": "13px", "color": "#2e7d32", "width": "100px", "textAlign": "right", "fontWeight": "bold"}},
|
||||||
|
{"widgettype": "Text", "options": {"text": "{{item.cnt}}", "fontSize": "13px", "color": "#666", "width": "60px", "textAlign": "right"}}
|
||||||
|
]
|
||||||
|
}{% if not loop.last %},{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
{% else %}
|
||||||
|
{
|
||||||
|
"widgettype": "Text",
|
||||||
|
"options": {"text": "暂无数据", "fontSize": "14px", "color": "#999", "textAlign": "center"}
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
]
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user