Some checks failed
CI Check / check (push) Has been cancelled
- 创建统一的Modal组件,采用Apple风格设计 - 实现毛玻璃效果、圆角、动画和细边框描边 - 修复弹窗打开时背景滚动问题 - 修复分类管理中重复卡片和数组字符串显示问题 - 优化选项解析逻辑,添加去重和过滤机制
212 lines
12 KiB
TypeScript
212 lines
12 KiB
TypeScript
/**
|
||
* 统一的 UI 样式工具类
|
||
* 苹果风格 + 炫彩元素
|
||
*
|
||
* ⚠️ 注意:此文件已重构为支持两套独立配置(浅色/深色)
|
||
*
|
||
* 推荐使用方式:
|
||
* 1. 在新组件中使用 `useThemeStyles()` Hook(推荐)
|
||
* 2. 在服务端组件或需要向后兼容的地方,继续使用此文件的导出
|
||
*
|
||
* 迁移步骤:
|
||
* - 将组件改为客户端组件('use client')
|
||
* - 使用 `const styles = useThemeStyles()` 替代直接导入
|
||
* - 使用 `styles.cardStyles.base` 替代 `cardStyles.base`
|
||
*/
|
||
|
||
import { gradientBackgrounds, glowEffects, cardAnimations } from './ui-animations'
|
||
|
||
// 导出主题 Hook(推荐使用)
|
||
export { useThemeStyles, useTheme } from './hooks/useThemeStyles'
|
||
|
||
// 导出两套独立的样式配置
|
||
export { lightStyles, darkStyles } from './ui-styles-theme-exports'
|
||
|
||
// 为了向后兼容,保留原有导出(包含 dark: 前缀)
|
||
// ⚠️ 这些样式会在深色模式下正确工作,但代码较长
|
||
// 建议逐步迁移到 useThemeStyles Hook
|
||
export const cardStyles = {
|
||
// 基础卡片 - 确保深色模式完全适配(兼容模式)
|
||
base: 'rounded-2xl border border-slate-200/70 dark:border-slate-700/70 bg-white dark:bg-slate-800 backdrop-blur-sm shadow-lg shadow-slate-200/50 dark:shadow-black/50 p-6 transition-all duration-300',
|
||
|
||
// 悬停卡片(带提升效果)- 确保深色模式完全适配
|
||
hover: 'rounded-2xl border border-slate-200/70 dark:border-slate-700/70 bg-white dark:bg-slate-800 backdrop-blur-sm shadow-lg shadow-slate-200/50 dark:shadow-black/50 hover:shadow-xl hover:shadow-indigo-500/20 dark:hover:shadow-indigo-500/30 hover:-translate-y-1 transition-all duration-300 p-6',
|
||
|
||
// 大卡片(带炫彩光晕)- 确保深色模式完全适配
|
||
large: 'rounded-3xl border border-slate-200/70 dark:border-slate-700/70 bg-white dark:bg-slate-800 backdrop-blur-md shadow-xl shadow-slate-200/60 dark:shadow-black/60 hover:shadow-2xl hover:shadow-indigo-500/25 dark:hover:shadow-indigo-500/35 transition-all duration-500 p-8',
|
||
|
||
// 扁平卡片 - 确保深色模式完全适配
|
||
flat: 'rounded-2xl border border-slate-200/70 dark:border-slate-700/70 bg-white dark:bg-slate-800 backdrop-blur-sm p-6 transition-all duration-300',
|
||
|
||
// 炫彩卡片(带渐变背景)- 确保深色模式完全适配
|
||
rainbow: `rounded-2xl border border-slate-200/70 dark:border-slate-700/70 ${gradientBackgrounds.rainbowSubtle} backdrop-blur-sm shadow-lg shadow-purple-500/20 dark:shadow-purple-500/30 hover:shadow-xl hover:shadow-purple-500/30 dark:hover:shadow-purple-500/40 hover:-translate-y-1 transition-all duration-300 p-6`,
|
||
|
||
// 玻璃态卡片 - 确保深色模式完全适配
|
||
glass: `rounded-2xl ${gradientBackgrounds.glass} shadow-lg shadow-slate-200/50 dark:shadow-black/50 hover:shadow-xl hover:shadow-indigo-500/20 dark:hover:shadow-indigo-500/30 transition-all duration-300 p-6`,
|
||
|
||
// 霓虹卡片 - 确保深色模式完全适配
|
||
neon: 'rounded-2xl border-2 border-cyan-400/50 dark:border-cyan-400/70 bg-white dark:bg-slate-800 backdrop-blur-sm shadow-lg shadow-cyan-500/30 dark:shadow-cyan-500/40 hover:shadow-xl hover:shadow-cyan-500/50 dark:hover:shadow-cyan-500/60 transition-all duration-300 p-6',
|
||
} as const
|
||
|
||
export const buttonStyles = {
|
||
// 主要按钮(带炫彩光晕)- 确保深色模式完全适配
|
||
primary: 'px-6 py-3 rounded-xl bg-indigo-600 dark:bg-indigo-500 text-white font-medium shadow-lg shadow-indigo-500/40 dark:shadow-indigo-500/50 hover:bg-indigo-700 dark:hover:bg-indigo-600 hover:shadow-xl hover:shadow-indigo-500/50 dark:hover:shadow-indigo-500/60 active:scale-95 transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed',
|
||
|
||
// 次要按钮 - 确保深色模式完全适配
|
||
secondary: 'px-6 py-3 rounded-xl border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-800 backdrop-blur-sm text-slate-700 dark:text-slate-200 font-medium hover:bg-slate-50 dark:hover:bg-slate-700 hover:border-indigo-400 dark:hover:border-indigo-400 active:scale-95 transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed',
|
||
|
||
// 幽灵按钮 - 确保深色模式完全适配
|
||
ghost: 'px-6 py-3 rounded-xl text-slate-700 dark:text-slate-200 font-medium hover:bg-slate-100 dark:hover:bg-slate-800 active:scale-95 transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed',
|
||
|
||
// 危险按钮 - 确保深色模式完全适配(浅色模式使用更深的红色以增强对比度)
|
||
danger: 'px-6 py-3 rounded-xl bg-red-700 dark:bg-red-500 text-white font-medium shadow-lg shadow-red-600/70 dark:shadow-red-500/50 hover:bg-red-800 dark:hover:bg-red-600 hover:shadow-xl hover:shadow-red-600/80 dark:hover:shadow-red-500/60 active:scale-95 transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed',
|
||
|
||
// 成功按钮 - 确保深色模式完全适配
|
||
success: 'px-6 py-3 rounded-xl bg-emerald-600 dark:bg-emerald-500 text-white font-medium shadow-lg shadow-emerald-500/40 dark:shadow-emerald-500/50 hover:bg-emerald-700 dark:hover:bg-emerald-600 hover:shadow-xl hover:shadow-emerald-500/50 dark:hover:shadow-emerald-500/60 active:scale-95 transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed',
|
||
|
||
// 炫彩渐变按钮
|
||
rainbow: 'px-6 py-3 rounded-xl bg-gradient-to-r from-pink-500 via-purple-500 to-indigo-500 text-white font-medium shadow-lg shadow-purple-500/40 hover:from-pink-600 hover:via-purple-600 hover:to-indigo-600 hover:shadow-xl hover:shadow-purple-500/50 active:scale-95 transition-all duration-300 disabled:opacity-50 disabled:cursor-not-allowed',
|
||
|
||
// 霓虹按钮
|
||
neon: 'px-6 py-3 rounded-xl bg-cyan-500 text-white font-medium shadow-lg shadow-cyan-500/50 hover:bg-cyan-400 hover:shadow-xl hover:shadow-cyan-500/70 active:scale-95 transition-all duration-300 disabled:opacity-50 disabled:cursor-not-allowed',
|
||
|
||
// 玻璃态按钮
|
||
glass: 'px-6 py-3 rounded-xl bg-white/10 dark:bg-white/5 backdrop-blur-md border border-white/20 text-white font-medium hover:bg-white/20 dark:hover:bg-white/10 hover:border-white/30 active:scale-95 transition-all duration-300 disabled:opacity-50 disabled:cursor-not-allowed',
|
||
} as const
|
||
|
||
export const buttonModifiers = {
|
||
compact: 'px-3 py-1.5 text-sm',
|
||
compactXs: 'px-2 py-1 text-xs',
|
||
icon: 'p-2 rounded-xl',
|
||
block: 'w-full justify-center',
|
||
pill: 'rounded-full',
|
||
} as const
|
||
|
||
export const inputStyles = {
|
||
// 基础输入框 - 确保深色模式完全适配
|
||
base: 'w-full px-4 py-3 rounded-xl border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-800 text-slate-900 dark:text-white placeholder:text-slate-400 dark:placeholder:text-slate-400 focus:border-indigo-500 dark:focus:border-indigo-400 focus:ring-2 focus:ring-indigo-200 dark:focus:ring-indigo-800 outline-none transition-all',
|
||
// 错误输入框 - 确保深色模式完全适配
|
||
error: 'w-full px-4 py-3 rounded-xl border-2 border-red-500 dark:border-red-600 bg-white dark:bg-slate-800 text-slate-900 dark:text-white placeholder:text-slate-400 dark:placeholder:text-slate-400 focus:ring-2 focus:ring-red-200 dark:focus:ring-red-800 outline-none transition-all',
|
||
} as const
|
||
|
||
export const inputModifiers = {
|
||
compact: 'px-3 py-2 text-sm rounded-lg',
|
||
compactXs: 'px-2.5 py-1.5 text-xs rounded-lg',
|
||
} as const
|
||
|
||
export const navItemStyles = {
|
||
// 基础导航项 - 确保深色模式完全适配
|
||
base: 'px-4 py-3 rounded-xl border border-slate-200/70 dark:border-slate-700/70 bg-white dark:bg-slate-800 backdrop-blur-sm text-slate-700 dark:text-slate-200 transition-all duration-300',
|
||
|
||
// 悬停导航项(带光晕效果)- 确保深色模式完全适配
|
||
hover: 'px-4 py-3 rounded-xl border border-slate-200/70 dark:border-slate-700/70 bg-white dark:bg-slate-800 backdrop-blur-sm hover:border-indigo-400 dark:hover:border-indigo-400 hover:bg-indigo-50 dark:hover:bg-indigo-900/30 hover:shadow-md hover:shadow-indigo-500/20 dark:hover:shadow-indigo-500/30 text-slate-700 dark:text-slate-200 transition-all duration-300',
|
||
|
||
// 激活导航项(带炫彩光晕)- 确保深色模式完全适配
|
||
active: 'px-4 py-3 rounded-xl border-2 border-indigo-500 dark:border-indigo-400 bg-indigo-50 dark:bg-indigo-900/40 backdrop-blur-sm text-indigo-700 dark:text-indigo-200 shadow-lg shadow-indigo-500/30 dark:shadow-indigo-500/40 transition-all duration-300',
|
||
|
||
// 炫彩导航项 - 确保深色模式完全适配
|
||
rainbow: 'px-4 py-3 rounded-xl border border-slate-200/70 dark:border-slate-700/70 bg-gradient-to-r from-pink-50/50 via-purple-50/50 to-indigo-50/50 dark:from-pink-900/20 dark:via-purple-900/20 dark:to-indigo-900/20 backdrop-blur-sm hover:shadow-md hover:shadow-purple-500/20 dark:hover:shadow-purple-500/30 text-slate-700 dark:text-slate-200 transition-all duration-300',
|
||
|
||
// 标签页导航项 - 确保深色模式完全适配
|
||
tabBase: 'relative pb-4 px-5 font-medium transition-all duration-300 ease-out text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white',
|
||
tabActive: 'relative pb-4 px-5 font-medium transition-all duration-300 ease-out text-slate-900 dark:text-white',
|
||
} as const
|
||
|
||
export const badgeStyles = {
|
||
base: 'px-3 py-1 rounded-lg text-xs font-medium',
|
||
primary: 'px-3 py-1 rounded-lg bg-indigo-100 dark:bg-indigo-900/30 text-indigo-700 dark:text-indigo-300 text-xs font-medium',
|
||
success: 'px-3 py-1 rounded-lg bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-300 text-xs font-medium',
|
||
warning: 'px-3 py-1 rounded-lg bg-yellow-100 dark:bg-yellow-900/30 text-yellow-700 dark:text-yellow-300 text-xs font-medium',
|
||
danger: 'px-3 py-1 rounded-lg bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-300 text-xs font-medium',
|
||
} as const
|
||
|
||
export const spacingStyles = {
|
||
section: 'space-y-8', // 区块间距
|
||
card: 'space-y-6', // 卡片内间距
|
||
form: 'space-y-4', // 表单元素间距
|
||
compact: 'space-y-2', // 紧密间距
|
||
} as const
|
||
|
||
/**
|
||
* 组合多个样式类
|
||
*/
|
||
export function cn(...classes: (string | undefined | false | null)[]): string {
|
||
return classes.filter(Boolean).join(' ')
|
||
}
|
||
|
||
/**
|
||
* 运行时根据主题获取样式配置
|
||
* ⚠️ 不推荐使用,建议在组件中使用 useThemeStyles Hook
|
||
*
|
||
* @param theme - 主题 'light' | 'dark'
|
||
* @returns 对应主题的样式配置
|
||
*/
|
||
export function getThemeStyles(theme: 'light' | 'dark') {
|
||
// 动态导入以避免类型冲突
|
||
if (theme === 'dark') {
|
||
return require('./ui-styles-dark')
|
||
}
|
||
return require('./ui-styles-light')
|
||
}
|
||
|
||
/**
|
||
* Section 显示动画类生成器(来自外观设置)
|
||
* @param isVisible - 是否可见
|
||
* @returns 组合后的样式类
|
||
*/
|
||
export function sectionRevealClass(isVisible: boolean): string {
|
||
return cn(
|
||
'transition-all duration-700 ease-out transform',
|
||
isVisible ? 'opacity-100 translate-y-0 scale-100' : 'opacity-0 translate-y-6 scale-95'
|
||
)
|
||
}
|
||
|
||
/**
|
||
* 浮动光球样式类生成器(来自外观设置)
|
||
* @param theme - 主题 'light' | 'dark'
|
||
* @param position - 位置 'topRight' | 'bottomLeft'
|
||
* @returns 样式类字符串
|
||
*/
|
||
export function floatingOrbClass(theme: 'light' | 'dark', position: 'topRight' | 'bottomLeft'): string {
|
||
const baseClasses = 'absolute rounded-full blur-3xl pointer-events-none'
|
||
const positionClasses = position === 'topRight'
|
||
? '-top-8 -right-8 h-24 w-24 animate-pulse-slow'
|
||
: '-bottom-8 -left-8 h-20 w-20 animate-[pulse_8s_ease-in-out_infinite]'
|
||
const colorClasses = position === 'topRight'
|
||
? (theme === 'dark' ? 'bg-indigo-500/15' : 'bg-indigo-400/20')
|
||
: (theme === 'dark' ? 'bg-purple-500/15' : 'bg-purple-400/20')
|
||
|
||
return cn(baseClasses, positionClasses, colorClasses)
|
||
}
|
||
|
||
/**
|
||
* Hero 动态背景样式生成器(来自外观设置)
|
||
* @param isMouseOver - 鼠标是否悬停
|
||
* @param glowPos - 光晕位置 { x: number, y: number }
|
||
* @param theme - 主题 'light' | 'dark'
|
||
* @param baseGradient - 基础渐变背景
|
||
* @returns 背景样式对象
|
||
*/
|
||
export function getHeroDynamicBackground(
|
||
isMouseOver: boolean,
|
||
glowPos: { x: number; y: number },
|
||
theme: 'light' | 'dark',
|
||
baseGradient: string
|
||
): { backgroundImage: string; transition: string } {
|
||
if (isMouseOver) {
|
||
return {
|
||
backgroundImage: `radial-gradient(circle 300px at ${glowPos.x}% ${glowPos.y}%, ${
|
||
theme === 'dark'
|
||
? 'rgba(255,255,255,0.2), rgba(255,255,255,0.1), rgba(255,255,255,0.05), transparent 75%'
|
||
: 'rgba(255,255,255,0.3), rgba(255,255,255,0.15), rgba(255,255,255,0.08), transparent 75%'
|
||
}), ${baseGradient}`,
|
||
transition: 'background-image 0.4s ease-out',
|
||
}
|
||
}
|
||
return {
|
||
backgroundImage: baseGradient,
|
||
transition: 'background-image 0.4s ease-out',
|
||
}
|
||
}
|
||
|