diff --git a/src/components/SubTabContainer/index.tsx b/src/components/SubTabContainer/index.tsx index 276a56d2..658280f3 100644 --- a/src/components/SubTabContainer/index.tsx +++ b/src/components/SubTabContainer/index.tsx @@ -56,16 +56,16 @@ export interface SubTabTheme { } /** - * 预设主题 + * 预设主题 - FUI 风格优化 */ const THEME_PRESETS: Record = { blackGold: { - bg: 'gray.900', - borderColor: 'rgba(212, 175, 55, 0.3)', - tabSelectedBg: '#D4AF37', - tabSelectedColor: 'gray.900', - tabUnselectedColor: '#D4AF37', - tabHoverBg: 'gray.600', + bg: 'transparent', + borderColor: 'rgba(212, 175, 55, 0.2)', + tabSelectedBg: 'linear-gradient(135deg, #D4AF37 0%, #B8960C 100%)', + tabSelectedColor: '#0A0A14', + tabUnselectedColor: 'rgba(212, 175, 55, 0.8)', + tabHoverBg: 'rgba(212, 175, 55, 0.1)', }, default: { bg: 'white', @@ -179,23 +179,28 @@ const SubTabContainer: React.FC = memo(({ key={tab.key} color={theme.tabUnselectedColor} borderRadius="full" - px={2.5} + px={3} py={1.5} fontSize="xs" whiteSpace="nowrap" flexShrink={0} + border="1px solid transparent" + transition="all 0.2s cubic-bezier(0.4, 0, 0.2, 1)" _selected={{ bg: theme.tabSelectedBg, color: theme.tabSelectedColor, fontWeight: 'bold', + boxShadow: '0 0 12px rgba(212, 175, 55, 0.4)', + border: '1px solid rgba(212, 175, 55, 0.5)', }} _hover={{ bg: theme.tabHoverBg, + border: '1px solid rgba(212, 175, 55, 0.3)', }} > - - {tab.icon && } - {tab.name} + + {tab.icon && } + {tab.name} ))} diff --git a/src/views/Company/components/CompanyHeader/index.tsx b/src/views/Company/components/CompanyHeader/index.tsx index 7d1de48c..4cf25f93 100644 --- a/src/views/Company/components/CompanyHeader/index.tsx +++ b/src/views/Company/components/CompanyHeader/index.tsx @@ -1,8 +1,11 @@ /** - * Company 页面顶部搜索栏组件 - * - 显示股票代码、名称、价格、涨跌幅 - * - 股票搜索功能 - * - 自选股操作 + * Company 页面顶部搜索栏组件 - FUI 科幻风格 + * + * 设计特点: + * - Glassmorphism 毛玻璃背景 + * - 发光效果和微动画 + * - Ash Thorp 风格的数据展示 + * - James Turrell 柔和光影 */ import React, { memo, useMemo, useCallback, useState } from 'react'; @@ -14,17 +17,24 @@ import { Text, Button, Icon, - Badge, Skeleton, + keyframes, } from '@chakra-ui/react'; import { AutoComplete, Spin } from 'antd'; -import { Search, Star } from 'lucide-react'; +import { Search, Star, TrendingUp, TrendingDown } from 'lucide-react'; import { useStockSearch } from '@hooks/useStockSearch'; import { THEME, getSearchBoxStyles } from '../../config'; +import { FUI_COLORS, FUI_GLOW, FUI_ANIMATION, FUI_GLASS } from '../../theme/fui'; import type { CompanyHeaderProps, StockSearchResult } from '../../types'; +// 发光脉冲动画 +const glowPulse = keyframes` + 0%, 100% { box-shadow: 0 0 20px rgba(212, 175, 55, 0.2); } + 50% { box-shadow: 0 0 30px rgba(212, 175, 55, 0.4); } +`; + /** - * 股票信息展示组件 + * 股票信息展示组件 - FUI 风格 */ const StockInfoDisplay = memo<{ stockCode: string; @@ -35,47 +45,95 @@ const StockInfoDisplay = memo<{ }>(({ stockCode, stockName, price, change, loading }) => { if (loading) { return ( - - - + + + ); } + const isPositive = change !== null && change !== undefined && change >= 0; + const TrendIcon = isPositive ? TrendingUp : TrendingDown; + return ( - - + + {/* 股票代码 & 名称 */} + {stockCode} {stockName && ( - + {stockName} )} + + {/* 价格 & 涨跌幅 */} {price !== null && price !== undefined && ( - - - ¥{price.toFixed(2)} - - {change !== null && change !== undefined && ( - = 0 ? THEME.positiveBg : THEME.negativeBg} - color={change >= 0 ? THEME.positive : THEME.negative} - fontSize="sm" - fontWeight="bold" + + {/* 价格 */} + + - {change >= 0 ? '+' : ''}{change.toFixed(2)}% - + Price + + + ¥{price.toFixed(2)} + + + + {/* 涨跌幅 Badge */} + {change !== null && change !== undefined && ( + + + + {isPositive ? '+' : ''}{change.toFixed(2)}% + + )} )} @@ -154,8 +212,36 @@ const SearchActions = memo<{ return ( - {/* 搜索框 */} - + {/* 搜索框 - FUI 风格 */} + : null} onKeyDown={handleKeyDown} /> - {/* 搜索按钮 */} + {/* 搜索按钮 - 发光效果 */} - {/* 自选按钮 */} + {/* 自选按钮 - FUI 风格 */} @@ -248,13 +359,42 @@ const CompanyHeader: React.FC = memo(({ return ( + {/* 环境光效果 - James Turrell 风格 */} + + + {/* 顶部发光线 */} + + ( TabLoadingFallback.displayName = 'TabLoadingFallback'; // ============================================ -// 主内容区组件 +// 主内容区组件 - FUI 风格 // ============================================ interface CompanyContentProps { @@ -41,12 +45,61 @@ interface CompanyContentProps { const CompanyContent = memo(({ stockCode, onTabChange }) => ( + {/* 角落装饰 - FUI 风格 */} + + + + + }> { }, [trackTabChanged]); return ( - - {/* 顶部搜索栏 */} - + {/* 全局环境光效果 - James Turrell 风格 */} + + {/* 顶部搜索栏 */} + + + + {/* 主内容区 */} - + + + ); }; diff --git a/src/views/Company/theme/components/FUIElements.tsx b/src/views/Company/theme/components/FUIElements.tsx new file mode 100644 index 00000000..821a500f --- /dev/null +++ b/src/views/Company/theme/components/FUIElements.tsx @@ -0,0 +1,381 @@ +/** + * FUI Elements - 科幻风格 UI 元素集合 + * + * 包含: + * - GlowText: 发光文字 + * - DataBadge: 数据标签 + * - StatusIndicator: 状态指示器 + * - Divider: 分隔线 + * - ProgressBar: 进度条 + */ + +import React, { memo } from 'react'; +import { Box, Text, HStack, BoxProps, TextProps } from '@chakra-ui/react'; +import { FUI_COLORS, FUI_GLOW, FUI_ANIMATION } from '../fui'; + +// ============================================ +// GlowText - 发光文字 +// ============================================ + +export interface GlowTextProps extends Omit { + children: React.ReactNode; + /** 发光颜色 */ + glowColor?: 'gold' | 'white' | 'positive' | 'negative'; + /** 发光强度 */ + intensity?: 'low' | 'medium' | 'high'; +} + +const GLOW_COLORS = { + gold: FUI_COLORS.gold[400], + white: '#FFFFFF', + positive: FUI_COLORS.status.positive, + negative: FUI_COLORS.status.negative, +}; + +const INTENSITY_MAP = { + low: 0.3, + medium: 0.5, + high: 0.8, +}; + +export const GlowText: React.FC = memo(({ + children, + glowColor = 'gold', + intensity = 'medium', + ...props +}) => { + const color = GLOW_COLORS[glowColor]; + const alpha = INTENSITY_MAP[intensity]; + + return ( + + {children} + + ); +}); + +GlowText.displayName = 'GlowText'; + +// ============================================ +// DataBadge - 数据标签 +// ============================================ + +export interface DataBadgeProps extends Omit { + children: React.ReactNode; + /** 标签类型 */ + variant?: 'default' | 'success' | 'danger' | 'warning' | 'info'; + /** 尺寸 */ + size?: 'sm' | 'md' | 'lg'; + /** 是否脉冲动画 */ + pulse?: boolean; +} + +const BADGE_VARIANTS = { + default: { + bg: 'rgba(212, 175, 55, 0.15)', + borderColor: 'rgba(212, 175, 55, 0.3)', + color: FUI_COLORS.gold[400], + }, + success: { + bg: 'rgba(34, 197, 94, 0.15)', + borderColor: 'rgba(34, 197, 94, 0.3)', + color: FUI_COLORS.status.negative, + }, + danger: { + bg: 'rgba(239, 68, 68, 0.15)', + borderColor: 'rgba(239, 68, 68, 0.3)', + color: FUI_COLORS.status.positive, + }, + warning: { + bg: 'rgba(245, 158, 11, 0.15)', + borderColor: 'rgba(245, 158, 11, 0.3)', + color: FUI_COLORS.status.warning, + }, + info: { + bg: 'rgba(59, 130, 246, 0.15)', + borderColor: 'rgba(59, 130, 246, 0.3)', + color: FUI_COLORS.status.info, + }, +}; + +const BADGE_SIZES = { + sm: { px: 2, py: 0.5, fontSize: '10px' }, + md: { px: 3, py: 1, fontSize: '12px' }, + lg: { px: 4, py: 1.5, fontSize: '14px' }, +}; + +export const DataBadge: React.FC = memo(({ + children, + variant = 'default', + size = 'md', + pulse = false, + ...props +}) => { + const variantStyle = BADGE_VARIANTS[variant]; + const sizeStyle = BADGE_SIZES[size]; + + return ( + + {children} + + ); +}); + +DataBadge.displayName = 'DataBadge'; + +// ============================================ +// StatusIndicator - 状态指示器(带脉冲点) +// ============================================ + +export interface StatusIndicatorProps extends Omit { + /** 状态 */ + status: 'active' | 'inactive' | 'warning' | 'error'; + /** 标签文字 */ + label?: string; + /** 是否显示脉冲动画 */ + pulse?: boolean; +} + +const STATUS_COLORS = { + active: FUI_COLORS.gold[400], + inactive: FUI_COLORS.text.muted, + warning: FUI_COLORS.status.warning, + error: FUI_COLORS.status.positive, +}; + +export const StatusIndicator: React.FC = memo(({ + status, + label, + pulse = true, + ...props +}) => { + const color = STATUS_COLORS[status]; + + return ( + + + {/* 脉冲光环 */} + {pulse && status === 'active' && ( + + )} + {/* 核心点 */} + + + {label && ( + + {label} + + )} + + ); +}); + +StatusIndicator.displayName = 'StatusIndicator'; + +// ============================================ +// FUIDivider - FUI 风格分隔线 +// ============================================ + +export interface FUIDividerProps extends Omit { + /** 方向 */ + orientation?: 'horizontal' | 'vertical'; + /** 是否发光 */ + glowing?: boolean; + /** 中间文字 */ + label?: string; +} + +export const FUIDivider: React.FC = memo(({ + orientation = 'horizontal', + glowing = false, + label, + ...props +}) => { + if (orientation === 'vertical') { + return ( + + ); + } + + if (label) { + return ( + + + + {label} + + + + ); + } + + return ( + + ); +}); + +FUIDivider.displayName = 'FUIDivider'; + +// ============================================ +// FUIProgressBar - FUI 风格进度条 +// ============================================ + +export interface FUIProgressBarProps extends Omit { + /** 进度值 (0-100) */ + value: number; + /** 颜色主题 */ + colorScheme?: 'gold' | 'positive' | 'negative' | 'gradient'; + /** 是否显示发光效果 */ + glowing?: boolean; + /** 高度 */ + size?: 'sm' | 'md' | 'lg'; + /** 是否显示数值 */ + showValue?: boolean; +} + +const PROGRESS_COLORS = { + gold: FUI_COLORS.gold[400], + positive: FUI_COLORS.status.positive, + negative: FUI_COLORS.status.negative, + gradient: `linear-gradient(90deg, ${FUI_COLORS.gold[500]}, ${FUI_COLORS.gold[300]})`, +}; + +const PROGRESS_SIZES = { + sm: '4px', + md: '6px', + lg: '8px', +}; + +export const FUIProgressBar: React.FC = memo(({ + value, + colorScheme = 'gold', + glowing = false, + size = 'md', + showValue = false, + ...props +}) => { + const clampedValue = Math.min(100, Math.max(0, value)); + const color = PROGRESS_COLORS[colorScheme]; + const isGradient = colorScheme === 'gradient'; + + return ( + + + + + + + {showValue && ( + + {clampedValue.toFixed(0)}% + + )} + + ); +}); + +FUIProgressBar.displayName = 'FUIProgressBar'; + +// ============================================ +// 统一导出 +// ============================================ + +export default { + GlowText, + DataBadge, + StatusIndicator, + FUIDivider, + FUIProgressBar, +}; diff --git a/src/views/Company/theme/components/GlassCard.tsx b/src/views/Company/theme/components/GlassCard.tsx new file mode 100644 index 00000000..c4fb3c25 --- /dev/null +++ b/src/views/Company/theme/components/GlassCard.tsx @@ -0,0 +1,207 @@ +/** + * GlassCard - Glassmorphism 风格卡片组件 + * + * 设计特点: + * - 毛玻璃背景效果 + * - 精细边框发光 + * - 悬停动画 + * - 可选角落装饰(FUI 风格) + */ + +import React, { memo, forwardRef } from 'react'; +import { Box, BoxProps } from '@chakra-ui/react'; +import { FUI_COLORS, FUI_GLASS, FUI_GLOW, FUI_ANIMATION } from '../fui'; + +// ============================================ +// 类型定义 +// ============================================ + +export interface GlassCardProps extends Omit { + children: React.ReactNode; + /** 变体样式 */ + variant?: 'default' | 'elevated' | 'outlined' | 'subtle'; + /** 是否启用悬停效果 */ + hoverable?: boolean; + /** 是否启用发光效果 */ + glowing?: boolean; + /** 是否显示角落装饰 */ + cornerDecor?: boolean; + /** 是否显示扫描线效果 */ + scanline?: boolean; + /** 边框圆角 */ + rounded?: 'sm' | 'md' | 'lg' | 'xl' | '2xl'; + /** 内边距预设 */ + padding?: 'none' | 'sm' | 'md' | 'lg'; +} + +// ============================================ +// 样式配置 +// ============================================ + +const VARIANTS = { + default: { + bg: `linear-gradient(135deg, ${FUI_COLORS.bg.elevated} 0%, ${FUI_COLORS.bg.primary} 100%)`, + border: FUI_GLASS.border.default, + backdropFilter: FUI_GLASS.blur.md, + }, + elevated: { + bg: `linear-gradient(145deg, ${FUI_COLORS.bg.surface} 0%, ${FUI_COLORS.bg.elevated} 100%)`, + border: FUI_GLASS.border.emphasis, + backdropFilter: FUI_GLASS.blur.lg, + }, + outlined: { + bg: 'transparent', + border: FUI_GLASS.border.emphasis, + backdropFilter: 'none', + }, + subtle: { + bg: FUI_GLASS.bg.gold, + border: FUI_GLASS.border.subtle, + backdropFilter: FUI_GLASS.blur.sm, + }, +}; + +const ROUNDED_MAP = { + sm: '8px', + md: '12px', + lg: '16px', + xl: '20px', + '2xl': '24px', +}; + +const PADDING_MAP = { + none: 0, + sm: 3, + md: 4, + lg: 6, +}; + +// ============================================ +// 角落装饰组件 +// ============================================ + +const CornerDecor: React.FC<{ position: 'tl' | 'tr' | 'bl' | 'br' }> = memo(({ position }) => { + const baseStyle = { + position: 'absolute' as const, + width: '12px', + height: '12px', + borderColor: FUI_COLORS.gold[400], + borderStyle: 'solid', + borderWidth: 0, + opacity: 0.6, + }; + + const positions = { + tl: { top: '8px', left: '8px', borderTopWidth: '2px', borderLeftWidth: '2px' }, + tr: { top: '8px', right: '8px', borderTopWidth: '2px', borderRightWidth: '2px' }, + bl: { bottom: '8px', left: '8px', borderBottomWidth: '2px', borderLeftWidth: '2px' }, + br: { bottom: '8px', right: '8px', borderBottomWidth: '2px', borderRightWidth: '2px' }, + }; + + return ; +}); + +CornerDecor.displayName = 'CornerDecor'; + +// ============================================ +// 扫描线覆盖层 +// ============================================ + +const ScanlineOverlay: React.FC = memo(() => ( + +)); + +ScanlineOverlay.displayName = 'ScanlineOverlay'; + +// ============================================ +// 主组件 +// ============================================ + +const GlassCard = forwardRef( + ( + { + children, + variant = 'default', + hoverable = true, + glowing = false, + cornerDecor = false, + scanline = false, + rounded = 'lg', + padding = 'md', + ...props + }, + ref + ) => { + const variantStyle = VARIANTS[variant]; + + return ( + + {/* 扫描线效果 */} + {scanline && } + + {/* 角落装饰 */} + {cornerDecor && ( + <> + + + + + + )} + + {/* 内容 */} + + {children} + + + ); + } +); + +GlassCard.displayName = 'GlassCard'; + +export default memo(GlassCard); diff --git a/src/views/Company/theme/components/index.ts b/src/views/Company/theme/components/index.ts new file mode 100644 index 00000000..39209a9f --- /dev/null +++ b/src/views/Company/theme/components/index.ts @@ -0,0 +1,21 @@ +/** + * FUI 主题组件统一导出 + */ + +export { default as GlassCard } from './GlassCard'; +export type { GlassCardProps } from './GlassCard'; + +export { + GlowText, + DataBadge, + StatusIndicator, + FUIDivider, + FUIProgressBar, +} from './FUIElements'; +export type { + GlowTextProps, + DataBadgeProps, + StatusIndicatorProps, + FUIDividerProps, + FUIProgressBarProps, +} from './FUIElements'; diff --git a/src/views/Company/theme/fui-animations.css b/src/views/Company/theme/fui-animations.css new file mode 100644 index 00000000..b0960611 --- /dev/null +++ b/src/views/Company/theme/fui-animations.css @@ -0,0 +1,141 @@ +/** + * FUI 主题动画样式 + * + * 在 Company 模块的入口文件中引入此文件: + * import './theme/fui-animations.css'; + */ + +/* 发光脉冲动画 */ +@keyframes glowPulse { + 0%, 100% { + box-shadow: 0 0 20px rgba(212, 175, 55, 0.3); + } + 50% { + box-shadow: 0 0 30px rgba(212, 175, 55, 0.5), 0 0 60px rgba(212, 175, 55, 0.2); + } +} + +/* 淡入动画 */ +@keyframes fadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +/* 向上滑入动画 */ +@keyframes slideUp { + from { + opacity: 0; + transform: translateY(10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +/* 扫描线动画 */ +@keyframes scanline { + 0% { + transform: translateY(-100%); + opacity: 0; + } + 10% { + opacity: 1; + } + 90% { + opacity: 1; + } + 100% { + transform: translateY(100%); + opacity: 0; + } +} + +/* 闪烁动画 */ +@keyframes shimmer { + 0% { + background-position: -200% 0; + } + 100% { + background-position: 200% 0; + } +} + +/* 边框发光动画 */ +@keyframes borderGlow { + 0%, 100% { + border-color: rgba(212, 175, 55, 0.2); + } + 50% { + border-color: rgba(212, 175, 55, 0.5); + } +} + +/* 脉冲动画 */ +@keyframes pulse { + 0%, 100% { + transform: scale(1); + opacity: 0.3; + } + 50% { + transform: scale(1.5); + opacity: 0; + } +} + +/* 数据流动动画 */ +@keyframes dataFlow { + 0% { + background-position: 0% 50%; + } + 50% { + background-position: 100% 50%; + } + 100% { + background-position: 0% 50%; + } +} + +/* 旋转发光动画 */ +@keyframes rotateGlow { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} + +/* 文字发光动画 */ +@keyframes textGlow { + 0%, 100% { + text-shadow: 0 0 10px rgba(212, 175, 55, 0.3); + } + 50% { + text-shadow: 0 0 20px rgba(212, 175, 55, 0.6), 0 0 30px rgba(212, 175, 55, 0.4); + } +} + +/* 浮动动画 */ +@keyframes float { + 0%, 100% { + transform: translateY(0); + } + 50% { + transform: translateY(-5px); + } +} + +/* 呼吸动画 */ +@keyframes breathe { + 0%, 100% { + opacity: 0.6; + } + 50% { + opacity: 1; + } +} diff --git a/src/views/Company/theme/fui.ts b/src/views/Company/theme/fui.ts new file mode 100644 index 00000000..f4544456 --- /dev/null +++ b/src/views/Company/theme/fui.ts @@ -0,0 +1,336 @@ +/** + * FUI (Fantasy User Interface) 科幻主题配置 + * + * 设计灵感: + * - Ash Thorp: 精细线条、数据可视化、霓虹发光 + * - Linear.app: 极简、精致微交互、清晰层次 + * - James Turrell: 柔和光影渐变、沉浸式氛围 + * - HeroUI: 现代组件风格 + */ + +// ============================================ +// 核心色彩系统 +// ============================================ + +export const FUI_COLORS = { + // 主色调 - 金色系(保持黑金风格) + gold: { + 50: '#FDF8E8', + 100: '#F8EDCB', + 200: '#F0D78C', + 300: '#E8C14D', + 400: '#D4AF37', // 主金色 + 500: '#B8960C', + 600: '#8B7209', + 700: '#5E4D06', + 800: '#312803', + 900: '#1A1500', + }, + + // 环境光色 - James Turrell 光影 + ambient: { + cyan: 'rgba(0, 255, 255, 0.15)', + magenta: 'rgba(255, 0, 255, 0.1)', + warm: 'rgba(255, 200, 100, 0.08)', + cool: 'rgba(100, 200, 255, 0.08)', + }, + + // 背景层次 + bg: { + deep: '#0A0A14', // 最深层 + primary: '#0F0F1A', // 主背景 + elevated: '#1A1A2E', // 抬升层 + surface: '#252540', // 表面层 + overlay: 'rgba(26, 26, 46, 0.95)', // 覆盖层 + }, + + // 边框 & 线条 + line: { + subtle: 'rgba(212, 175, 55, 0.1)', + default: 'rgba(212, 175, 55, 0.2)', + emphasis: 'rgba(212, 175, 55, 0.4)', + glow: 'rgba(212, 175, 55, 0.6)', + }, + + // 文字 + text: { + primary: 'rgba(255, 255, 255, 0.95)', + secondary: 'rgba(255, 255, 255, 0.7)', + muted: 'rgba(255, 255, 255, 0.5)', + dim: 'rgba(255, 255, 255, 0.3)', + }, + + // 状态色 + status: { + positive: '#EF4444', // 涨 - 红 + negative: '#22C55E', // 跌 - 绿 + warning: '#F59E0B', + info: '#3B82F6', + }, +} as const; + +// ============================================ +// 发光效果(Glow Effects) +// ============================================ + +export const FUI_GLOW = { + // 金色发光 + gold: { + sm: '0 0 8px rgba(212, 175, 55, 0.3)', + md: '0 0 16px rgba(212, 175, 55, 0.4)', + lg: '0 0 32px rgba(212, 175, 55, 0.5)', + pulse: '0 0 20px rgba(212, 175, 55, 0.6), 0 0 40px rgba(212, 175, 55, 0.3)', + }, + + // 环境光发光 (Turrell style) + ambient: { + warm: '0 0 60px rgba(255, 200, 100, 0.15)', + cool: '0 0 60px rgba(100, 200, 255, 0.1)', + mixed: '0 0 80px rgba(212, 175, 55, 0.1), 0 0 120px rgba(100, 200, 255, 0.05)', + }, + + // 文字发光 + text: { + gold: '0 0 10px rgba(212, 175, 55, 0.5)', + white: '0 0 10px rgba(255, 255, 255, 0.3)', + }, +} as const; + +// ============================================ +// Glassmorphism 配置 +// ============================================ + +export const FUI_GLASS = { + // 背景模糊 + blur: { + sm: 'blur(8px)', + md: 'blur(16px)', + lg: 'blur(24px)', + xl: 'blur(40px)', + }, + + // 玻璃背景 + bg: { + light: 'rgba(255, 255, 255, 0.03)', + medium: 'rgba(255, 255, 255, 0.05)', + dark: 'rgba(0, 0, 0, 0.2)', + gold: 'rgba(212, 175, 55, 0.05)', + }, + + // 边框 + border: { + subtle: '1px solid rgba(255, 255, 255, 0.05)', + default: '1px solid rgba(212, 175, 55, 0.15)', + emphasis: '1px solid rgba(212, 175, 55, 0.3)', + }, +} as const; + +// ============================================ +// 动画配置 +// ============================================ + +export const FUI_ANIMATION = { + // 过渡时间 + duration: { + instant: '0.1s', + fast: '0.2s', + normal: '0.3s', + slow: '0.5s', + slower: '0.8s', + }, + + // 缓动函数 + easing: { + default: 'cubic-bezier(0.4, 0, 0.2, 1)', + smooth: 'cubic-bezier(0.25, 0.1, 0.25, 1)', + bounce: 'cubic-bezier(0.68, -0.55, 0.265, 1.55)', + expo: 'cubic-bezier(0.16, 1, 0.3, 1)', + }, + + // 预设动画 + presets: { + fadeIn: 'fadeIn 0.3s ease-out', + slideUp: 'slideUp 0.3s ease-out', + glow: 'glowPulse 2s ease-in-out infinite', + scanline: 'scanline 3s linear infinite', + shimmer: 'shimmer 2s linear infinite', + }, +} as const; + +// ============================================ +// CSS Keyframes(需要在全局样式中注入) +// ============================================ + +export const FUI_KEYFRAMES = ` + @keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } + } + + @keyframes slideUp { + from { + opacity: 0; + transform: translateY(10px); + } + to { + opacity: 1; + transform: translateY(0); + } + } + + @keyframes glowPulse { + 0%, 100% { + box-shadow: 0 0 20px rgba(212, 175, 55, 0.3); + } + 50% { + box-shadow: 0 0 30px rgba(212, 175, 55, 0.5), 0 0 60px rgba(212, 175, 55, 0.2); + } + } + + @keyframes scanline { + 0% { + transform: translateY(-100%); + opacity: 0; + } + 10% { opacity: 1; } + 90% { opacity: 1; } + 100% { + transform: translateY(100%); + opacity: 0; + } + } + + @keyframes shimmer { + 0% { + background-position: -200% 0; + } + 100% { + background-position: 200% 0; + } + } + + @keyframes borderGlow { + 0%, 100% { + border-color: rgba(212, 175, 55, 0.2); + } + 50% { + border-color: rgba(212, 175, 55, 0.5); + } + } +`; + +// ============================================ +// 组件样式预设 +// ============================================ + +export const FUI_STYLES = { + // Glassmorphism 卡片 + glassCard: { + base: { + background: `linear-gradient(135deg, ${FUI_COLORS.bg.elevated} 0%, ${FUI_COLORS.bg.primary} 100%)`, + backdropFilter: FUI_GLASS.blur.md, + border: FUI_GLASS.border.default, + borderRadius: '16px', + transition: `all ${FUI_ANIMATION.duration.normal} ${FUI_ANIMATION.easing.smooth}`, + }, + hover: { + borderColor: FUI_COLORS.line.emphasis, + boxShadow: FUI_GLOW.gold.md, + transform: 'translateY(-2px)', + }, + }, + + // FUI 面板(带角落装饰) + fuiPanel: { + base: { + position: 'relative' as const, + background: FUI_COLORS.bg.elevated, + border: FUI_GLASS.border.default, + borderRadius: '12px', + overflow: 'hidden', + }, + cornerDecor: { + position: 'absolute' as const, + width: '20px', + height: '20px', + borderColor: FUI_COLORS.gold[400], + borderStyle: 'solid', + }, + }, + + // 扫描线效果 + scanlineOverlay: { + position: 'absolute' as const, + top: 0, + left: 0, + right: 0, + bottom: 0, + pointerEvents: 'none' as const, + background: `repeating-linear-gradient( + 0deg, + transparent, + transparent 2px, + rgba(212, 175, 55, 0.03) 2px, + rgba(212, 175, 55, 0.03) 4px + )`, + opacity: 0.5, + }, + + // 数据标签 + dataTag: { + display: 'inline-flex', + alignItems: 'center', + padding: '4px 12px', + background: 'rgba(212, 175, 55, 0.1)', + border: '1px solid rgba(212, 175, 55, 0.3)', + borderRadius: '20px', + fontSize: '12px', + fontWeight: 500, + color: FUI_COLORS.gold[400], + letterSpacing: '0.5px', + }, + + // 发光按钮 + glowButton: { + base: { + background: `linear-gradient(135deg, ${FUI_COLORS.gold[500]} 0%, ${FUI_COLORS.gold[400]} 100%)`, + color: FUI_COLORS.bg.deep, + fontWeight: 'bold', + borderRadius: '8px', + transition: `all ${FUI_ANIMATION.duration.fast} ${FUI_ANIMATION.easing.default}`, + }, + hover: { + boxShadow: FUI_GLOW.gold.md, + transform: 'translateY(-1px)', + }, + }, + + // 输入框 + input: { + base: { + background: FUI_COLORS.bg.primary, + border: FUI_GLASS.border.default, + borderRadius: '8px', + color: FUI_COLORS.text.primary, + transition: `all ${FUI_ANIMATION.duration.fast} ${FUI_ANIMATION.easing.default}`, + }, + focus: { + borderColor: FUI_COLORS.gold[400], + boxShadow: FUI_GLOW.gold.sm, + }, + }, +} as const; + +// ============================================ +// 导出完整主题对象 +// ============================================ + +export const FUI_THEME = { + colors: FUI_COLORS, + glow: FUI_GLOW, + glass: FUI_GLASS, + animation: FUI_ANIMATION, + styles: FUI_STYLES, +} as const; + +export default FUI_THEME; diff --git a/src/views/Company/theme/index.ts b/src/views/Company/theme/index.ts new file mode 100644 index 00000000..632e5b32 --- /dev/null +++ b/src/views/Company/theme/index.ts @@ -0,0 +1,17 @@ +/** + * Company 页面 FUI 主题统一导出 + */ + +// 主题配置 +export { default as FUI_THEME } from './fui'; +export { + FUI_COLORS, + FUI_GLOW, + FUI_GLASS, + FUI_ANIMATION, + FUI_KEYFRAMES, + FUI_STYLES, +} from './fui'; + +// 主题组件 +export * from './components';