feat: 全面UI优化 — 5套主题系统 + 设计体系 + 菜单重组 + 页面优化

1. 主题系统扩展 (5套主题)
   - ocean-deep(深海蓝) / light-professional(明亮白) / midnight-purple(午夜紫)
   - pine-green(松林绿) / orange-vitality(活力橙)
   - useTheme hook 重写支持5主题 + 向后兼容
   - AppearanceSettings 5主题预览卡片

2. 设计体系 (index.css)
   - 按钮系统: dg-btn-primary/secondary/ghost/danger + 尺寸变体
   - 徽章系统: dg-badge-* (8色)
   - 卡片/表格: dg-card-section, dg-table-row, dg-table-header
   - 状态点: dg-status-dot-* (4色)
   - 动效关键帧: slideInLeft, scaleIn

3. 菜单重组 (menu.ts)
   - 5分组: 概览/数据源/工单/权限/系统
   - 6个图标更新: Code2/FileEdit/ShieldCheck/FolderLock/ScrollText/Timer
   - 新增 permission 权限码字段

4. Layout 侧边栏增强
   - 左侧渐变指示条 + 发光效果
   - hover 微动画 + 图标变色
   - 面包屑导航头部
   - 主题切换循环5主题

5. 核心页面优化
   - TicketsPage: 设计系统类 + 颜色对齐
   - QueriesPage: 去Card依赖 + dg-table/dg-btn统一
   - Breadcrumb组件 + useBreadcrumb hook
This commit is contained in:
logan committed 2026-05-27 01:59:08 +08:00
1 parent 4e2df4d3d7
commit 746a000e8a
25 files changed
+7718 -178

No files matched your search

+37 -17
View File
@@ -1,7 +1,7 @@
import { useState, useEffect, useCallback } from 'react';
import { useTheme } from '@/hooks/useTheme';
import { useTheme, THEME_OPTIONS, type Theme } from '@/hooks/useTheme';
import { LOGIN_STYLE_OPTIONS, type LoginStyle } from '@/pages/LoginPage';
import { Sun, Moon, Check, Save, Loader2 } from 'lucide-react';
import { Check, Save, Loader2 } from 'lucide-react';
import api from '@/api/request';
import { toast } from '@/hooks/useToast';
@@ -16,7 +16,15 @@ export default function AppearanceSettings() {
useEffect(() => {
api.get<{ theme?: string; login_style?: string }>('/user/preferences')
.then(prefs => {
if (prefs?.theme) setTheme(prefs.theme as 'dark' | 'light');
if (prefs?.theme) {
// Backward compat: map old values
const t = prefs.theme;
if (t === 'dark' || t === 'light') {
setTheme(t === 'dark' ? 'ocean-deep' : 'light-professional');
} else if (THEME_OPTIONS.some(o => o.id === t)) {
setTheme(t as Theme);
}
}
if (prefs?.login_style) setLoginStyle(prefs.login_style as LoginStyle);
})
.catch(() => {
@@ -27,7 +35,7 @@ export default function AppearanceSettings() {
.finally(() => setLoading(false));
}, []);
const handleThemeChange = (t: 'dark' | 'light') => {
const handleThemeChange = (t: Theme) => {
setTheme(t);
setDirty(true);
};
@@ -66,24 +74,36 @@ export default function AppearanceSettings() {
<div className="space-y-8">
{/* Theme Mode */}
<div>
<h3 className="text-sm font-semibold mb-3">主题模式</h3>
<div className="flex gap-3">
{([
{ id: 'dark' as const, icon: Moon, label: '深色模式' },
{ id: 'light' as const, icon: Sun, label: '浅色模式' },
]).map(opt => (
<h3 className="text-sm font-semibold mb-1">主题模式</h3>
<p className="text-xs text-[var(--dg-text-muted)] mb-4">选择界面配色方案,即时预览效果</p>
<div className="grid grid-cols-5 gap-3">
{THEME_OPTIONS.map(opt => (
<button
key={opt.id}
onClick={() => handleThemeChange(opt.id)}
className={`flex items-center gap-2 px-5 py-3 rounded-xl text-sm font-medium transition-all ${
theme === opt.id
? 'bg-blue-500/15 text-blue-400 ring-1 ring-blue-500/30'
: 'bg-[var(--dg-bg-tertiary)] text-[var(--dg-text-secondary)] hover:bg-[var(--dg-bg-hover)]'
className={`group relative rounded-xl p-4 text-left transition-all bg-[var(--dg-bg-tertiary)] hover:bg-[var(--dg-bg-hover)] ${
theme === opt.id ? 'ring-2 ring-blue-500/50' : ''
}`}
>
<opt.icon className="w-4 h-4" />
{opt.label}
{theme === opt.id && <Check className="w-3.5 h-3.5 ml-1" />}
{/* Color preview swatch */}
<div
className="w-full h-6 rounded-md mb-3"
style={{
background: opt.isDark
? `linear-gradient(135deg, ${opt.primaryColor} 0%, color-mix(in srgb, ${opt.primaryColor} 40%, #1a1a2e) 50%, #0d1117 100%)`
: `linear-gradient(135deg, ${opt.primaryColor} 0%, color-mix(in srgb, ${opt.primaryColor} 60%, #ffffff) 50%, #f8fafc 100%)`,
}}
/>
{/* Active check mark */}
{theme === opt.id && (
<div className="absolute top-2 right-2 w-5 h-5 rounded-full bg-blue-500 flex items-center justify-center">
<Check className="w-3 h-3 text-white" />
</div>
)}
<h4 className="text-sm font-medium mb-0.5">{opt.name}</h4>
<p className="text-xs text-[var(--dg-text-muted)] leading-snug">{opt.description}</p>
</button>
))}
</div>