mirror of
https://github.com/router-for-me/Cli-Proxy-API-Management-Center.git
synced 2026-06-16 21:03:58 +08:00
Compare commits
1 Commits
@@ -13,15 +13,15 @@ import { Button } from '@/components/ui/Button';
|
||||
import { PageTransition } from '@/components/common/PageTransition';
|
||||
import { MainRoutes } from '@/router/MainRoutes';
|
||||
import {
|
||||
IconBot,
|
||||
IconChartLine,
|
||||
IconFileText,
|
||||
IconInfo,
|
||||
IconLayoutDashboard,
|
||||
IconScrollText,
|
||||
IconSettings,
|
||||
IconShield,
|
||||
IconTimer,
|
||||
IconSidebarAuthFiles,
|
||||
IconSidebarConfig,
|
||||
IconSidebarDashboard,
|
||||
IconSidebarLogs,
|
||||
IconSidebarOauth,
|
||||
IconSidebarProviders,
|
||||
IconSidebarQuota,
|
||||
IconSidebarSystem,
|
||||
IconSidebarUsage,
|
||||
} from '@/components/ui/icons';
|
||||
import { INLINE_LOGO_JPEG } from '@/assets/logoInline';
|
||||
import {
|
||||
@@ -38,15 +38,15 @@ import { isSupportedLanguage } from '@/utils/language';
|
||||
import type { Theme } from '@/types';
|
||||
|
||||
const sidebarIcons: Record<string, ReactNode> = {
|
||||
dashboard: <IconLayoutDashboard size={18} />,
|
||||
aiProviders: <IconBot size={18} />,
|
||||
authFiles: <IconFileText size={18} />,
|
||||
oauth: <IconShield size={18} />,
|
||||
quota: <IconTimer size={18} />,
|
||||
usage: <IconChartLine size={18} />,
|
||||
config: <IconSettings size={18} />,
|
||||
logs: <IconScrollText size={18} />,
|
||||
system: <IconInfo size={18} />,
|
||||
dashboard: <IconSidebarDashboard size={18} />,
|
||||
aiProviders: <IconSidebarProviders size={18} />,
|
||||
authFiles: <IconSidebarAuthFiles size={18} />,
|
||||
oauth: <IconSidebarOauth size={18} />,
|
||||
quota: <IconSidebarQuota size={18} />,
|
||||
usage: <IconSidebarUsage size={18} />,
|
||||
config: <IconSidebarConfig size={18} />,
|
||||
logs: <IconSidebarLogs size={18} />,
|
||||
system: <IconSidebarSystem size={18} />,
|
||||
};
|
||||
|
||||
// Header action icons - smaller size for header buttons
|
||||
@@ -132,7 +132,13 @@ const headerIcons = {
|
||||
</clipPath>
|
||||
</defs>
|
||||
<circle cx="12" cy="12" r="4" />
|
||||
<circle cx="12" cy="12" r="4" clipPath="url(#mainLayoutAutoThemeSunLeftHalf)" fill="currentColor" />
|
||||
<circle
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="4"
|
||||
clipPath="url(#mainLayoutAutoThemeSunLeftHalf)"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path d="M12 2v2" />
|
||||
<path d="M12 20v2" />
|
||||
<path d="M4.93 4.93l1.41 1.41" />
|
||||
@@ -171,17 +177,35 @@ const THEME_CARDS: Array<{
|
||||
{
|
||||
key: 'white',
|
||||
labelKey: 'theme.white',
|
||||
colors: { bg: '#ffffff', card: '#ffffff', border: '#e5e5e5', text: '#2d2a26', textMuted: '#a29c95' },
|
||||
colors: {
|
||||
bg: '#ffffff',
|
||||
card: '#ffffff',
|
||||
border: '#e5e5e5',
|
||||
text: '#2d2a26',
|
||||
textMuted: '#a29c95',
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'light',
|
||||
labelKey: 'theme.light',
|
||||
colors: { bg: '#faf9f5', card: '#f0eee8', border: '#e3e1db', text: '#2d2a26', textMuted: '#a29c95' },
|
||||
colors: {
|
||||
bg: '#faf9f5',
|
||||
card: '#f0eee8',
|
||||
border: '#e3e1db',
|
||||
text: '#2d2a26',
|
||||
textMuted: '#a29c95',
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'dark',
|
||||
labelKey: 'theme.dark',
|
||||
colors: { bg: '#151412', card: '#1d1b18', border: '#3a3530', text: '#f6f4f1', textMuted: '#9c958d' },
|
||||
colors: {
|
||||
bg: '#151412',
|
||||
card: '#1d1b18',
|
||||
border: '#3a3530',
|
||||
text: '#f6f4f1',
|
||||
textMuted: '#9c958d',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@@ -420,7 +444,6 @@ export function MainLayout() {
|
||||
});
|
||||
}, [fetchConfig]);
|
||||
|
||||
|
||||
const statusClass =
|
||||
connectionStatus === 'connected'
|
||||
? 'success'
|
||||
@@ -503,7 +526,7 @@ export function MainLayout() {
|
||||
clearCache();
|
||||
const results = await Promise.allSettled([
|
||||
fetchConfig(undefined, true),
|
||||
triggerHeaderRefresh()
|
||||
triggerHeaderRefresh(),
|
||||
]);
|
||||
const rejected = results.find((result) => result.status === 'rejected');
|
||||
if (rejected && rejected.status === 'rejected') {
|
||||
@@ -618,7 +641,10 @@ export function MainLayout() {
|
||||
>
|
||||
{headerIcons.update}
|
||||
</Button>
|
||||
<div className={`language-menu ${languageMenuOpen ? 'open' : ''}`} ref={languageMenuRef}>
|
||||
<div
|
||||
className={`language-menu ${languageMenuOpen ? 'open' : ''}`}
|
||||
ref={languageMenuRef}
|
||||
>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
@@ -631,7 +657,11 @@ export function MainLayout() {
|
||||
{headerIcons.language}
|
||||
</Button>
|
||||
{languageMenuOpen && (
|
||||
<div className="notification entering language-menu-popover" role="menu" aria-label={t('language.switch')}>
|
||||
<div
|
||||
className="notification entering language-menu-popover"
|
||||
role="menu"
|
||||
aria-label={t('language.switch')}
|
||||
>
|
||||
{LANGUAGE_ORDER.map((lang) => (
|
||||
<button
|
||||
key={lang}
|
||||
@@ -667,7 +697,11 @@ export function MainLayout() {
|
||||
: headerIcons.sun}
|
||||
</Button>
|
||||
{themeMenuOpen && (
|
||||
<div className="notification entering theme-menu-popover" role="menu" aria-label={t('theme.switch')}>
|
||||
<div
|
||||
className="notification entering theme-menu-popover"
|
||||
role="menu"
|
||||
aria-label={t('theme.switch')}
|
||||
>
|
||||
{THEME_CARDS.map((tc) => (
|
||||
<button
|
||||
key={tc.key}
|
||||
@@ -679,7 +713,10 @@ export function MainLayout() {
|
||||
>
|
||||
<div
|
||||
className="theme-card-preview"
|
||||
style={{ background: tc.colors.bg, border: `1px solid ${tc.colors.border}` }}
|
||||
style={{
|
||||
background: tc.colors.bg,
|
||||
border: `1px solid ${tc.colors.border}`,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="theme-card-header"
|
||||
|
||||
+191
-1
@@ -16,7 +16,15 @@ const baseSvgProps: SVGProps<SVGSVGElement> = {
|
||||
strokeLinecap: 'round',
|
||||
strokeLinejoin: 'round',
|
||||
'aria-hidden': 'true',
|
||||
focusable: 'false'
|
||||
focusable: 'false',
|
||||
};
|
||||
|
||||
const sidebarSvgProps: SVGProps<SVGSVGElement> = {
|
||||
...baseSvgProps,
|
||||
strokeWidth: 1.72,
|
||||
strokeLinecap: 'square',
|
||||
strokeLinejoin: 'miter',
|
||||
strokeMiterlimit: 10,
|
||||
};
|
||||
|
||||
export function IconSlidersHorizontal({ size = 20, ...props }: IconProps) {
|
||||
@@ -322,3 +330,185 @@ export function IconLayoutDashboard({ size = 20, ...props }: IconProps) {
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function IconSidebarDashboard({ size = 20, ...props }: IconProps) {
|
||||
return (
|
||||
<svg {...sidebarSvgProps} width={size} height={size} {...props}>
|
||||
<rect x="3.75" y="4.5" width="16.5" height="15" rx="1.5" />
|
||||
<path d="M3.75 9.25h16.5" />
|
||||
<path d="M10.5 9.25V19.5" />
|
||||
<rect
|
||||
x="6.1"
|
||||
y="12.1"
|
||||
width="2.3"
|
||||
height="2.3"
|
||||
rx="0.35"
|
||||
fill="currentColor"
|
||||
fillOpacity="0.16"
|
||||
/>
|
||||
<polyline points="13.1 15.8 15.2 13.6 16.8 15 18.35 11.95" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function IconSidebarConfig({ size = 20, ...props }: IconProps) {
|
||||
return (
|
||||
<svg {...sidebarSvgProps} width={size} height={size} {...props}>
|
||||
<path d="M5 8h14" />
|
||||
<path d="M5 16h14" />
|
||||
<path d="M9 6.1 10.9 8 9 9.9 7.1 8Z" fill="currentColor" fillOpacity="0.16" />
|
||||
<rect
|
||||
x="13.6"
|
||||
y="14.1"
|
||||
width="2.9"
|
||||
height="2.9"
|
||||
rx="0.45"
|
||||
fill="currentColor"
|
||||
fillOpacity="0.16"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function IconSidebarProviders({ size = 20, ...props }: IconProps) {
|
||||
return (
|
||||
<svg {...sidebarSvgProps} width={size} height={size} {...props}>
|
||||
<path d="M12 4.7 14.8 7.5 12 10.3 9.2 7.5Z" fill="currentColor" fillOpacity="0.16" />
|
||||
<rect x="4.6" y="13" width="3.8" height="3.8" rx="0.5" />
|
||||
<rect x="15.6" y="13" width="3.8" height="3.8" rx="0.5" />
|
||||
<rect x="10.1" y="16.5" width="3.8" height="3.8" rx="0.5" />
|
||||
<path d="M12 10.3v6.2" />
|
||||
<path d="M12 10.3 6.5 13" />
|
||||
<path d="M12 10.3 17.5 13" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function IconSidebarAuthFiles({ size = 20, ...props }: IconProps) {
|
||||
return (
|
||||
<svg {...sidebarSvgProps} width={size} height={size} {...props}>
|
||||
<path d="M7 4.5h7l3 3v11a2.5 2.5 0 0 1-2.5 2.5H7.5A2.5 2.5 0 0 1 5 18.5V7a2.5 2.5 0 0 1 2-2.5Z" />
|
||||
<path d="M14 4.5v3h3" />
|
||||
<path d="M8.4 10.9h5.7" />
|
||||
<path d="M8.4 14.2h4.4" />
|
||||
<path d="M15.9 14.6 18.3 17 15.9 19.4 13.5 17Z" fill="currentColor" fillOpacity="0.16" />
|
||||
<path d="m14.9 17 0.9 0.9 1.8-1.9" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function IconSidebarOauth({ size = 20, ...props }: IconProps) {
|
||||
return (
|
||||
<svg {...sidebarSvgProps} width={size} height={size} {...props}>
|
||||
<path d="M4.5 8.5h8.2" />
|
||||
<polyline points="10.1 5.6 13 8.5 10.1 11.4" />
|
||||
<path d="M19.5 15.5h-8.2" />
|
||||
<polyline points="13.9 12.6 11 15.5 13.9 18.4" />
|
||||
<path d="M12 9.4 14.6 12 12 14.6 9.4 12Z" fill="currentColor" fillOpacity="0.16" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function IconSidebarQuota({ size = 20, ...props }: IconProps) {
|
||||
return (
|
||||
<svg {...sidebarSvgProps} width={size} height={size} {...props}>
|
||||
<path d="M5 16.8a7 7 0 0 1 14 0" />
|
||||
<path d="m7.3 13.8 1.4-1.4" />
|
||||
<path d="M12 11V9" />
|
||||
<path d="m16.7 13.8-1.4-1.4" />
|
||||
<path d="M12 16.8 15.5 12.4" />
|
||||
<path d="M12 15.2 13.6 16.8 12 18.4 10.4 16.8Z" fill="currentColor" stroke="none" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function IconSidebarUsage({ size = 20, ...props }: IconProps) {
|
||||
return (
|
||||
<svg {...sidebarSvgProps} width={size} height={size} {...props}>
|
||||
<path d="M5 5v14a2 2 0 0 0 2 2h12" />
|
||||
<polyline points="7.4 15.5 10.2 12.3 12.7 13.8 16.1 9.1 18.4 10.8" />
|
||||
<rect
|
||||
x="9.55"
|
||||
y="11.65"
|
||||
width="1.3"
|
||||
height="1.3"
|
||||
rx="0.2"
|
||||
fill="currentColor"
|
||||
stroke="none"
|
||||
/>
|
||||
<rect
|
||||
x="12.05"
|
||||
y="13.15"
|
||||
width="1.3"
|
||||
height="1.3"
|
||||
rx="0.2"
|
||||
fill="currentColor"
|
||||
stroke="none"
|
||||
/>
|
||||
<rect
|
||||
x="15.45"
|
||||
y="8.45"
|
||||
width="1.3"
|
||||
height="1.3"
|
||||
rx="0.2"
|
||||
fill="currentColor"
|
||||
stroke="none"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function IconSidebarLogs({ size = 20, ...props }: IconProps) {
|
||||
return (
|
||||
<svg {...sidebarSvgProps} width={size} height={size} {...props}>
|
||||
<rect x="4" y="5" width="16" height="14" rx="1.5" />
|
||||
<path d="M4 9h16" />
|
||||
<rect
|
||||
x="6.1"
|
||||
y="6.35"
|
||||
width="1.15"
|
||||
height="1.15"
|
||||
rx="0.15"
|
||||
fill="currentColor"
|
||||
stroke="none"
|
||||
/>
|
||||
<rect
|
||||
x="8.55"
|
||||
y="6.35"
|
||||
width="1.15"
|
||||
height="1.15"
|
||||
rx="0.15"
|
||||
fill="currentColor"
|
||||
fillOpacity="0.45"
|
||||
stroke="none"
|
||||
/>
|
||||
<path d="m7.1 12.3 2.5 2-2.5 2" />
|
||||
<path d="M11.9 12.2h3.4" />
|
||||
<path d="M11.9 16.4h4.8" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function IconSidebarSystem({ size = 20, ...props }: IconProps) {
|
||||
return (
|
||||
<svg {...sidebarSvgProps} width={size} height={size} {...props}>
|
||||
<rect x="5" y="5" width="14" height="3.3" rx="0.8" />
|
||||
<rect x="5" y="10.35" width="14" height="3.3" rx="0.8" />
|
||||
<rect x="5" y="15.7" width="14" height="3.3" rx="0.8" />
|
||||
<rect x="6.8" y="6.05" width="1.1" height="1.1" rx="0.15" fill="currentColor" stroke="none" />
|
||||
<rect x="6.8" y="11.4" width="1.1" height="1.1" rx="0.15" fill="currentColor" stroke="none" />
|
||||
<rect
|
||||
x="6.8"
|
||||
y="16.75"
|
||||
width="1.1"
|
||||
height="1.1"
|
||||
rx="0.15"
|
||||
fill="currentColor"
|
||||
stroke="none"
|
||||
/>
|
||||
<path d="M10.4 6.6h5.2" />
|
||||
<path d="M10.4 11.95h5.2" />
|
||||
<path d="M10.4 17.3h5.2" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
+46
-10
@@ -81,7 +81,9 @@
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
transition: background $transition-fast, color $transition-fast;
|
||||
transition:
|
||||
background $transition-fast,
|
||||
color $transition-fast;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -118,7 +120,10 @@
|
||||
max-width: 320px;
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
transition: max-width 0.4s ease, opacity 0.4s ease, transform 0.4s ease;
|
||||
transition:
|
||||
max-width 0.4s ease,
|
||||
opacity 0.4s ease,
|
||||
transform 0.4s ease;
|
||||
}
|
||||
|
||||
.brand-abbr {
|
||||
@@ -126,7 +131,9 @@
|
||||
transform: translateX(12px);
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.4s ease, transform 0.4s ease;
|
||||
transition:
|
||||
opacity 0.4s ease,
|
||||
transform 0.4s ease;
|
||||
}
|
||||
|
||||
&.collapsed {
|
||||
@@ -220,7 +227,9 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
transition: background-color $transition-fast, color $transition-fast;
|
||||
transition:
|
||||
background-color $transition-fast,
|
||||
color $transition-fast;
|
||||
|
||||
&:hover {
|
||||
background: var(--bg-secondary);
|
||||
@@ -278,7 +287,9 @@
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
transition: border-color $transition-fast, background-color $transition-fast;
|
||||
transition:
|
||||
border-color $transition-fast,
|
||||
background-color $transition-fast;
|
||||
|
||||
&:hover {
|
||||
background: var(--bg-secondary);
|
||||
@@ -439,7 +450,9 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $spacing-lg;
|
||||
transition: width $transition-normal, transform $transition-normal;
|
||||
transition:
|
||||
width $transition-normal,
|
||||
transform $transition-normal;
|
||||
overflow-y: auto;
|
||||
flex-shrink: 0;
|
||||
height: 100%;
|
||||
@@ -464,22 +477,33 @@
|
||||
.nav-item {
|
||||
padding: 10px 12px;
|
||||
border-radius: $radius-md;
|
||||
border: 1px solid transparent;
|
||||
color: var(--text-primary);
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: $spacing-sm;
|
||||
cursor: pointer;
|
||||
transition: background $transition-fast, color $transition-fast;
|
||||
transition:
|
||||
background $transition-fast,
|
||||
color $transition-fast;
|
||||
|
||||
.nav-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
opacity: 0.9;
|
||||
opacity: 0.96;
|
||||
border-radius: 7px;
|
||||
background: linear-gradient(180deg, rgb(255 255 255 / 0.18), rgb(255 255 255 / 0))
|
||||
var(--bg-secondary);
|
||||
box-shadow: inset 0 0 0 1px var(--border-primary);
|
||||
transition:
|
||||
background $transition-fast,
|
||||
box-shadow $transition-fast,
|
||||
color $transition-fast;
|
||||
|
||||
svg {
|
||||
width: 18px;
|
||||
@@ -496,12 +520,24 @@
|
||||
|
||||
&:hover {
|
||||
background: var(--bg-secondary);
|
||||
|
||||
.nav-icon {
|
||||
background: linear-gradient(180deg, rgb(255 255 255 / 0.24), rgb(255 255 255 / 0))
|
||||
var(--bg-primary);
|
||||
box-shadow: inset 0 0 0 1px var(--border-hover);
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: rgba($primary-color, 0.14);
|
||||
color: var(--primary-color);
|
||||
border: 1px solid rgba($primary-color, 0.35);
|
||||
|
||||
.nav-icon {
|
||||
background: linear-gradient(180deg, rgb(255 255 255 / 0.22), rgb(255 255 255 / 0))
|
||||
rgba($primary-color, 0.1);
|
||||
box-shadow: inset 0 0 0 1px rgba($primary-color, 0.26);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user