feat: 前端全面优化 — 懒加载/骨架屏/错误边界/面包屑/主题增强/Glass登录页/动画系统

- 新增: ErrorBoundary, PageTransition, Breadcrumb, SkeletonCard (4组件)
- 重构: App.tsx (React.lazy懒加载所有页面), index.css (55+主题变量/动画/工具类)
- 优化: MainLayout (CSS变量布局+Footer), Header (搜索增强+快捷键), Sidebar (折叠Tooltip+子菜单动画)
- 优化: Dashboard骨架屏+级联动画, Stats/Charts/Recent主题感知颜色
- 优化: Login.tsx Glass毛玻璃效果+密码显隐图标+光球动画
- 图表Recharts主题化(CustomTooltip/chart-grid/chart-axis)
- 减少动画偏好支持(prefers-reduced-motion)
This commit is contained in:
attacker committed 2026-06-24 02:06:07 +00:00
1 parent 3d7932c087
commit 722587ab84
14 files changed
+2026 -969

No files matched your search

+229 -75
View File
@@ -38,8 +38,10 @@
--dg-bg-secondary: #161b22;
--dg-bg-tertiary: #21262d;
--dg-bg-hover: #30363d;
--dg-bg-active: #3d444d;
--dg-border: #30363d;
--dg-border-light: #21262d;
--dg-border-hover: #484f58;
--dg-text-primary: #f0f6fc;
--dg-text-secondary: #8b949e;
--dg-text-muted: #6e7681;
@@ -56,6 +58,15 @@
--dg-gradient-text-start: #60a5fa;
--dg-gradient-text-end: #c084fc;
--dg-input-focus-ring: rgba(96, 165, 250, 0.2);
--chart-grid: #1e293b;
--chart-axis: #475569;
--chart-tooltip-bg: #0f172a;
--chart-tooltip-border: #1e293b;
--shadow-card: 0 1px 3px rgba(0, 0, 0, 0.3), 0 0 0 1px rgba(255, 255, 255, 0.02);
--shadow-card-hover: 0 8px 32px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(88, 166, 255, 0.15);
--shadow-dropdown: 0 10px 40px rgba(0, 0, 0, 0.5);
--skeleton-base: #21262d;
--skeleton-highlight: #30363d;
}
/* ===== Theme 2: Light Professional (明亮白) ===== */
@@ -93,8 +104,10 @@
--dg-bg-secondary: #f8f9fb;
--dg-bg-tertiary: #f0f1f5;
--dg-bg-hover: #e8eaef;
--dg-bg-active: #dfe1e8;
--dg-border: #e2e5eb;
--dg-border-light: #eaecf0;
--dg-border-hover: #d0d5dd;
--dg-text-primary: #1a1d23;
--dg-text-secondary: #5c6370;
--dg-text-muted: #9ca3af;
@@ -111,6 +124,15 @@
--dg-gradient-text-start: #1d4ed8;
--dg-gradient-text-end: #6d28d9;
--dg-input-focus-ring: rgba(37, 99, 235, 0.15);
--chart-grid: #e2e5eb;
--chart-axis: #9ca3af;
--chart-tooltip-bg: #ffffff;
--chart-tooltip-border: #e2e5eb;
--shadow-card: 0 1px 3px rgba(0, 0, 0, 0.06), 0 0 0 1px rgba(0, 0, 0, 0.04);
--shadow-card-hover: 0 8px 32px rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(37, 99, 235, 0.1);
--shadow-dropdown: 0 10px 40px rgba(0, 0, 0, 0.12);
--skeleton-base: #f0f1f5;
--skeleton-highlight: #e8eaef;
}
/* ===== Theme 3: Midnight Purple (午夜紫) ===== */
@@ -147,8 +169,10 @@
--dg-bg-secondary: #130c21;
--dg-bg-tertiary: #1c1330;
--dg-bg-hover: #2d1f4a;
--dg-bg-active: #3d2a5e;
--dg-border: #2d1f4a;
--dg-border-light: #1c1330;
--dg-border-hover: #3d2a5e;
--dg-text-primary: #faf5ff;
--dg-text-secondary: #b794c7;
--dg-text-muted: #7c5a8a;
@@ -165,6 +189,15 @@
--dg-gradient-text-start: #a855f7;
--dg-gradient-text-end: #c084fc;
--dg-input-focus-ring: rgba(192, 132, 252, 0.2);
--chart-grid: #2d1f4a;
--chart-axis: #7c5a8a;
--chart-tooltip-bg: #130c21;
--chart-tooltip-border: #2d1f4a;
--shadow-card: 0 1px 3px rgba(0, 0, 0, 0.3), 0 0 0 1px rgba(192, 132, 252, 0.05);
--shadow-card-hover: 0 8px 32px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(192, 132, 252, 0.2);
--shadow-dropdown: 0 10px 40px rgba(0, 0, 0, 0.5);
--skeleton-base: #1c1330;
--skeleton-highlight: #2d1f4a;
}
/* ===== Theme 4: Pine Green (松林绿) ===== */
@@ -201,8 +234,10 @@
--dg-bg-secondary: #111f1a;
--dg-bg-tertiary: #192c24;
--dg-bg-hover: #253d32;
--dg-bg-active: #2f4f3e;
--dg-border: #253d32;
--dg-border-light: #192c24;
--dg-border-hover: #2f4f3e;
--dg-text-primary: #f0fdf4;
--dg-text-secondary: #86b8a0;
--dg-text-muted: #5a7c6b;
@@ -219,6 +254,15 @@
--dg-gradient-text-start: #059669;
--dg-gradient-text-end: #34d399;
--dg-input-focus-ring: rgba(74, 222, 128, 0.2);
--chart-grid: #253d32;
--chart-axis: #5a7c6b;
--chart-tooltip-bg: #111f1a;
--chart-tooltip-border: #253d32;
--shadow-card: 0 1px 3px rgba(0, 0, 0, 0.3), 0 0 0 1px rgba(74, 222, 128, 0.05);
--shadow-card-hover: 0 8px 32px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(74, 222, 128, 0.2);
--shadow-dropdown: 0 10px 40px rgba(0, 0, 0, 0.5);
--skeleton-base: #192c24;
--skeleton-highlight: #253d32;
}
/* ===== Theme 5: Orange Vitality (活力橙) ===== */
@@ -255,8 +299,10 @@
--dg-bg-secondary: #1d1610;
--dg-bg-tertiary: #2b1f16;
--dg-bg-hover: #3d2d20;
--dg-bg-active: #4d3928;
--dg-border: #3d2d20;
--dg-border-light: #2b1f16;
--dg-border-hover: #4d3928;
--dg-text-primary: #fef7ed;
--dg-text-secondary: #d4a574;
--dg-text-muted: #9c7a54;
@@ -273,6 +319,15 @@
--dg-gradient-text-start: #f97316;
--dg-gradient-text-end: #fbbf24;
--dg-input-focus-ring: rgba(251, 146, 60, 0.2);
--chart-grid: #3d2d20;
--chart-axis: #9c7a54;
--chart-tooltip-bg: #1d1610;
--chart-tooltip-border: #3d2d20;
--shadow-card: 0 1px 3px rgba(0, 0, 0, 0.3), 0 0 0 1px rgba(251, 146, 60, 0.05);
--shadow-card-hover: 0 8px 32px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(251, 146, 60, 0.2);
--shadow-dropdown: 0 10px 40px rgba(0, 0, 0, 0.5);
--skeleton-base: #2b1f16;
--skeleton-highlight: #3d2d20;
}
}
@@ -287,7 +342,7 @@
body {
@apply bg-[var(--dg-bg-primary)] text-[var(--dg-text-primary)] antialiased;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans SC', sans-serif;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans SC', 'PingFang SC', 'Microsoft YaHei', sans-serif;
font-feature-settings: 'rlig' 1, 'calt' 1;
transition: background-color 0.3s ease, color 0.3s ease;
}
@@ -302,7 +357,13 @@
code,
pre,
.font-mono {
font-family: 'JetBrains Mono', 'Fira Code', 'Consolas', monospace;
font-family: 'JetBrains Mono', 'Fira Code', 'Consolas', 'SF Mono', monospace;
}
/* ===== Selection Color (theme-aware) ===== */
::selection {
background-color: var(--dg-blue);
color: white;
}
}
@@ -310,19 +371,24 @@
/* ===== Design System Card / Input ===== */
.dg-card {
@apply bg-[var(--dg-bg-secondary)] border border-[var(--dg-border)] rounded-xl;
transition: background-color 0.3s ease, border-color 0.3s ease;
box-shadow: var(--shadow-card);
transition: background-color 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease, transform 0.3s ease;
}
.dg-card-hover {
@apply transition-all duration-300 hover:-translate-y-0.5;
@apply transition-all duration-300;
}
.dg-card-hover:hover {
border-color: var(--dg-card-hover-border);
box-shadow: var(--shadow-card-hover);
transform: translateY(-2px);
}
.dg-input {
@apply bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] placeholder:text-[var(--dg-text-muted)] focus:border-[var(--dg-blue)] focus:ring-1;
@apply bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] placeholder:text-[var(--dg-text-muted)] focus:border-[var(--dg-blue)] focus:ring-1 rounded-lg;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.dg-input:focus {
--tw-ring-color: var(--dg-input-focus-ring);
box-shadow: 0 0 0 3px var(--dg-input-focus-ring);
}
.dg-text-muted {
color: var(--dg-text-muted);
@@ -343,21 +409,42 @@
background: linear-gradient(to bottom right, var(--dg-gradient-start), var(--dg-gradient-end));
}
/* ===== Page Container ===== */
.dg-page {
@apply space-y-6 animate-fade-in;
}
.dg-page-header {
@apply flex items-center justify-between flex-wrap gap-4;
}
.dg-page-title {
@apply text-2xl font-bold text-[var(--dg-text-primary)] tracking-tight;
}
.dg-page-subtitle {
@apply text-sm text-[var(--dg-text-muted)] mt-1;
}
.dg-page-actions {
@apply flex items-center gap-2 flex-wrap;
}
/* ===== Buttons ===== */
.dg-btn {
@apply inline-flex items-center justify-center gap-2 px-4 py-2 rounded-lg text-sm font-medium transition-all duration-200;
}
.dg-btn-primary {
@apply dg-btn dg-gradient-bg text-white hover:opacity-90 active:scale-[0.98];
box-shadow: 0 4px 16px color-mix(in srgb, var(--dg-gradient-start) 25%, transparent);
}
.dg-btn-primary:hover {
box-shadow: 0 6px 24px color-mix(in srgb, var(--dg-gradient-start) 35%, transparent);
}
.dg-btn-secondary {
@apply dg-btn bg-[var(--dg-bg-tertiary)] text-[var(--dg-text-primary)] hover:bg-[var(--dg-bg-hover)] active:scale-[0.98];
@apply dg-btn bg-[var(--dg-bg-tertiary)] text-[var(--dg-text-primary)] border border-[var(--dg-border)] hover:bg-[var(--dg-bg-hover)] hover:border-[var(--dg-border-hover)] active:scale-[0.98];
}
.dg-btn-ghost {
@apply dg-btn text-[var(--dg-text-secondary)] hover:bg-[var(--dg-bg-hover)] hover:text-[var(--dg-text-primary)];
}
.dg-btn-danger {
@apply dg-btn bg-red-500/10 text-red-400 hover:bg-red-500/20 active:scale-[0.98];
@apply dg-btn bg-red-500/10 text-red-400 border border-red-500/20 hover:bg-red-500/20 active:scale-[0.98];
}
.dg-btn-sm {
@apply px-3 py-1.5 text-xs;
@@ -392,7 +479,24 @@
@apply dg-badge bg-cyan-500/10 text-cyan-400;
}
.dg-badge-muted {
@apply dg-badge bg-[var(--dg-bg-tertiary)] text-[var(--dg-text-secondary)];
@apply dg-badge bg-[var(--dg-bg-tertiary)] text-[var(--dg-text-secondary)] border border-[var(--dg-border)];
}
/* ===== Status Badges ===== */
.dg-status-running {
@apply dg-badge bg-green-500/10 text-green-400 border border-green-500/20;
}
.dg-status-stopped {
@apply dg-badge bg-gray-500/10 text-gray-400 border border-gray-500/20;
}
.dg-status-warning {
@apply dg-badge bg-yellow-500/10 text-yellow-400 border border-yellow-500/20;
}
.dg-status-error {
@apply dg-badge bg-red-500/10 text-red-400 border border-red-500/20;
}
.dg-status-info {
@apply dg-badge bg-blue-500/10 text-blue-400 border border-blue-500/20;
}
/* ===== Scrollbar ===== */
@@ -414,56 +518,70 @@
.scrollbar-thin::-webkit-scrollbar-thumb:hover {
background: var(--dg-text-muted);
}
[data-theme='light-professional'] .scrollbar-thin::-webkit-scrollbar-thumb,
[data-theme='light'] .scrollbar-thin::-webkit-scrollbar-thumb {
background: #d1d5db;
}
[data-theme='light-professional'] .scrollbar-thin::-webkit-scrollbar-thumb:hover,
[data-theme='light'] .scrollbar-thin::-webkit-scrollbar-thumb:hover {
background: #9ca3af;
}
/* ===== Animations ===== */
@keyframes fade-in {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}
.animate-fade-in {
animation: fade-in 0.35s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
.animate-fade-in-delay-1 {
animation: fade-in 0.35s cubic-bezier(0.16, 1, 0.3, 1) 0.05s both;
}
.animate-fade-in-delay-2 {
animation: fade-in 0.35s cubic-bezier(0.16, 1, 0.3, 1) 0.1s both;
}
.animate-fade-in-delay-3 {
animation: fade-in 0.35s cubic-bezier(0.16, 1, 0.3, 1) 0.15s both;
}
.animate-fade-in-delay-4 {
animation: fade-in 0.35s cubic-bezier(0.16, 1, 0.3, 1) 0.2s both;
}
@keyframes scale-in {
from { opacity: 0; transform: scale(0.95); }
to { opacity: 1; transform: scale(1); }
}
.animate-scale-in {
animation: scale-in 0.25s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
@keyframes slide-in-right {
from { opacity: 0; transform: translateX(20px); }
to { opacity: 1; transform: translateX(0); }
}
.animate-slide-in-right {
animation: slide-in-right 0.3s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
@keyframes pulse-ring {
0% {
transform: scale(0.8);
opacity: 0.5;
}
100% {
transform: scale(1.2);
opacity: 0;
}
0% { transform: scale(0.8); opacity: 0.5; }
100% { transform: scale(1.2); opacity: 0; }
}
.animate-pulse-ring {
animation: pulse-ring 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(8px);
}
to {
opacity: 1;
transform: translateY(0);
}
/* Skeleton shimmer */
@keyframes shimmer {
0% { background-position: -200% 0; }
100% { background-position: 200% 0; }
}
@keyframes scaleIn {
from {
opacity: 0;
transform: scale(0.95);
}
to {
opacity: 1;
transform: scale(1);
}
.dg-skeleton {
background: linear-gradient(90deg, var(--skeleton-base) 25%, var(--skeleton-highlight) 50%, var(--skeleton-base) 75%);
background-size: 200% 100%;
animation: shimmer 1.5s ease-in-out infinite;
border-radius: 0.5rem;
}
/* ===== Glass effect ===== */
.glass {
background: rgba(15, 23, 42, 0.8);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
background: color-mix(in srgb, var(--dg-bg-secondary) 75%, transparent);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
border: 1px solid var(--dg-border);
}
/* ===== Status indicators ===== */
@@ -483,29 +601,41 @@
@apply bg-red-400;
}
/* Badge variants (legacy) */
.badge-critical {
@apply border-red-500/30 text-red-400 bg-red-500/10;
/* ===== Chart theme-aware utilities ===== */
.chart-grid-stroke {
stroke: var(--chart-grid);
}
.badge-warning {
@apply border-amber-500/30 text-amber-400 bg-amber-500/10;
.chart-axis-stroke {
stroke: var(--chart-axis);
}
.badge-success {
@apply border-emerald-500/30 text-emerald-400 bg-emerald-500/10;
}
.badge-info {
@apply border-blue-500/30 text-blue-400 bg-blue-500/10;
.chart-tooltip {
background-color: var(--chart-tooltip-bg) !important;
border: 1px solid var(--chart-tooltip-border) !important;
border-radius: 6px !important;
color: var(--dg-text-primary) !important;
box-shadow: var(--shadow-dropdown) !important;
}
/* Card hover effect (legacy) */
.card-hover {
@apply transition-all duration-300 hover:shadow-lg hover:shadow-slate-900/50 hover:-translate-y-0.5;
/* ===== Table improvements ===== */
.dg-table-row {
@apply transition-colors duration-150;
}
.dg-table-row:hover {
background-color: color-mix(in srgb, var(--dg-bg-hover) 50%, transparent);
}
/* ===== Focus ring (theme-aware) ===== */
.dg-focus-ring:focus-visible {
outline: none;
box-shadow: 0 0 0 2px var(--dg-bg-primary), 0 0 0 4px var(--dg-blue);
}
}
/* ===== Global element styling (kept for backward compatibility) ===== */
/* ===== Global element styling ===== */
pre {
@apply bg-slate-950 rounded-lg p-4 overflow-x-auto;
@apply rounded-lg p-4 overflow-x-auto;
background-color: var(--dg-bg-tertiary);
border: 1px solid var(--dg-border);
}
code {
@@ -517,17 +647,21 @@ table {
}
th {
@apply text-left font-medium text-slate-400 text-xs uppercase tracking-wider;
@apply text-left font-medium text-xs uppercase tracking-wider;
color: var(--dg-text-secondary);
}
td {
@apply text-sm;
color: var(--dg-text-primary);
}
input:focus,
textarea:focus,
select:focus {
@apply outline-none ring-2 ring-blue-500/30 border-blue-500/50;
outline: none;
box-shadow: 0 0 0 2px var(--dg-bg-primary), 0 0 0 4px var(--dg-blue);
border-color: var(--dg-blue);
}
button {
@@ -536,26 +670,36 @@ button {
/* Terminal styling */
.terminal {
@apply bg-slate-950 font-mono text-sm text-slate-300;
@apply font-mono text-sm;
background-color: var(--dg-bg-primary);
color: var(--dg-text-secondary);
}
.terminal .prompt {
@apply text-emerald-400;
color: var(--dg-green);
}
.terminal .command {
@apply text-slate-100;
color: var(--dg-text-primary);
}
.terminal .output {
@apply text-slate-400;
color: var(--dg-text-muted);
}
/* Chart tooltip styling */
/* Chart tooltip styling - theme aware override */
.recharts-tooltip-wrapper {
@apply z-50;
}
.recharts-default-tooltip {
background-color: var(--chart-tooltip-bg) !important;
border-color: var(--chart-tooltip-border) !important;
color: var(--dg-text-primary) !important;
border-radius: 6px !important;
box-shadow: var(--shadow-dropdown) !important;
}
/* Dialog overlay */
[data-state='open'] > [data-radix-dialog-overlay] {
@apply bg-slate-950/80 backdrop-blur-sm;
@apply backdrop-blur-sm;
background-color: color-mix(in srgb, var(--dg-bg-primary) 80%, transparent);
}
/* Dropdown menu styling */
@@ -565,16 +709,26 @@ button {
/* Progress bar animation */
@keyframes progress-indeterminate {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(100%);
}
0% { transform: translateX(-100%); }
100% { transform: translateX(100%); }
}
.progress-indeterminate::after {
content: '';
@apply absolute inset-0 bg-current;
animation: progress-indeterminate 1.5s ease-in-out infinite;
}
/* ===== Reduced motion support ===== */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
.dg-skeleton {
animation: none;
background: var(--skeleton-base);
}
}