update ui
This commit is contained in:
118
src/constants/professionalTheme.js
Normal file
118
src/constants/professionalTheme.js
Normal 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,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -17,6 +17,7 @@ import { fetchIndustryData, selectIndustryData, selectIndustryLoading } from '..
|
||||
import { stockService } from '../../../services/stockService';
|
||||
import { logger } from '../../../utils/logger';
|
||||
import TradingTimeFilter from './TradingTimeFilter';
|
||||
import { PROFESSIONAL_COLORS } from '../../../constants/professionalTheme';
|
||||
|
||||
const { Option } = AntSelect;
|
||||
|
||||
@@ -423,10 +424,10 @@ const CompactSearchBox = ({
|
||||
return (
|
||||
<div style={{
|
||||
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',
|
||||
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.08), inset 0 1px 0 rgba(255, 255, 255, 0.5)',
|
||||
border: '1px solid rgba(200, 200, 200, 0.3)',
|
||||
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.3), 0 0 20px rgba(255, 195, 0, 0.1)',
|
||||
border: `1px solid ${PROFESSIONAL_COLORS.border.default}`,
|
||||
backdropFilter: 'blur(10px)'
|
||||
}}>
|
||||
{/* 单行紧凑布局 */}
|
||||
@@ -448,11 +449,13 @@ const CompactSearchBox = ({
|
||||
style={{ width: 240 }}
|
||||
>
|
||||
<Input
|
||||
prefix={<SearchOutlined style={{ color: '#1890ff' }} />}
|
||||
prefix={<SearchOutlined style={{ color: PROFESSIONAL_COLORS.gold[500] }} />}
|
||||
style={{
|
||||
borderRadius: '8px',
|
||||
border: '2px solid #e6f7ff',
|
||||
boxShadow: '0 2px 8px rgba(24, 144, 255, 0.1)'
|
||||
border: `1px solid ${PROFESSIONAL_COLORS.border.default}`,
|
||||
boxShadow: `0 2px 8px rgba(255, 195, 0, 0.1)`,
|
||||
background: PROFESSIONAL_COLORS.background.secondary,
|
||||
color: PROFESSIONAL_COLORS.text.primary
|
||||
}}
|
||||
/>
|
||||
</AutoComplete>
|
||||
@@ -460,7 +463,7 @@ const CompactSearchBox = ({
|
||||
{/* 时间筛选 */}
|
||||
<Tooltip title="时间筛选">
|
||||
<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
|
||||
value={tradingTimeRange?.key || null}
|
||||
onChange={handleTradingTimeChange}
|
||||
@@ -492,7 +495,7 @@ const CompactSearchBox = ({
|
||||
width: 120,
|
||||
borderRadius: '8px'
|
||||
}}
|
||||
suffixIcon={<FilterOutlined style={{ fontSize: 14, color: '#52c41a' }} />}
|
||||
suffixIcon={<FilterOutlined style={{ fontSize: 14, color: PROFESSIONAL_COLORS.gold[500] }} />}
|
||||
/>
|
||||
</Tooltip>
|
||||
|
||||
@@ -526,7 +529,7 @@ const CompactSearchBox = ({
|
||||
width: 130,
|
||||
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="hot">🔥 最热</Option>
|
||||
|
||||
@@ -45,6 +45,7 @@ import {
|
||||
} from '../../../store/slices/communityDataSlice';
|
||||
import { usePagination } from './DynamicNewsCard/hooks/usePagination';
|
||||
import { PAGINATION_CONFIG, DISPLAY_MODES } from './DynamicNewsCard/constants';
|
||||
import { PROFESSIONAL_COLORS } from '../../../constants/professionalTheme';
|
||||
|
||||
// 🔍 调试:渲染计数器
|
||||
let dynamicNewsCardRenderCount = 0;
|
||||
@@ -74,8 +75,8 @@ const DynamicNewsCard = forwardRef(({
|
||||
}, ref) => {
|
||||
const dispatch = useDispatch();
|
||||
const toast = useToast();
|
||||
const cardBg = useColorModeValue('white', 'gray.800');
|
||||
const borderColor = useColorModeValue('gray.200', 'gray.700');
|
||||
const cardBg = PROFESSIONAL_COLORS.background.card;
|
||||
const borderColor = PROFESSIONAL_COLORS.border.default;
|
||||
|
||||
// 通知权限相关
|
||||
const { browserPermission, requestBrowserPermission } = useNotification();
|
||||
@@ -409,10 +410,10 @@ const [currentMode, setCurrentMode] = useState('vertical');
|
||||
{/* 第一行:标题 + 通知开关 + 更新时间 */}
|
||||
<Flex justify="space-between" align="center">
|
||||
{/* 左侧:标题 */}
|
||||
<Heading size="md">
|
||||
<Heading size="md" color={PROFESSIONAL_COLORS.text.primary}>
|
||||
<HStack spacing={2}>
|
||||
<TimeIcon />
|
||||
<Text>实时要闻·动态追踪</Text>
|
||||
<TimeIcon color={PROFESSIONAL_COLORS.gold[500]} />
|
||||
<Text bgGradient={PROFESSIONAL_COLORS.gradients.gold} bgClip="text">实时要闻·动态追踪</Text>
|
||||
</HStack>
|
||||
</Heading>
|
||||
|
||||
@@ -473,7 +474,7 @@ const [currentMode, setCurrentMode] = useState('vertical');
|
||||
</Tooltip>
|
||||
|
||||
{/* 更新时间 */}
|
||||
<Text fontSize="xs" color="gray.500" whiteSpace="nowrap">
|
||||
<Text fontSize="xs" color={PROFESSIONAL_COLORS.text.secondary} whiteSpace="nowrap">
|
||||
最后更新: {lastUpdateTime?.toLocaleTimeString() || '--'}
|
||||
</Text>
|
||||
</HStack>
|
||||
@@ -542,7 +543,7 @@ const [currentMode, setCurrentMode] = useState('vertical');
|
||||
left={0}
|
||||
right={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"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
@@ -550,8 +551,8 @@ const [currentMode, setCurrentMode] = useState('vertical');
|
||||
borderRadius="md"
|
||||
>
|
||||
<VStack spacing={3}>
|
||||
<Spinner size="xl" color="blue.500" thickness="4px" />
|
||||
<Text color={useColorModeValue('gray.600', 'gray.300')} fontWeight="medium">
|
||||
<Spinner size="xl" color={PROFESSIONAL_COLORS.gold[500]} thickness="4px" />
|
||||
<Text color={PROFESSIONAL_COLORS.text.primary} fontWeight="medium">
|
||||
正在加载最新事件...
|
||||
</Text>
|
||||
</VStack>
|
||||
|
||||
@@ -204,22 +204,10 @@ const DynamicNewsDetailPanel = ({ event, showHeader = true }) => {
|
||||
// 🎯 加载事件详情(增加浏览量)
|
||||
loadEventDetail();
|
||||
|
||||
// PRO 和 MAX 会员的相关股票默认展开,其他情况收起
|
||||
const shouldOpenStocks = canAccessStocks;
|
||||
setIsStocksOpen(shouldOpenStocks);
|
||||
// 相关股票默认收起
|
||||
setIsStocksOpen(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);
|
||||
setIsHistoricalOpen(false);
|
||||
setHasLoadedHistorical(false);
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
useColorModeValue,
|
||||
} from '@chakra-ui/react';
|
||||
import { getImportanceConfig } from '../../../../constants/importanceLevels';
|
||||
import { PROFESSIONAL_COLORS } from '../../../../constants/professionalTheme';
|
||||
|
||||
// 导入子组件
|
||||
import ImportanceStamp from './ImportanceStamp';
|
||||
@@ -53,45 +54,46 @@ const HorizontalDynamicNewsEventCard = ({
|
||||
}) => {
|
||||
const importance = getImportanceConfig(event.importance);
|
||||
|
||||
// 所有 useColorModeValue 必须在顶层调用
|
||||
const cardBg = useColorModeValue('white', 'gray.800');
|
||||
const cardBgAlt = useColorModeValue('gray.50', 'gray.750');
|
||||
const selectedBg = useColorModeValue('blue.50', 'blue.900');
|
||||
const selectedBorderColor = useColorModeValue('blue.500', 'blue.400');
|
||||
const linkColor = useColorModeValue('blue.600', 'blue.400');
|
||||
// 专业配色 - 黑色、灰色、金色主题
|
||||
const cardBg = PROFESSIONAL_COLORS.background.card;
|
||||
const cardBgHover = PROFESSIONAL_COLORS.background.cardHover;
|
||||
const selectedBg = 'rgba(255, 195, 0, 0.1)'; // 金色半透明
|
||||
const selectedBorderColor = PROFESSIONAL_COLORS.gold[500];
|
||||
const linkColor = PROFESSIONAL_COLORS.text.primary;
|
||||
const borderColor = PROFESSIONAL_COLORS.border.default;
|
||||
|
||||
/**
|
||||
* 根据平均涨幅计算背景色(分级策略)- 使用毛玻璃效果
|
||||
* 根据平均涨幅计算背景色(专业配色 - 深色主题)
|
||||
* @param {number} avgChange - 平均涨跌幅
|
||||
* @returns {string} Chakra UI 颜色值
|
||||
* @returns {string} CSS 颜色值
|
||||
*/
|
||||
const getChangeBasedBgColor = (avgChange) => {
|
||||
const numChange = Number(avgChange);
|
||||
|
||||
// 如果没有涨跌幅数据,使用半透明背景
|
||||
// 如果没有涨跌幅数据,使用默认卡片背景
|
||||
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);
|
||||
if (numChange > 0) {
|
||||
// 涨:红色系半透明
|
||||
if (absChange >= 9) return useColorModeValue('rgba(254, 202, 202, 0.9)', 'rgba(127, 29, 29, 0.9)');
|
||||
if (absChange >= 7) return useColorModeValue('rgba(254, 202, 202, 0.8)', 'rgba(153, 27, 27, 0.8)');
|
||||
if (absChange >= 5) return useColorModeValue('rgba(254, 226, 226, 0.8)', 'rgba(185, 28, 28, 0.8)');
|
||||
if (absChange >= 3) return useColorModeValue('rgba(254, 226, 226, 0.7)', 'rgba(220, 38, 38, 0.7)');
|
||||
return useColorModeValue('rgba(254, 242, 242, 0.7)', 'rgba(239, 68, 68, 0.7)');
|
||||
// 涨:深红色系
|
||||
if (absChange >= 9) return 'rgba(239, 68, 68, 0.15)';
|
||||
if (absChange >= 7) return 'rgba(239, 68, 68, 0.12)';
|
||||
if (absChange >= 5) return 'rgba(239, 68, 68, 0.1)';
|
||||
if (absChange >= 3) return 'rgba(239, 68, 68, 0.08)';
|
||||
return 'rgba(239, 68, 68, 0.05)';
|
||||
} else if (numChange < 0) {
|
||||
// 跌:绿色系半透明
|
||||
if (absChange >= 9) return useColorModeValue('rgba(187, 247, 208, 0.9)', 'rgba(20, 83, 45, 0.9)');
|
||||
if (absChange >= 7) return useColorModeValue('rgba(187, 247, 208, 0.8)', 'rgba(22, 101, 52, 0.8)');
|
||||
if (absChange >= 5) return useColorModeValue('rgba(209, 250, 229, 0.8)', 'rgba(21, 128, 61, 0.8)');
|
||||
if (absChange >= 3) return useColorModeValue('rgba(209, 250, 229, 0.7)', 'rgba(22, 163, 74, 0.7)');
|
||||
return useColorModeValue('rgba(240, 253, 244, 0.7)', 'rgba(34, 197, 94, 0.7)');
|
||||
// 跌:深绿色系
|
||||
if (absChange >= 9) return 'rgba(16, 185, 129, 0.15)';
|
||||
if (absChange >= 7) return 'rgba(16, 185, 129, 0.12)';
|
||||
if (absChange >= 5) return 'rgba(16, 185, 129, 0.1)';
|
||||
if (absChange >= 3) return 'rgba(16, 185, 129, 0.08)';
|
||||
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 (
|
||||
@@ -134,11 +136,13 @@ const HorizontalDynamicNewsEventCard = ({
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
height: '4px',
|
||||
bgGradient: `linear(to-r, ${importance.color}, ${importance.borderColor})`,
|
||||
height: '3px',
|
||||
background: isSelected
|
||||
? PROFESSIONAL_COLORS.gradients.gold
|
||||
: `linear-gradient(90deg, ${PROFESSIONAL_COLORS.importance[importance.level]?.border || PROFESSIONAL_COLORS.gold[500]} 0%, transparent 100%)`,
|
||||
borderTopLeftRadius: 'xl',
|
||||
borderTopRightRadius: 'xl',
|
||||
opacity: isSelected ? 1 : 0.7,
|
||||
opacity: 0.8,
|
||||
}}
|
||||
transition="all 0.3s cubic-bezier(0.4, 0, 0.2, 1)"
|
||||
cursor="pointer"
|
||||
|
||||
@@ -42,6 +42,7 @@ import {
|
||||
import { TrendingUp, Activity, Globe, Zap } from 'lucide-react';
|
||||
import ReactECharts from 'echarts-for-react';
|
||||
import { logger } from '../../../utils/logger';
|
||||
import { PROFESSIONAL_COLORS } from '../../../constants/professionalTheme';
|
||||
|
||||
/**
|
||||
* 获取指数行情数据(日线数据)
|
||||
|
||||
@@ -33,6 +33,7 @@ import { useCommunityEvents } from './hooks/useCommunityEvents';
|
||||
|
||||
import { logger } from '../../utils/logger';
|
||||
import { useNotification } from '../../contexts/NotificationContext';
|
||||
import { PROFESSIONAL_COLORS } from '../../constants/professionalTheme';
|
||||
|
||||
// 导航栏已由 MainLayout 提供,无需在此导入
|
||||
|
||||
@@ -43,10 +44,10 @@ const Community = () => {
|
||||
// Redux状态
|
||||
const { popularKeywords, hotEvents } = useSelector(state => state.communityData);
|
||||
|
||||
// Chakra UI hooks
|
||||
const bgColor = useColorModeValue('gray.50', 'gray.900');
|
||||
const alertBgColor = useColorModeValue('blue.50', 'blue.900');
|
||||
const alertBorderColor = useColorModeValue('blue.200', 'blue.700');
|
||||
// 专业配色 - 深色主题
|
||||
const bgColor = PROFESSIONAL_COLORS.background.primary;
|
||||
const alertBgColor = 'rgba(59, 130, 246, 0.1)';
|
||||
const alertBorderColor = PROFESSIONAL_COLORS.border.default;
|
||||
|
||||
// Ref:用于首次滚动到内容区域
|
||||
const containerRef = useRef(null);
|
||||
|
||||
Reference in New Issue
Block a user