feat: 侧边栏分组图标与导航样式优化

- 分组支持 icon 字段,可按名称自动匹配或手动选择
- 左侧导航与总览卡片显示彩色 SVG 图标
- 优化侧栏链接圆角与选中态样式

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-05-30 18:06:42 +08:00
parent 11129cc3a0
commit f7ce6f1058
8 changed files with 250 additions and 24 deletions
+8
View File
@@ -28,6 +28,14 @@
<div class="errors">{{ form.name.errors[0] }}</div>
{% endif %}
</div>
<div class="form-row">
{{ form.icon.label }}
{{ form.icon() }}
<p class="hint" style="margin: 0.35rem 0 0">留空则按分组名称自动匹配图标(如「交易」「Gate 扫单」)。</p>
{% if form.icon.errors %}
<div class="errors">{{ form.icon.errors[0] }}</div>
{% endif %}
</div>
<div class="form-row">
{{ form.sort_order.label }}
{{ form.sort_order() }}
+3 -1
View File
@@ -30,6 +30,7 @@
<thead>
<tr>
<th>ID</th>
<th>图标</th>
<th>名称</th>
<th>排序</th>
<th style="width: 200px">操作</th>
@@ -39,6 +40,7 @@
{% for g in groups %}
<tr>
<td>{{ g.id }}</td>
<td><code>{{ g.resolve_icon_key() }}</code></td>
<td>{{ g.name }}</td>
<td>{{ g.sort_order }}</td>
<td>
@@ -59,7 +61,7 @@
</tr>
{% else %}
<tr>
<td colspan="4" class="hint">暂无分组,点击「新建分组」</td>
<td colspan="5" class="hint">暂无分组,点击「新建分组」</td>
</tr>
{% endfor %}
</tbody>
+13 -4
View File
@@ -1,4 +1,5 @@
{% extends "base.html" %}
{% from "macros/group_icon.html" import render as group_icon %}
{% block title %}导航 · 本地导航{% endblock %}
{% block body %}
<div class="app-shell">
@@ -35,7 +36,11 @@
</div>
{% for group, services in grouped %}
<div class="sidebar-section">
<h2>{{ group.name }}</h2>
<h2 class="sidebar-group-title">
{{ group_icon(group.resolve_icon_key()) }}
<span class="sidebar-group-name">{{ group.name }}</span>
</h2>
<div class="sidebar-links">
{% for svc in services %}
<a
href="#"
@@ -48,11 +53,12 @@
data-embed-kind="{{ (svc.embed_kind or '')|e }}"
data-service-id="{{ svc.id }}"
data-name="{{ svc.name | e }}"
>{{ svc.name }}</a
><span class="nav-link-text">{{ svc.name }}</span></a
>
{% else %}
<div class="hint" style="padding: 0 1rem">该分组下暂无服务</div>
<div class="hint sidebar-empty">该分组下暂无服务</div>
{% endfor %}
</div>
</div>
{% else %}
<div class="hint" style="padding: 1rem">
@@ -74,7 +80,10 @@
<div class="service-dashboard" id="service-dashboard">
{% for group, services in grouped %}
<section class="dash-section">
<h3 class="dash-section-title">{{ group.name }}</h3>
<h3 class="dash-section-title">
{{ group_icon(group.resolve_icon_key()) }}
<span>{{ group.name }}</span>
</h3>
<div class="service-card-grid">
{% for svc in services %}
<button
+51
View File
@@ -0,0 +1,51 @@
{% macro render(icon_key) %}
{% set key = icon_key or 'folder' %}
<span class="sidebar-group-icon sidebar-group-icon--{{ key }}" aria-hidden="true">
{% if key == 'order' %}
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
<path d="M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2"/>
<rect x="9" y="3" width="6" height="4" rx="1"/>
<path d="M9 12h6M9 16h6"/>
</svg>
{% elif key == 'trade' %}
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 17l6-6 4 4 8-8"/>
<path d="M14 7h7v7"/>
</svg>
{% elif key == 'review' %}
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="9"/>
<path d="M12 7v5l3 2"/>
</svg>
{% elif key == 'api' %}
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
<path d="M8 9l-3 3 3 3"/>
<path d="M16 9l3 3-3 3"/>
<path d="M14 5l-4 14"/>
</svg>
{% elif key == 'gate' %}
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="2"/>
<path d="M12 2v3M12 19v3M2 12h3M19 12h3"/>
<path d="M4.9 4.9l2.1 2.1M16.9 16.9l2.1 2.1M4.9 19.1l2.1-2.1M16.9 7.1l2.1-2.1"/>
</svg>
{% elif key == 'chart' %}
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
<path d="M4 19V5"/>
<path d="M4 19h16"/>
<path d="M8 15V9M12 15V7M16 15v-4"/>
</svg>
{% elif key == 'server' %}
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
<rect x="3" y="4" width="18" height="6" rx="2"/>
<rect x="3" y="14" width="18" height="6" rx="2"/>
<circle cx="7" cy="7" r="1" fill="currentColor" stroke="none"/>
<circle cx="7" cy="17" r="1" fill="currentColor" stroke="none"/>
</svg>
{% else %}
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
<path d="M4 7a2 2 0 0 1 2-2h4l2 2h6a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V7z"/>
</svg>
{% endif %}
</span>
{% endmacro %}