mirror of
https://github.com/router-for-me/Cli-Proxy-API-Management-Center.git
synced 2026-09-24 16:04:35 +08:00
feat(nav): restructure navigation items into groups with metadata and update styles
This commit is contained in:
@@ -384,18 +384,85 @@ export function MainLayout() {
|
||||
});
|
||||
}, [fetchConfig]);
|
||||
|
||||
const navItems = [
|
||||
{ path: '/', label: t('nav.dashboard'), icon: sidebarIcons.dashboard },
|
||||
{ path: '/config', label: t('nav.config_management'), icon: sidebarIcons.config },
|
||||
{ path: '/ai-providers', label: t('nav.ai_providers'), icon: sidebarIcons.aiProviders },
|
||||
{ path: '/auth-files', label: t('nav.auth_files'), icon: sidebarIcons.authFiles },
|
||||
{ path: '/oauth', label: t('nav.oauth', { defaultValue: 'OAuth' }), icon: sidebarIcons.oauth },
|
||||
{ path: '/quota', label: t('nav.quota_management'), icon: sidebarIcons.quota },
|
||||
...(config?.loggingToFile
|
||||
? [{ path: '/logs', label: t('nav.logs'), icon: sidebarIcons.logs }]
|
||||
: []),
|
||||
{ path: '/system', label: t('nav.system_info'), icon: sidebarIcons.system },
|
||||
const navGroups = [
|
||||
{
|
||||
id: 'operate',
|
||||
labelKey: 'nav_groups.operate',
|
||||
items: [
|
||||
{
|
||||
path: '/',
|
||||
labelKey: 'nav.dashboard',
|
||||
metaKey: 'nav_meta.dashboard',
|
||||
icon: sidebarIcons.dashboard,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'gateway',
|
||||
labelKey: 'nav_groups.gateway',
|
||||
items: [
|
||||
{
|
||||
path: '/ai-providers',
|
||||
labelKey: 'nav.ai_providers',
|
||||
metaKey: 'nav_meta.ai_providers',
|
||||
icon: sidebarIcons.aiProviders,
|
||||
},
|
||||
{
|
||||
path: '/auth-files',
|
||||
labelKey: 'nav.auth_files',
|
||||
metaKey: 'nav_meta.auth_files',
|
||||
icon: sidebarIcons.authFiles,
|
||||
},
|
||||
{
|
||||
path: '/oauth',
|
||||
labelKey: 'nav.oauth',
|
||||
metaKey: 'nav_meta.oauth',
|
||||
icon: sidebarIcons.oauth,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'observe',
|
||||
labelKey: 'nav_groups.observe',
|
||||
items: [
|
||||
{
|
||||
path: '/quota',
|
||||
labelKey: 'nav.quota_management',
|
||||
metaKey: 'nav_meta.quota_management',
|
||||
icon: sidebarIcons.quota,
|
||||
},
|
||||
...(config?.loggingToFile
|
||||
? [
|
||||
{
|
||||
path: '/logs',
|
||||
labelKey: 'nav.logs',
|
||||
metaKey: 'nav_meta.logs',
|
||||
icon: sidebarIcons.logs,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'control',
|
||||
labelKey: 'nav_groups.control',
|
||||
items: [
|
||||
{
|
||||
path: '/config',
|
||||
labelKey: 'nav.config_management',
|
||||
metaKey: 'nav_meta.config_management',
|
||||
icon: sidebarIcons.config,
|
||||
},
|
||||
{
|
||||
path: '/system',
|
||||
labelKey: 'nav.system_info',
|
||||
metaKey: 'nav_meta.system_info',
|
||||
icon: sidebarIcons.system,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
const navItems = navGroups.flatMap((group) => group.items);
|
||||
const navOrder = navItems.map((item) => item.path);
|
||||
const getRouteOrder = (pathname: string) => {
|
||||
const trimmedPath =
|
||||
@@ -652,17 +719,32 @@ export function MainLayout() {
|
||||
</div>
|
||||
|
||||
<div className="nav-section">
|
||||
{navItems.map((item) => (
|
||||
<NavLink
|
||||
key={item.path}
|
||||
to={item.path}
|
||||
className={({ isActive }) => `nav-item ${isActive ? 'active' : ''}`}
|
||||
onClick={() => setSidebarOpen(false)}
|
||||
title={showSidebarLabels ? undefined : item.label}
|
||||
>
|
||||
<span className="nav-icon">{item.icon}</span>
|
||||
{showSidebarLabels && <span className="nav-label">{item.label}</span>}
|
||||
</NavLink>
|
||||
{navGroups.map((group, idx) => (
|
||||
<div className="nav-group" key={group.id}>
|
||||
{showSidebarLabels
|
||||
? <div className="nav-group-label">{t(group.labelKey)}</div>
|
||||
: idx > 0 && <div className="nav-group-divider" aria-hidden="true" />}
|
||||
{group.items.map((item) => {
|
||||
const itemLabel = t(item.labelKey);
|
||||
return (
|
||||
<NavLink
|
||||
key={item.path}
|
||||
to={item.path}
|
||||
className={({ isActive }) => `nav-item ${isActive ? 'active' : ''}`}
|
||||
onClick={() => setSidebarOpen(false)}
|
||||
title={showSidebarLabels ? undefined : itemLabel}
|
||||
>
|
||||
<span className="nav-icon">{item.icon}</span>
|
||||
{showSidebarLabels && (
|
||||
<span className="nav-text">
|
||||
<span className="nav-label">{itemLabel}</span>
|
||||
<span className="nav-meta">{t(item.metaKey)}</span>
|
||||
</span>
|
||||
)}
|
||||
</NavLink>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
@@ -120,6 +120,22 @@
|
||||
"logs": "Logs Viewer",
|
||||
"system_info": "Management Center Info"
|
||||
},
|
||||
"nav_groups": {
|
||||
"operate": "Operate",
|
||||
"gateway": "Gateway",
|
||||
"observe": "Observe",
|
||||
"control": "Control"
|
||||
},
|
||||
"nav_meta": {
|
||||
"dashboard": "Operations overview",
|
||||
"ai_providers": "Upstream model routing",
|
||||
"auth_files": "Auth files & credentials",
|
||||
"oauth": "OAuth authorization",
|
||||
"quota_management": "API keys & limits",
|
||||
"logs": "Request tracing",
|
||||
"config_management": "Gateway configuration",
|
||||
"system_info": "Runtime & diagnostics"
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Dashboard",
|
||||
"subtitle": "Welcome to CLI Proxy API Management Center",
|
||||
|
||||
@@ -120,6 +120,22 @@
|
||||
"logs": "Просмотр логов",
|
||||
"system_info": "Информация системы"
|
||||
},
|
||||
"nav_groups": {
|
||||
"operate": "Работа",
|
||||
"gateway": "Шлюз",
|
||||
"observe": "Наблюдение",
|
||||
"control": "Управление"
|
||||
},
|
||||
"nav_meta": {
|
||||
"dashboard": "Обзор работы",
|
||||
"ai_providers": "Маршрутизация моделей",
|
||||
"auth_files": "Файлы и учётные данные",
|
||||
"oauth": "Авторизация OAuth",
|
||||
"quota_management": "API ключи и лимиты",
|
||||
"logs": "Трассировка запросов",
|
||||
"config_management": "Базовая конфигурация шлюза",
|
||||
"system_info": "Состояние и диагностика"
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Панель управления",
|
||||
"subtitle": "Добро пожаловать в Центр управления CLI Proxy API",
|
||||
|
||||
@@ -120,6 +120,22 @@
|
||||
"logs": "日志查看",
|
||||
"system_info": "中心信息"
|
||||
},
|
||||
"nav_groups": {
|
||||
"operate": "运行",
|
||||
"gateway": "网关",
|
||||
"observe": "观测",
|
||||
"control": "控制"
|
||||
},
|
||||
"nav_meta": {
|
||||
"dashboard": "运行总览",
|
||||
"ai_providers": "上游模型路由",
|
||||
"auth_files": "Auth 文件与凭证",
|
||||
"oauth": "OAuth 授权登录",
|
||||
"quota_management": "API Key 与限额",
|
||||
"logs": "请求追踪与排查",
|
||||
"config_management": "网关基础配置",
|
||||
"system_info": "运行与诊断信息"
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "仪表盘",
|
||||
"subtitle": "欢迎使用 CLI Proxy API 管理中心",
|
||||
|
||||
@@ -120,6 +120,22 @@
|
||||
"logs": "記錄檢視",
|
||||
"system_info": "中心資訊"
|
||||
},
|
||||
"nav_groups": {
|
||||
"operate": "運行",
|
||||
"gateway": "閘道",
|
||||
"observe": "觀測",
|
||||
"control": "控制"
|
||||
},
|
||||
"nav_meta": {
|
||||
"dashboard": "運行總覽",
|
||||
"ai_providers": "上游模型路由",
|
||||
"auth_files": "Auth 檔案與憑證",
|
||||
"oauth": "OAuth 授權登入",
|
||||
"quota_management": "API Key 與限額",
|
||||
"logs": "請求追蹤與排查",
|
||||
"config_management": "閘道基礎設定",
|
||||
"system_info": "運行與診斷資訊"
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "儀表板",
|
||||
"subtitle": "歡迎使用 CLI Proxy API 管理中心",
|
||||
|
||||
+60
-3
@@ -3,7 +3,7 @@
|
||||
:root {
|
||||
--header-height: 80px;
|
||||
--shell-gutter: 24px;
|
||||
--sidebar-panel-width: 240px;
|
||||
--sidebar-panel-width: 256px;
|
||||
--sidebar-collapsed-width: 68px;
|
||||
--sidebar-active-width: var(--sidebar-panel-width);
|
||||
--sidebar-gap: 32px;
|
||||
@@ -428,6 +428,14 @@
|
||||
justify-content: center;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.nav-group-label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.nav-group-divider {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-brand {
|
||||
@@ -462,12 +470,36 @@
|
||||
.nav-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $spacing-sm;
|
||||
gap: 14px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.nav-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $spacing-xs;
|
||||
}
|
||||
|
||||
.nav-group-label {
|
||||
padding: 0 12px 4px;
|
||||
font-size: 10.5px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
color: color-mix(in srgb, var(--text-primary) 42%, transparent);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.nav-group-divider {
|
||||
display: none;
|
||||
height: 1px;
|
||||
width: 24px;
|
||||
margin: 4px auto;
|
||||
background: color-mix(in srgb, var(--border-color) 70%, transparent);
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
padding: 10px 12px;
|
||||
padding: 9px 12px;
|
||||
border-radius: 11px;
|
||||
border: 1px solid transparent;
|
||||
color: var(--text-primary);
|
||||
@@ -509,6 +541,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
.nav-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.nav-label {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
@@ -516,6 +555,16 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.nav-meta {
|
||||
margin-top: 2px;
|
||||
font-size: 11.5px;
|
||||
font-weight: 500;
|
||||
color: color-mix(in srgb, var(--text-primary) 50%, transparent);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: color-mix(in srgb, var(--text-primary) 7%, transparent);
|
||||
border-color: color-mix(in srgb, var(--border-color) 58%, transparent);
|
||||
@@ -527,6 +576,10 @@
|
||||
color-mix(in srgb, var(--bg-primary) 88%, transparent);
|
||||
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--border-hover) 86%, transparent);
|
||||
}
|
||||
|
||||
.nav-meta {
|
||||
color: color-mix(in srgb, var(--text-primary) 62%, transparent);
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
@@ -542,6 +595,10 @@
|
||||
color-mix(in srgb, var(--primary-color) 14%, var(--bg-primary));
|
||||
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--primary-color) 24%, transparent);
|
||||
}
|
||||
|
||||
.nav-meta {
|
||||
color: color-mix(in srgb, var(--text-primary) 68%, transparent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user