update ui

This commit is contained in:
2025-11-13 23:24:54 +08:00
parent d3b980b3ca
commit 3caa5f4c3a
7 changed files with 179 additions and 63 deletions

View File

@@ -0,0 +1,118 @@
// src/constants/professionalTheme.js
// 专业金融风格配色主题 - 黑色、灰色、金色
/**
* 专业配色方案
* 主色调:黑色系 + 灰色系 + 金色点缀
* 适用于金融、投资类产品
*/
export const PROFESSIONAL_COLORS = {
// 背景色
background: {
primary: '#0A0E17', // 深黑蓝
secondary: '#151922', // 深灰
card: '#1A1F2E', // 卡片背景
cardHover: '#212633', // 卡片悬浮
overlay: 'rgba(10, 14, 23, 0.95)', // 遮罩
},
// 文字颜色
text: {
primary: '#E8E9ED', // 主文字 - 浅灰白
secondary: '#A0A4B8', // 次要文字 - 中灰
tertiary: '#6B7280', // 三级文字 - 深灰
muted: '#4B5563', // 弱化文字
},
// 金色系 - 高端感
gold: {
50: '#FFF9E6',
100: '#FFF3CC',
200: '#FFE799',
300: '#FFDB66',
400: '#FFCF33',
500: '#FFC300', // 主金色
600: '#CC9C00',
700: '#997500',
800: '#664E00',
900: '#332700',
},
// 边框颜色
border: {
light: '#2D3748', // 浅边框
default: '#374151', // 默认边框
dark: '#1F2937', // 深边框
gold: '#FFC300', // 金色边框
},
// 状态颜色
status: {
success: '#10B981', // 成功/涨
error: '#EF4444', // 错误/跌
warning: '#F59E0B', // 警告
info: '#3B82F6', // 信息
},
// 渐变色
gradients: {
gold: 'linear-gradient(135deg, #FFC300 0%, #FFD54F 100%)',
darkGold: 'linear-gradient(135deg, #997500 0%, #FFC300 100%)',
blackGold: 'linear-gradient(135deg, #0A0E17 0%, #2D2416 50%, #0A0E17 100%)',
card: 'linear-gradient(135deg, #1A1F2E 0%, #212633 100%)',
},
// 重要性等级金色配色
importance: {
S: {
bg: 'rgba(255, 195, 0, 0.15)',
border: '#FFC300',
text: '#FFD54F',
badge: '#FFC300',
},
A: {
bg: 'rgba(249, 115, 22, 0.15)',
border: '#F97316',
text: '#FB923C',
badge: '#F97316',
},
B: {
bg: 'rgba(59, 130, 246, 0.15)',
border: '#3B82F6',
text: '#60A5FA',
badge: '#3B82F6',
},
C: {
bg: 'rgba(107, 114, 128, 0.15)',
border: '#6B7280',
text: '#9CA3AF',
badge: '#6B7280',
},
}
};
/**
* 获取专业配色
* @param {string} category - 配色类别
* @param {string} shade - 色阶
*/
export const getProfessionalColor = (category, shade) => {
return PROFESSIONAL_COLORS[category]?.[shade] || '#E8E9ED';
};
/**
* Chakra UI 主题扩展
*/
export const professionalThemeExtension = {
colors: {
professional: PROFESSIONAL_COLORS,
},
styles: {
global: {
body: {
bg: PROFESSIONAL_COLORS.background.primary,
color: PROFESSIONAL_COLORS.text.primary,
},
},
},
};

View File

@@ -17,6 +17,7 @@ import { fetchIndustryData, selectIndustryData, selectIndustryLoading } from '..
import { stockService } from '../../../services/stockService'; import { stockService } from '../../../services/stockService';
import { logger } from '../../../utils/logger'; import { logger } from '../../../utils/logger';
import TradingTimeFilter from './TradingTimeFilter'; import TradingTimeFilter from './TradingTimeFilter';
import { PROFESSIONAL_COLORS } from '../../../constants/professionalTheme';
const { Option } = AntSelect; const { Option } = AntSelect;
@@ -423,10 +424,10 @@ const CompactSearchBox = ({
return ( return (
<div style={{ <div style={{
padding: '16px 20px', padding: '16px 20px',
background: 'linear-gradient(135deg, rgba(255, 255, 255, 0.9) 0%, rgba(250, 250, 250, 0.95) 100%)', background: PROFESSIONAL_COLORS.background.card,
borderRadius: '12px', borderRadius: '12px',
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.08), inset 0 1px 0 rgba(255, 255, 255, 0.5)', boxShadow: '0 4px 12px rgba(0, 0, 0, 0.3), 0 0 20px rgba(255, 195, 0, 0.1)',
border: '1px solid rgba(200, 200, 200, 0.3)', border: `1px solid ${PROFESSIONAL_COLORS.border.default}`,
backdropFilter: 'blur(10px)' backdropFilter: 'blur(10px)'
}}> }}>
{/* 单行紧凑布局 */} {/* 单行紧凑布局 */}
@@ -448,11 +449,13 @@ const CompactSearchBox = ({
style={{ width: 240 }} style={{ width: 240 }}
> >
<Input <Input
prefix={<SearchOutlined style={{ color: '#1890ff' }} />} prefix={<SearchOutlined style={{ color: PROFESSIONAL_COLORS.gold[500] }} />}
style={{ style={{
borderRadius: '8px', borderRadius: '8px',
border: '2px solid #e6f7ff', border: `1px solid ${PROFESSIONAL_COLORS.border.default}`,
boxShadow: '0 2px 8px rgba(24, 144, 255, 0.1)' boxShadow: `0 2px 8px rgba(255, 195, 0, 0.1)`,
background: PROFESSIONAL_COLORS.background.secondary,
color: PROFESSIONAL_COLORS.text.primary
}} }}
/> />
</AutoComplete> </AutoComplete>
@@ -460,7 +463,7 @@ const CompactSearchBox = ({
{/* 时间筛选 */} {/* 时间筛选 */}
<Tooltip title="时间筛选"> <Tooltip title="时间筛选">
<div style={{ display: 'inline-flex', alignItems: 'center', gap: 4 }}> <div style={{ display: 'inline-flex', alignItems: 'center', gap: 4 }}>
<CalendarOutlined style={{ color: '#8c8c8c', fontSize: 12 }} /> <CalendarOutlined style={{ color: PROFESSIONAL_COLORS.gold[500], fontSize: 12 }} />
<TradingTimeFilter <TradingTimeFilter
value={tradingTimeRange?.key || null} value={tradingTimeRange?.key || null}
onChange={handleTradingTimeChange} onChange={handleTradingTimeChange}
@@ -492,7 +495,7 @@ const CompactSearchBox = ({
width: 120, width: 120,
borderRadius: '8px' borderRadius: '8px'
}} }}
suffixIcon={<FilterOutlined style={{ fontSize: 14, color: '#52c41a' }} />} suffixIcon={<FilterOutlined style={{ fontSize: 14, color: PROFESSIONAL_COLORS.gold[500] }} />}
/> />
</Tooltip> </Tooltip>
@@ -526,7 +529,7 @@ const CompactSearchBox = ({
width: 130, width: 130,
borderRadius: '8px' borderRadius: '8px'
}} }}
suffixIcon={<SortAscendingOutlined style={{ fontSize: 14, color: '#faad14' }} />} suffixIcon={<SortAscendingOutlined style={{ fontSize: 14, color: PROFESSIONAL_COLORS.gold[500] }} />}
> >
<Option value="new"> 最新</Option> <Option value="new"> 最新</Option>
<Option value="hot">🔥 最热</Option> <Option value="hot">🔥 最热</Option>

View File

@@ -45,6 +45,7 @@ import {
} from '../../../store/slices/communityDataSlice'; } from '../../../store/slices/communityDataSlice';
import { usePagination } from './DynamicNewsCard/hooks/usePagination'; import { usePagination } from './DynamicNewsCard/hooks/usePagination';
import { PAGINATION_CONFIG, DISPLAY_MODES } from './DynamicNewsCard/constants'; import { PAGINATION_CONFIG, DISPLAY_MODES } from './DynamicNewsCard/constants';
import { PROFESSIONAL_COLORS } from '../../../constants/professionalTheme';
// 🔍 调试:渲染计数器 // 🔍 调试:渲染计数器
let dynamicNewsCardRenderCount = 0; let dynamicNewsCardRenderCount = 0;
@@ -74,8 +75,8 @@ const DynamicNewsCard = forwardRef(({
}, ref) => { }, ref) => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const toast = useToast(); const toast = useToast();
const cardBg = useColorModeValue('white', 'gray.800'); const cardBg = PROFESSIONAL_COLORS.background.card;
const borderColor = useColorModeValue('gray.200', 'gray.700'); const borderColor = PROFESSIONAL_COLORS.border.default;
// 通知权限相关 // 通知权限相关
const { browserPermission, requestBrowserPermission } = useNotification(); const { browserPermission, requestBrowserPermission } = useNotification();
@@ -409,10 +410,10 @@ const [currentMode, setCurrentMode] = useState('vertical');
{/* 第一行:标题 + 通知开关 + 更新时间 */} {/* 第一行:标题 + 通知开关 + 更新时间 */}
<Flex justify="space-between" align="center"> <Flex justify="space-between" align="center">
{/* 左侧:标题 */} {/* 左侧:标题 */}
<Heading size="md"> <Heading size="md" color={PROFESSIONAL_COLORS.text.primary}>
<HStack spacing={2}> <HStack spacing={2}>
<TimeIcon /> <TimeIcon color={PROFESSIONAL_COLORS.gold[500]} />
<Text>实时要闻·动态追踪</Text> <Text bgGradient={PROFESSIONAL_COLORS.gradients.gold} bgClip="text">实时要闻·动态追踪</Text>
</HStack> </HStack>
</Heading> </Heading>
@@ -473,7 +474,7 @@ const [currentMode, setCurrentMode] = useState('vertical');
</Tooltip> </Tooltip>
{/* 更新时间 */} {/* 更新时间 */}
<Text fontSize="xs" color="gray.500" whiteSpace="nowrap"> <Text fontSize="xs" color={PROFESSIONAL_COLORS.text.secondary} whiteSpace="nowrap">
最后更新: {lastUpdateTime?.toLocaleTimeString() || '--'} 最后更新: {lastUpdateTime?.toLocaleTimeString() || '--'}
</Text> </Text>
</HStack> </HStack>
@@ -542,7 +543,7 @@ const [currentMode, setCurrentMode] = useState('vertical');
left={0} left={0}
right={0} right={0}
bottom={0} bottom={0}
bg={useColorModeValue('rgba(255, 255, 255, 0.85)', 'rgba(26, 32, 44, 0.85)')} bg="rgba(26, 31, 46, 0.95)"
display="flex" display="flex"
alignItems="center" alignItems="center"
justifyContent="center" justifyContent="center"
@@ -550,8 +551,8 @@ const [currentMode, setCurrentMode] = useState('vertical');
borderRadius="md" borderRadius="md"
> >
<VStack spacing={3}> <VStack spacing={3}>
<Spinner size="xl" color="blue.500" thickness="4px" /> <Spinner size="xl" color={PROFESSIONAL_COLORS.gold[500]} thickness="4px" />
<Text color={useColorModeValue('gray.600', 'gray.300')} fontWeight="medium"> <Text color={PROFESSIONAL_COLORS.text.primary} fontWeight="medium">
正在加载最新事件... 正在加载最新事件...
</Text> </Text>
</VStack> </VStack>

View File

@@ -204,22 +204,10 @@ const DynamicNewsDetailPanel = ({ event, showHeader = true }) => {
// 🎯 加载事件详情(增加浏览量) // 🎯 加载事件详情(增加浏览量)
loadEventDetail(); loadEventDetail();
// PRO 和 MAX 会员的相关股票默认展开,其他情况收起 // 相关股票默认收起
const shouldOpenStocks = canAccessStocks; setIsStocksOpen(false);
setIsStocksOpen(shouldOpenStocks);
setHasLoadedStocks(false); setHasLoadedStocks(false);
// PRO 和 MAX 会员自动加载股票数据(无论是否展开)
const shouldLoadStocks = canAccessStocks; // PRO 或 MAX 都有权限
if (shouldLoadStocks) {
console.log('%c📊 [PRO/MAX会员] 自动加载相关股票数据', 'color: #10B981; font-weight: bold;', {
eventId: event?.id,
userTier
});
loadStocksData();
setHasLoadedStocks(true);
}
setIsConceptsOpen(false); setIsConceptsOpen(false);
setIsHistoricalOpen(false); setIsHistoricalOpen(false);
setHasLoadedHistorical(false); setHasLoadedHistorical(false);

View File

@@ -13,6 +13,7 @@ import {
useColorModeValue, useColorModeValue,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { getImportanceConfig } from '../../../../constants/importanceLevels'; import { getImportanceConfig } from '../../../../constants/importanceLevels';
import { PROFESSIONAL_COLORS } from '../../../../constants/professionalTheme';
// 导入子组件 // 导入子组件
import ImportanceStamp from './ImportanceStamp'; import ImportanceStamp from './ImportanceStamp';
@@ -53,45 +54,46 @@ const HorizontalDynamicNewsEventCard = ({
}) => { }) => {
const importance = getImportanceConfig(event.importance); const importance = getImportanceConfig(event.importance);
// 所有 useColorModeValue 必须在顶层调用 // 专业配色 - 黑色、灰色、金色主题
const cardBg = useColorModeValue('white', 'gray.800'); const cardBg = PROFESSIONAL_COLORS.background.card;
const cardBgAlt = useColorModeValue('gray.50', 'gray.750'); const cardBgHover = PROFESSIONAL_COLORS.background.cardHover;
const selectedBg = useColorModeValue('blue.50', 'blue.900'); const selectedBg = 'rgba(255, 195, 0, 0.1)'; // 金色半透明
const selectedBorderColor = useColorModeValue('blue.500', 'blue.400'); const selectedBorderColor = PROFESSIONAL_COLORS.gold[500];
const linkColor = useColorModeValue('blue.600', 'blue.400'); const linkColor = PROFESSIONAL_COLORS.text.primary;
const borderColor = PROFESSIONAL_COLORS.border.default;
/** /**
* 根据平均涨幅计算背景色(分级策略)- 使用毛玻璃效果 * 根据平均涨幅计算背景色(专业配色 - 深色主题)
* @param {number} avgChange - 平均涨跌幅 * @param {number} avgChange - 平均涨跌幅
* @returns {string} Chakra UI 颜色值 * @returns {string} CSS 颜色值
*/ */
const getChangeBasedBgColor = (avgChange) => { const getChangeBasedBgColor = (avgChange) => {
const numChange = Number(avgChange); const numChange = Number(avgChange);
// 如果没有涨跌幅数据,使用半透明背景 // 如果没有涨跌幅数据,使用默认卡片背景
if (avgChange == null || isNaN(numChange)) { if (avgChange == null || isNaN(numChange)) {
return useColorModeValue('rgba(255, 255, 255, 0.8)', 'rgba(26, 32, 44, 0.8)'); return PROFESSIONAL_COLORS.background.card;
} }
// 根据涨跌幅分级返回半透明背景色(毛玻璃效果) // 根据涨跌幅分级返回深色主题背景色
const absChange = Math.abs(numChange); const absChange = Math.abs(numChange);
if (numChange > 0) { if (numChange > 0) {
// 涨:红色系半透明 // 涨:红色系
if (absChange >= 9) return useColorModeValue('rgba(254, 202, 202, 0.9)', 'rgba(127, 29, 29, 0.9)'); if (absChange >= 9) return 'rgba(239, 68, 68, 0.15)';
if (absChange >= 7) return useColorModeValue('rgba(254, 202, 202, 0.8)', 'rgba(153, 27, 27, 0.8)'); if (absChange >= 7) return 'rgba(239, 68, 68, 0.12)';
if (absChange >= 5) return useColorModeValue('rgba(254, 226, 226, 0.8)', 'rgba(185, 28, 28, 0.8)'); if (absChange >= 5) return 'rgba(239, 68, 68, 0.1)';
if (absChange >= 3) return useColorModeValue('rgba(254, 226, 226, 0.7)', 'rgba(220, 38, 38, 0.7)'); if (absChange >= 3) return 'rgba(239, 68, 68, 0.08)';
return useColorModeValue('rgba(254, 242, 242, 0.7)', 'rgba(239, 68, 68, 0.7)'); return 'rgba(239, 68, 68, 0.05)';
} else if (numChange < 0) { } else if (numChange < 0) {
// 跌:绿色系半透明 // 跌:绿色系
if (absChange >= 9) return useColorModeValue('rgba(187, 247, 208, 0.9)', 'rgba(20, 83, 45, 0.9)'); if (absChange >= 9) return 'rgba(16, 185, 129, 0.15)';
if (absChange >= 7) return useColorModeValue('rgba(187, 247, 208, 0.8)', 'rgba(22, 101, 52, 0.8)'); if (absChange >= 7) return 'rgba(16, 185, 129, 0.12)';
if (absChange >= 5) return useColorModeValue('rgba(209, 250, 229, 0.8)', 'rgba(21, 128, 61, 0.8)'); if (absChange >= 5) return 'rgba(16, 185, 129, 0.1)';
if (absChange >= 3) return useColorModeValue('rgba(209, 250, 229, 0.7)', 'rgba(22, 163, 74, 0.7)'); if (absChange >= 3) return 'rgba(16, 185, 129, 0.08)';
return useColorModeValue('rgba(240, 253, 244, 0.7)', 'rgba(34, 197, 94, 0.7)'); return 'rgba(16, 185, 129, 0.05)';
} }
return useColorModeValue('rgba(255, 255, 255, 0.8)', 'rgba(26, 32, 44, 0.8)'); return PROFESSIONAL_COLORS.background.card;
}; };
return ( return (
@@ -134,11 +136,13 @@ const HorizontalDynamicNewsEventCard = ({
top: 0, top: 0,
left: 0, left: 0,
right: 0, right: 0,
height: '4px', height: '3px',
bgGradient: `linear(to-r, ${importance.color}, ${importance.borderColor})`, background: isSelected
? PROFESSIONAL_COLORS.gradients.gold
: `linear-gradient(90deg, ${PROFESSIONAL_COLORS.importance[importance.level]?.border || PROFESSIONAL_COLORS.gold[500]} 0%, transparent 100%)`,
borderTopLeftRadius: 'xl', borderTopLeftRadius: 'xl',
borderTopRightRadius: 'xl', borderTopRightRadius: 'xl',
opacity: isSelected ? 1 : 0.7, opacity: 0.8,
}} }}
transition="all 0.3s cubic-bezier(0.4, 0, 0.2, 1)" transition="all 0.3s cubic-bezier(0.4, 0, 0.2, 1)"
cursor="pointer" cursor="pointer"

View File

@@ -42,6 +42,7 @@ import {
import { TrendingUp, Activity, Globe, Zap } from 'lucide-react'; import { TrendingUp, Activity, Globe, Zap } from 'lucide-react';
import ReactECharts from 'echarts-for-react'; import ReactECharts from 'echarts-for-react';
import { logger } from '../../../utils/logger'; import { logger } from '../../../utils/logger';
import { PROFESSIONAL_COLORS } from '../../../constants/professionalTheme';
/** /**
* 获取指数行情数据(日线数据) * 获取指数行情数据(日线数据)

View File

@@ -33,6 +33,7 @@ import { useCommunityEvents } from './hooks/useCommunityEvents';
import { logger } from '../../utils/logger'; import { logger } from '../../utils/logger';
import { useNotification } from '../../contexts/NotificationContext'; import { useNotification } from '../../contexts/NotificationContext';
import { PROFESSIONAL_COLORS } from '../../constants/professionalTheme';
// 导航栏已由 MainLayout 提供,无需在此导入 // 导航栏已由 MainLayout 提供,无需在此导入
@@ -43,10 +44,10 @@ const Community = () => {
// Redux状态 // Redux状态
const { popularKeywords, hotEvents } = useSelector(state => state.communityData); const { popularKeywords, hotEvents } = useSelector(state => state.communityData);
// Chakra UI hooks // 专业配色 - 深色主题
const bgColor = useColorModeValue('gray.50', 'gray.900'); const bgColor = PROFESSIONAL_COLORS.background.primary;
const alertBgColor = useColorModeValue('blue.50', 'blue.900'); const alertBgColor = 'rgba(59, 130, 246, 0.1)';
const alertBorderColor = useColorModeValue('blue.200', 'blue.700'); const alertBorderColor = PROFESSIONAL_COLORS.border.default;
// Ref用于首次滚动到内容区域 // Ref用于首次滚动到内容区域
const containerRef = useRef(null); const containerRef = useRef(null);