feat(dashboard): add dashboard page with stats and splash screen

This commit is contained in:
Supra4E8C
2025-12-21 16:05:09 +08:00
parent f6563490a6
commit a9df58cba7
9 changed files with 601 additions and 3 deletions

View File

@@ -0,0 +1,106 @@
@use 'sass:color';
@use '../../styles/variables.scss' as *;
.splash-screen {
position: fixed;
inset: 0;
z-index: 9999;
display: flex;
align-items: center;
justify-content: center;
background: var(--bg-primary);
opacity: 1;
transition: opacity 0.4s ease-out;
&.fade-out {
opacity: 0;
pointer-events: none;
}
}
.splash-content {
display: flex;
flex-direction: column;
align-items: center;
gap: $spacing-md;
animation: splash-enter 0.6s ease-out;
}
@keyframes splash-enter {
from {
opacity: 0;
transform: scale(0.9) translateY(20px);
}
to {
opacity: 1;
transform: scale(1) translateY(0);
}
}
.splash-logo {
width: 80px;
height: 80px;
border-radius: $radius-lg;
box-shadow: $shadow-lg;
animation: splash-logo-pulse 1.5s ease-in-out infinite;
}
@keyframes splash-logo-pulse {
0%, 100% {
transform: scale(1);
}
50% {
transform: scale(1.05);
}
}
.splash-title {
font-size: 28px;
font-weight: 800;
color: var(--text-primary);
margin: 0;
letter-spacing: -0.5px;
}
.splash-subtitle {
font-size: 16px;
font-weight: 500;
color: var(--text-secondary);
margin: 0;
margin-top: -8px;
}
.splash-loader {
width: 120px;
height: 3px;
background: var(--border-color);
border-radius: $radius-full;
overflow: hidden;
margin-top: $spacing-md;
}
.splash-loader-bar {
width: 100%;
height: 100%;
background: var(--primary-color);
border-radius: $radius-full;
animation: splash-loading 1.2s ease-in-out infinite;
transform-origin: left;
}
@keyframes splash-loading {
0% {
transform: scaleX(0);
}
50% {
transform: scaleX(1);
transform-origin: left;
}
50.01% {
transform-origin: right;
}
100% {
transform: scaleX(0);
transform-origin: right;
}
}

View File

@@ -0,0 +1,40 @@
import { useEffect, useState } from 'react';
import { INLINE_LOGO_JPEG } from '@/assets/logoInline';
import './SplashScreen.scss';
interface SplashScreenProps {
onFinish: () => void;
duration?: number;
}
export function SplashScreen({ onFinish, duration = 1500 }: SplashScreenProps) {
const [fadeOut, setFadeOut] = useState(false);
useEffect(() => {
const fadeTimer = setTimeout(() => {
setFadeOut(true);
}, duration - 400);
const finishTimer = setTimeout(() => {
onFinish();
}, duration);
return () => {
clearTimeout(fadeTimer);
clearTimeout(finishTimer);
};
}, [duration, onFinish]);
return (
<div className={`splash-screen ${fadeOut ? 'fade-out' : ''}`}>
<div className="splash-content">
<img src={INLINE_LOGO_JPEG} alt="CPAMC" className="splash-logo" />
<h1 className="splash-title">CLI Proxy API</h1>
<p className="splash-subtitle">Management Center</p>
<div className="splash-loader">
<div className="splash-loader-bar" />
</div>
</div>
</div>
);
}

View File

@@ -8,6 +8,7 @@ import {
IconFileText,
IconInfo,
IconKey,
IconLayoutDashboard,
IconScrollText,
IconSettings,
IconShield,
@@ -18,6 +19,7 @@ import { useAuthStore, useConfigStore, useLanguageStore, useNotificationStore, u
import { versionApi } from '@/services/api';
const sidebarIcons: Record<string, ReactNode> = {
dashboard: <IconLayoutDashboard size={18} />,
settings: <IconSlidersHorizontal size={18} />,
apiKeys: <IconKey size={18} />,
aiProviders: <IconBot size={18} />,
@@ -230,6 +232,7 @@ export function MainLayout() {
: 'muted';
const navItems = [
{ path: '/', label: t('nav.dashboard'), icon: sidebarIcons.dashboard },
{ path: '/settings', label: t('nav.basic_settings'), icon: sidebarIcons.settings },
{ path: '/api-keys', label: t('nav.api_keys'), icon: sidebarIcons.apiKeys },
{ path: '/ai-providers', label: t('nav.ai_providers'), icon: sidebarIcons.aiProviders },

View File

@@ -303,3 +303,14 @@ export function IconCode({ size = 20, ...props }: IconProps) {
</svg>
);
}
export function IconLayoutDashboard({ size = 20, ...props }: IconProps) {
return (
<svg {...baseSvgProps} width={size} height={size} {...props}>
<rect width="7" height="9" x="3" y="3" rx="1" />
<rect width="7" height="5" x="14" y="3" rx="1" />
<rect width="7" height="9" x="14" y="12" rx="1" />
<rect width="7" height="5" x="3" y="16" rx="1" />
</svg>
);
}