From 1a55e037c9e6259b709ad76b53862d865c409488 Mon Sep 17 00:00:00 2001 From: zdl <3489966805@qq.com> Date: Mon, 22 Dec 2025 16:50:49 +0800 Subject: [PATCH] =?UTF-8?q?feat(MarketDashboard):=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E6=8A=95=E8=B5=84=E4=BB=AA=E8=A1=A8=E7=9B=98=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 指数卡片组件(带迷你面积图) - 成交额柱状图、涨跌分布图组件 - 热门板块排行组件 - 毛玻璃背景,黑金配色 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../MarketDashboard/MarketDashboard.js | 50 +++++ .../MarketDashboard/components/HotConcepts.js | 45 +++++ .../components/MarketOverview.js | 81 ++++++++ .../components/PlatformStats.js | 34 ++++ .../components/TradingCalendar.js | 179 ++++++++++++++++++ .../components/atoms/ConceptItem.js | 57 ++++++ .../components/atoms/DayCell.js | 87 +++++++++ .../components/atoms/HotSectorsRanking.js | 78 ++++++++ .../components/atoms/IndexCard.js | 55 ++++++ .../components/atoms/IndexChartCard.js | 96 ++++++++++ .../components/atoms/MiniTrendLine.js | 50 +++++ .../components/atoms/RiseFallChart.js | 92 +++++++++ .../components/atoms/StatCard.js | 44 +++++ .../components/atoms/StatItem.js | 47 +++++ .../components/atoms/TurnoverChart.js | 56 ++++++ .../MarketDashboard/components/atoms/index.js | 11 ++ .../MarketDashboard/components/index.js | 6 + .../components/MarketDashboard/constants.ts | 108 +++++++++++ .../components/MarketDashboard/index.js | 3 + .../components/MarketDashboard/types.ts | 88 +++++++++ 20 files changed, 1267 insertions(+) create mode 100644 src/views/Profile/components/MarketDashboard/MarketDashboard.js create mode 100644 src/views/Profile/components/MarketDashboard/components/HotConcepts.js create mode 100644 src/views/Profile/components/MarketDashboard/components/MarketOverview.js create mode 100644 src/views/Profile/components/MarketDashboard/components/PlatformStats.js create mode 100644 src/views/Profile/components/MarketDashboard/components/TradingCalendar.js create mode 100644 src/views/Profile/components/MarketDashboard/components/atoms/ConceptItem.js create mode 100644 src/views/Profile/components/MarketDashboard/components/atoms/DayCell.js create mode 100644 src/views/Profile/components/MarketDashboard/components/atoms/HotSectorsRanking.js create mode 100644 src/views/Profile/components/MarketDashboard/components/atoms/IndexCard.js create mode 100644 src/views/Profile/components/MarketDashboard/components/atoms/IndexChartCard.js create mode 100644 src/views/Profile/components/MarketDashboard/components/atoms/MiniTrendLine.js create mode 100644 src/views/Profile/components/MarketDashboard/components/atoms/RiseFallChart.js create mode 100644 src/views/Profile/components/MarketDashboard/components/atoms/StatCard.js create mode 100644 src/views/Profile/components/MarketDashboard/components/atoms/StatItem.js create mode 100644 src/views/Profile/components/MarketDashboard/components/atoms/TurnoverChart.js create mode 100644 src/views/Profile/components/MarketDashboard/components/atoms/index.js create mode 100644 src/views/Profile/components/MarketDashboard/components/index.js create mode 100644 src/views/Profile/components/MarketDashboard/constants.ts create mode 100644 src/views/Profile/components/MarketDashboard/index.js create mode 100644 src/views/Profile/components/MarketDashboard/types.ts diff --git a/src/views/Profile/components/MarketDashboard/MarketDashboard.js b/src/views/Profile/components/MarketDashboard/MarketDashboard.js new file mode 100644 index 00000000..1e2c0e46 --- /dev/null +++ b/src/views/Profile/components/MarketDashboard/MarketDashboard.js @@ -0,0 +1,50 @@ +// 市场概览仪表盘主组件 - 投资仪表盘 +import React from 'react'; +import { Box, Text, HStack, Icon } from '@chakra-ui/react'; +import { TrendingUp } from 'lucide-react'; +import GlassCard from '@components/GlassCard'; +import { MarketOverview } from './components'; +import { MOCK_INDICES, MOCK_MARKET_STATS } from './constants'; + +const MarketDashboard = ({ + indices = MOCK_INDICES, + marketStats = MOCK_MARKET_STATS, +}) => { + return ( + + {/* 标题栏 */} + + + + 投资仪表盘 + + + + + {/* 市场概况:指数卡片 + 成交额 + 涨跌分布 + 热门板块 */} + + + ); +}; + +export default MarketDashboard; diff --git a/src/views/Profile/components/MarketDashboard/components/HotConcepts.js b/src/views/Profile/components/MarketDashboard/components/HotConcepts.js new file mode 100644 index 00000000..d228c901 --- /dev/null +++ b/src/views/Profile/components/MarketDashboard/components/HotConcepts.js @@ -0,0 +1,45 @@ +// 热点概念组件 +import React from 'react'; +import { Box, Text, VStack, SimpleGrid, HStack, Icon } from '@chakra-ui/react'; +import { Flame } from 'lucide-react'; +import { ConceptItem } from './atoms'; +import { THEME } from '../constants'; + +const HotConcepts = ({ concepts = [], onConceptClick }) => { + return ( + + + {/* 标题 */} + + + + 热点概念 + + + + {/* 概念列表 */} + + {concepts.map((concept) => ( + onConceptClick?.(concept)} + /> + ))} + + + + ); +}; + +export default HotConcepts; diff --git a/src/views/Profile/components/MarketDashboard/components/MarketOverview.js b/src/views/Profile/components/MarketDashboard/components/MarketOverview.js new file mode 100644 index 00000000..df09b713 --- /dev/null +++ b/src/views/Profile/components/MarketDashboard/components/MarketOverview.js @@ -0,0 +1,81 @@ +// 市场概况组件 - 顶部横条(匹配设计图布局) +import React from 'react'; +import { Box, SimpleGrid } from '@chakra-ui/react'; +import { + IndexChartCard, + TurnoverChart, + RiseFallChart, + HotSectorsRanking, +} from './atoms'; + +const MarketOverview = ({ indices = [], marketStats = {} }) => { + // 默认指数数据(带图表数据) + const defaultIndices = [ + { + code: 'sh000001', + name: '上证指数', + value: 3391.88, + change: 1.23, + chartData: [3350, 3360, 3355, 3370, 3365, 3380, 3375, 3390, 3385, 3392], + }, + { + code: 'sz399001', + name: '深证成指', + value: 10728.54, + change: 0.86, + chartData: [10650, 10680, 10660, 10700, 10690, 10720, 10710, 10730, 10720, 10728], + }, + { + code: 'sz399006', + name: '创业板指', + value: 2156.32, + change: -0.45, + chartData: [2180, 2175, 2170, 2165, 2168, 2160, 2165, 2158, 2160, 2156], + }, + ]; + + const displayIndices = indices.length > 0 ? indices : defaultIndices; + + return ( + + {/* 6列网格布局:3个指数卡片 + 成交额 + 涨跌分布 + 热门板块 */} + + {/* 指数卡片(带迷你图表) */} + {displayIndices.map((index) => ( + + ))} + + {/* 成交额柱状图 */} + + + {/* 涨跌分布图 */} + + + {/* 热门板块排行 */} + + + + ); +}; + +export default MarketOverview; diff --git a/src/views/Profile/components/MarketDashboard/components/PlatformStats.js b/src/views/Profile/components/MarketDashboard/components/PlatformStats.js new file mode 100644 index 00000000..68b622d8 --- /dev/null +++ b/src/views/Profile/components/MarketDashboard/components/PlatformStats.js @@ -0,0 +1,34 @@ +// AI平台能力统计组件 - 底部横条 +import React from 'react'; +import { Box, HStack, Divider } from '@chakra-ui/react'; +import { StatItem } from './atoms'; +import { THEME } from '../constants'; + +const PlatformStats = ({ stats = [] }) => { + return ( + + + }> + {stats.map((stat, index) => ( + + ))} + + + ); +}; + +export default PlatformStats; diff --git a/src/views/Profile/components/MarketDashboard/components/TradingCalendar.js b/src/views/Profile/components/MarketDashboard/components/TradingCalendar.js new file mode 100644 index 00000000..f250aae6 --- /dev/null +++ b/src/views/Profile/components/MarketDashboard/components/TradingCalendar.js @@ -0,0 +1,179 @@ +// 交易日历组件 +import React, { useState, useMemo } from 'react'; +import { + Box, + Text, + VStack, + HStack, + Grid, + GridItem, + IconButton, +} from '@chakra-ui/react'; +import { ChevronLeft, ChevronRight, Calendar } from 'lucide-react'; +import { DayCell } from './atoms'; +import { THEME, WEEKDAY_LABELS } from '../constants'; + +const TradingCalendar = ({ tradingDays = [] }) => { + const [currentDate, setCurrentDate] = useState(new Date()); + + const calendarData = useMemo(() => { + const year = currentDate.getFullYear(); + const month = currentDate.getMonth(); + + // 当月第一天 + const firstDay = new Date(year, month, 1); + // 当月最后一天 + const lastDay = new Date(year, month + 1, 0); + // 第一天是星期几 + const startWeekday = firstDay.getDay(); + // 当月天数 + const daysInMonth = lastDay.getDate(); + + // 上月最后几天 + const prevMonthLastDay = new Date(year, month, 0).getDate(); + + const days = []; + + // 填充上月日期 + for (let i = startWeekday - 1; i >= 0; i--) { + days.push({ + day: prevMonthLastDay - i, + isCurrentMonth: false, + isWeekend: false, + isTrading: false, + }); + } + + // 填充当月日期 + const today = new Date(); + for (let day = 1; day <= daysInMonth; day++) { + const date = new Date(year, month, day); + const weekday = date.getDay(); + const isWeekend = weekday === 0 || weekday === 6; + const isToday = + day === today.getDate() && + month === today.getMonth() && + year === today.getFullYear(); + + // 检查是否为交易日(简化逻辑:非周末即交易日) + // 实际应用中应该从 tradingDays 数组判断 + const dateStr = `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`; + const isTrading = tradingDays.length > 0 + ? tradingDays.includes(dateStr) + : !isWeekend; + + days.push({ + day, + isCurrentMonth: true, + isWeekend, + isTrading, + isToday, + }); + } + + // 填充下月日期(补满 6 行 * 7 天 = 42 格) + const remaining = 42 - days.length; + for (let day = 1; day <= remaining; day++) { + days.push({ + day, + isCurrentMonth: false, + isWeekend: false, + isTrading: false, + }); + } + + return days; + }, [currentDate, tradingDays]); + + const handlePrevMonth = () => { + setCurrentDate(new Date(currentDate.getFullYear(), currentDate.getMonth() - 1, 1)); + }; + + const handleNextMonth = () => { + setCurrentDate(new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 1)); + }; + + const monthText = `${currentDate.getFullYear()}年${currentDate.getMonth() + 1}月`; + + return ( + + + {/* 日历头部 */} + + + + + 交易日历 + + + + } + size="xs" + variant="ghost" + color="rgba(255, 255, 255, 0.6)" + onClick={handlePrevMonth} + aria-label="上月" + _hover={{ bg: 'rgba(212, 175, 55, 0.15)' }} + /> + + {monthText} + + } + size="xs" + variant="ghost" + color="rgba(255, 255, 255, 0.6)" + onClick={handleNextMonth} + aria-label="下月" + _hover={{ bg: 'rgba(212, 175, 55, 0.15)' }} + /> + + + + {/* 星期标题 */} + + {WEEKDAY_LABELS.map((label, index) => ( + + + {label} + + + ))} + + + {/* 日期网格 */} + + {calendarData.map((dayData, index) => ( + + + + ))} + + + + ); +}; + +export default TradingCalendar; diff --git a/src/views/Profile/components/MarketDashboard/components/atoms/ConceptItem.js b/src/views/Profile/components/MarketDashboard/components/atoms/ConceptItem.js new file mode 100644 index 00000000..a9202f57 --- /dev/null +++ b/src/views/Profile/components/MarketDashboard/components/atoms/ConceptItem.js @@ -0,0 +1,57 @@ +// 概念项组件 +import React from 'react'; +import { Box, Text, HStack } from '@chakra-ui/react'; +import { THEME } from '../../constants'; +import MiniTrendLine from './MiniTrendLine'; + +const ConceptItem = ({ name, change, trend = [], onClick }) => { + const isUp = change >= 0; + const changeColor = isUp ? THEME.status.up : THEME.status.down; + const changeText = isUp ? `+${change.toFixed(2)}%` : `${change.toFixed(2)}%`; + + return ( + + + + {name} + + + {trend.length > 0 && ( + + )} + + {changeText} + + + + + ); +}; + +export default ConceptItem; diff --git a/src/views/Profile/components/MarketDashboard/components/atoms/DayCell.js b/src/views/Profile/components/MarketDashboard/components/atoms/DayCell.js new file mode 100644 index 00000000..d4241718 --- /dev/null +++ b/src/views/Profile/components/MarketDashboard/components/atoms/DayCell.js @@ -0,0 +1,87 @@ +// 日期单元格组件 +import React from 'react'; +import { Box, Text } from '@chakra-ui/react'; +import { THEME } from '../../constants'; + +const DayCell = ({ + day, + isTrading = true, + isToday = false, + isWeekend = false, + isCurrentMonth = true, +}) => { + // 今天的样式(金色背景) + if (isToday) { + return ( + + + {day} + + + ); + } + + // 非当月日期 + if (!isCurrentMonth) { + return ( + + + {day} + + + ); + } + + // 周末(非交易日) + if (isWeekend || !isTrading) { + return ( + + + {day} + + + ); + } + + // 普通交易日 + return ( + + + {day} + + + ); +}; + +export default DayCell; diff --git a/src/views/Profile/components/MarketDashboard/components/atoms/HotSectorsRanking.js b/src/views/Profile/components/MarketDashboard/components/atoms/HotSectorsRanking.js new file mode 100644 index 00000000..d15c44db --- /dev/null +++ b/src/views/Profile/components/MarketDashboard/components/atoms/HotSectorsRanking.js @@ -0,0 +1,78 @@ +// 热门板块排行组件 +import React from 'react'; +import { Box, Text, VStack, HStack } from '@chakra-ui/react'; +import MiniTrendLine from './MiniTrendLine'; + +const HotSectorsRanking = ({ sectors = [], title = '热门板块排行' }) => { + // 默认数据 + const defaultSectors = [ + { rank: 1, name: '人工智能', change: 3.2, trend: [100, 102, 101, 104, 103, 106] }, + { rank: 2, name: '新能源车', change: 1.8, trend: [100, 99, 101, 102, 101, 103] }, + { rank: 3, name: '生物医药', change: 1.3, trend: [100, 101, 100, 102, 101, 102] }, + { rank: 4, name: '消费科技', change: 1.2, trend: [100, 100, 101, 100, 102, 102] }, + { rank: 5, name: '其他', change: 0.4, trend: [100, 100, 100, 101, 100, 101] }, + ]; + + const data = sectors.length > 0 ? sectors : defaultSectors; + + return ( + + + + {title} + + + {/* 排行列表 */} + + {data.map((sector, index) => ( + + {/* 排名 */} + + + {sector.rank} + + + {sector.name} + + + + {/* 趋势线 */} + + = 0 ? 'red' : 'green'} + width={40} + height={14} + /> + + + {/* 涨跌幅 */} + = 0 ? '#EF4444' : '#22C55E'} + fontWeight="medium" + w="50px" + textAlign="right" + > + {sector.change >= 0 ? '+' : ''}{sector.change.toFixed(1)}% + + + ))} + + + + ); +}; + +export default HotSectorsRanking; diff --git a/src/views/Profile/components/MarketDashboard/components/atoms/IndexCard.js b/src/views/Profile/components/MarketDashboard/components/atoms/IndexCard.js new file mode 100644 index 00000000..f1f0facf --- /dev/null +++ b/src/views/Profile/components/MarketDashboard/components/atoms/IndexCard.js @@ -0,0 +1,55 @@ +// 指数卡片组件 +import React from 'react'; +import { Box, Text, HStack, VStack } from '@chakra-ui/react'; +import { THEME } from '../../constants'; +import MiniTrendLine from './MiniTrendLine'; + +const IndexCard = ({ name, value, change, trend = [] }) => { + const isUp = change >= 0; + const changeColor = isUp ? THEME.status.up : THEME.status.down; + const changeText = isUp ? `+${change.toFixed(2)}%` : `${change.toFixed(2)}%`; + + return ( + + + + {name} + + + + + {value.toLocaleString()} + + + {changeText} + + + {trend.length > 0 && ( + + )} + + + + ); +}; + +export default IndexCard; diff --git a/src/views/Profile/components/MarketDashboard/components/atoms/IndexChartCard.js b/src/views/Profile/components/MarketDashboard/components/atoms/IndexChartCard.js new file mode 100644 index 00000000..9862d42c --- /dev/null +++ b/src/views/Profile/components/MarketDashboard/components/atoms/IndexChartCard.js @@ -0,0 +1,96 @@ +// 指数图表卡片 - 带迷你K线图 +import React, { useMemo } from 'react'; +import { Box, Text, VStack, HStack } from '@chakra-ui/react'; + +const IndexChartCard = ({ name, value, change, chartData = [] }) => { + const isUp = change >= 0; + const changeColor = isUp ? '#EF4444' : '#22C55E'; + const changeText = isUp ? `+${change.toFixed(2)}%` : `${change.toFixed(2)}%`; + + // 生成迷你图表路径 + const chartPath = useMemo(() => { + if (!chartData || chartData.length < 2) return ''; + + const width = 120; + const height = 40; + const padding = 4; + + const min = Math.min(...chartData); + const max = Math.max(...chartData); + const range = max - min || 1; + + const points = chartData.map((val, i) => { + const x = padding + (i / (chartData.length - 1)) * (width - padding * 2); + const y = height - padding - ((val - min) / range) * (height - padding * 2); + return `${x},${y}`; + }); + + return `M ${points.join(' L ')}`; + }, [chartData]); + + // 生成填充区域 + const areaPath = useMemo(() => { + if (!chartPath) return ''; + const width = 120; + const height = 40; + return `${chartPath} L ${width - 4},${height - 4} L 4,${height - 4} Z`; + }, [chartPath]); + + return ( + + + {/* 标题 */} + + {name} + + + {/* 数值和涨跌幅 */} + + + {typeof value === 'number' ? value.toLocaleString(undefined, { minimumFractionDigits: 2 }) : value} + + + {changeText} + + + + {/* 迷你图表 */} + {chartData.length > 0 && ( + + + {/* 填充区域 */} + + {/* 线条 */} + + + + )} + + + ); +}; + +export default IndexChartCard; diff --git a/src/views/Profile/components/MarketDashboard/components/atoms/MiniTrendLine.js b/src/views/Profile/components/MarketDashboard/components/atoms/MiniTrendLine.js new file mode 100644 index 00000000..68192657 --- /dev/null +++ b/src/views/Profile/components/MarketDashboard/components/atoms/MiniTrendLine.js @@ -0,0 +1,50 @@ +// 迷你趋势线组件 - 基于 SVG +import React, { useMemo } from 'react'; +import { Box } from '@chakra-ui/react'; +import { THEME } from '../../constants'; + +const MiniTrendLine = ({ + data = [], + color = 'green', + width = 60, + height = 24, +}) => { + const pathD = useMemo(() => { + if (!data || data.length < 2) return ''; + + const min = Math.min(...data); + const max = Math.max(...data); + const range = max - min || 1; + + const points = data.map((value, index) => { + const x = (index / (data.length - 1)) * width; + const y = height - ((value - min) / range) * height * 0.8 - height * 0.1; + return `${x},${y}`; + }); + + return `M ${points.join(' L ')}`; + }, [data, width, height]); + + const strokeColor = color === 'red' ? THEME.status.up : THEME.status.down; + + if (!data || data.length < 2) { + return ; + } + + return ( + + + + + + ); +}; + +export default MiniTrendLine; diff --git a/src/views/Profile/components/MarketDashboard/components/atoms/RiseFallChart.js b/src/views/Profile/components/MarketDashboard/components/atoms/RiseFallChart.js new file mode 100644 index 00000000..207d93ba --- /dev/null +++ b/src/views/Profile/components/MarketDashboard/components/atoms/RiseFallChart.js @@ -0,0 +1,92 @@ +// 涨跌分布图组件 +import React from 'react'; +import { Box, Text, VStack, HStack } from '@chakra-ui/react'; + +const RiseFallChart = ({ + riseCount = 2156, + fallCount = 2034, + flatCount = 312, + title = '涨跌分布' +}) => { + const total = riseCount + fallCount + flatCount; + const risePercent = ((riseCount / total) * 100).toFixed(1); + const fallPercent = ((fallCount / total) * 100).toFixed(1); + const flatPercent = ((flatCount / total) * 100).toFixed(1); + + // 分布数据 - 模拟不同涨跌幅区间 + const distribution = [ + { range: '>7%', rise: 86, fall: 12, label: '涨停' }, + { range: '3-7%', rise: 420, fall: 180 }, + { range: '0-3%', rise: 1650, fall: 0 }, + { range: '-3-0%', rise: 0, fall: 1542 }, + { range: '-7--3%', rise: 0, fall: 280 }, + { range: '<-7%', rise: 0, fall: 20, label: '跌停' }, + ]; + + const maxCount = Math.max(...distribution.map(d => Math.max(d.rise, d.fall))); + + return ( + + + + {title} + + + {/* 水平柱状图 */} + + {distribution.map((item, index) => ( + + {/* 涨(红色,向右) */} + + {item.rise > 0 && ( + + )} + + {/* 中心线 */} + + {/* 跌(绿色,向左显示但实际向右) */} + + {item.fall > 0 && ( + + )} + + + ))} + + + {/* 统计数字 */} + + + 涨 {riseCount} + + + 平 {flatCount} + + + 跌 {fallCount} + + + + + ); +}; + +export default RiseFallChart; diff --git a/src/views/Profile/components/MarketDashboard/components/atoms/StatCard.js b/src/views/Profile/components/MarketDashboard/components/atoms/StatCard.js new file mode 100644 index 00000000..dbfbcc59 --- /dev/null +++ b/src/views/Profile/components/MarketDashboard/components/atoms/StatCard.js @@ -0,0 +1,44 @@ +// 统计卡片组件(涨停/跌停/成交额) +import React from 'react'; +import { Box, Text, VStack } from '@chakra-ui/react'; +import { THEME } from '../../constants'; + +const StatCard = ({ label, value, subLabel, valueColor }) => { + return ( + + + + {label} + + + {value} + + {subLabel && ( + + {subLabel} + + )} + + + ); +}; + +export default StatCard; diff --git a/src/views/Profile/components/MarketDashboard/components/atoms/StatItem.js b/src/views/Profile/components/MarketDashboard/components/atoms/StatItem.js new file mode 100644 index 00000000..f234982c --- /dev/null +++ b/src/views/Profile/components/MarketDashboard/components/atoms/StatItem.js @@ -0,0 +1,47 @@ +// AI平台能力统计项组件 +import React from 'react'; +import { Box, Text, VStack, Icon } from '@chakra-ui/react'; +import { + Building2, + BarChart3, + Calendar, + Bot, + TrendingUp, + Database, +} from 'lucide-react'; +import { THEME } from '../../constants'; + +// 图标映射 +const iconMap = { + building: Building2, + chart: BarChart3, + calendar: Calendar, + robot: Bot, + trending: TrendingUp, + database: Database, +}; + +const StatItem = ({ icon, value, label }) => { + const IconComponent = iconMap[icon] || Database; + + return ( + + + + + {value} + + + {label} + + + + ); +}; + +export default StatItem; diff --git a/src/views/Profile/components/MarketDashboard/components/atoms/TurnoverChart.js b/src/views/Profile/components/MarketDashboard/components/atoms/TurnoverChart.js new file mode 100644 index 00000000..5b779893 --- /dev/null +++ b/src/views/Profile/components/MarketDashboard/components/atoms/TurnoverChart.js @@ -0,0 +1,56 @@ +// 成交额柱状图组件 +import React from 'react'; +import { Box, Text, VStack, HStack } from '@chakra-ui/react'; + +const TurnoverChart = ({ data = [], title = '成交额' }) => { + // 默认数据 + const chartData = data.length > 0 ? data : [ + { time: '10:30', value: 0.85 }, + { time: '11:00', value: 0.92 }, + { time: '11:15', value: 0.78 }, + { time: '13:00', value: 1.05 }, + { time: '13:30', value: 1.12 }, + { time: '14:00', value: 0.95 }, + ]; + + const maxValue = Math.max(...chartData.map(d => d.value)); + + return ( + + + + {title} + + + {/* 柱状图 */} + + {chartData.map((item, index) => ( + + ))} + + + {/* 当前值 */} + + 1.25亿 + + + + ); +}; + +export default TurnoverChart; diff --git a/src/views/Profile/components/MarketDashboard/components/atoms/index.js b/src/views/Profile/components/MarketDashboard/components/atoms/index.js new file mode 100644 index 00000000..988447c5 --- /dev/null +++ b/src/views/Profile/components/MarketDashboard/components/atoms/index.js @@ -0,0 +1,11 @@ +// 原子组件导出 +export { default as MiniTrendLine } from './MiniTrendLine'; +export { default as IndexCard } from './IndexCard'; +export { default as StatCard } from './StatCard'; +export { default as ConceptItem } from './ConceptItem'; +export { default as DayCell } from './DayCell'; +export { default as StatItem } from './StatItem'; +export { default as IndexChartCard } from './IndexChartCard'; +export { default as TurnoverChart } from './TurnoverChart'; +export { default as RiseFallChart } from './RiseFallChart'; +export { default as HotSectorsRanking } from './HotSectorsRanking'; diff --git a/src/views/Profile/components/MarketDashboard/components/index.js b/src/views/Profile/components/MarketDashboard/components/index.js new file mode 100644 index 00000000..b4883089 --- /dev/null +++ b/src/views/Profile/components/MarketDashboard/components/index.js @@ -0,0 +1,6 @@ +// 组件导出 +export { default as MarketOverview } from './MarketOverview'; +export { default as HotConcepts } from './HotConcepts'; +export { default as TradingCalendar } from './TradingCalendar'; +export { default as PlatformStats } from './PlatformStats'; +export * from './atoms'; diff --git a/src/views/Profile/components/MarketDashboard/constants.ts b/src/views/Profile/components/MarketDashboard/constants.ts new file mode 100644 index 00000000..b634a484 --- /dev/null +++ b/src/views/Profile/components/MarketDashboard/constants.ts @@ -0,0 +1,108 @@ +// MarketDashboard 常量定义 + +// 黑金主题配色 +export const THEME = { + bg: { + primary: '#0A0A0A', // 纯黑背景 + card: '#141414', // 卡片背景 + cardHover: '#1A1A1A', // 卡片悬停 + gradient: 'linear-gradient(135deg, #1a1a1a 0%, #0a0a0a 100%)', + }, + text: { + primary: '#FFFFFF', // 主文字(白色) + secondary: '#8B8B8B', // 次要文字(灰色) + accent: '#D4AF37', // 强调色(金色) + gold: '#D4AF37', // 金色 + goldLight: '#F0D78C', // 浅金色 + }, + status: { + up: '#EF4444', // 上涨(红色) + down: '#22C55E', // 下跌(绿色) + }, + border: 'rgba(212, 175, 55, 0.2)', // 金色边框 + borderGold: 'rgba(212, 175, 55, 0.4)', + shadow: '0 4px 20px rgba(212, 175, 55, 0.1)', +}; + +// 模拟数据(后续替换为真实 API) +export const MOCK_INDICES: Array<{ + name: string; + code: string; + value: number; + change: number; + chartData: number[]; +}> = [ + { + name: '上证指数', + code: '000001.SH', + value: 3391.88, + change: 0.52, + chartData: [3350, 3360, 3355, 3370, 3365, 3380, 3375, 3390, 3385, 3392], + }, + { + name: '深证成指', + code: '399001.SZ', + value: 10723.49, + change: 0.68, + chartData: [10650, 10680, 10660, 10700, 10690, 10720, 10710, 10730, 10720, 10723], + }, + { + name: '创业板指', + code: '399006.SZ', + value: 2156.78, + change: 1.23, + chartData: [2130, 2140, 2135, 2150, 2145, 2155, 2150, 2160, 2155, 2157], + }, +]; + +export const MOCK_MARKET_STATS = { + limitUp: 86, + limitDown: 12, + turnover: '1.2万亿', + riseCount: 2156, + fallCount: 2034, + flatCount: 312, + turnoverData: [ + { time: '10:30', value: 0.85 }, + { time: '11:00', value: 0.92 }, + { time: '11:15', value: 0.78 }, + { time: '13:00', value: 1.05 }, + { time: '13:30', value: 1.12 }, + { time: '14:00', value: 0.95 }, + ], + hotSectors: [ + { rank: 1, name: '人工智能', change: 3.2, trend: [100, 102, 101, 104, 103, 106] }, + { rank: 2, name: '新能源车', change: 1.8, trend: [100, 99, 101, 102, 101, 103] }, + { rank: 3, name: '生物医药', change: 1.3, trend: [100, 101, 100, 102, 101, 102] }, + { rank: 4, name: '消费科技', change: 1.2, trend: [100, 100, 101, 100, 102, 102] }, + { rank: 5, name: '半导体', change: 0.9, trend: [100, 100, 100, 101, 100, 101] }, + ], +}; + +export const MOCK_HOT_CONCEPTS: Array<{ + id: string; + name: string; + change: number; + trend: number[]; +}> = [ + { id: '1', name: '人工智能', change: 3.25, trend: [100, 102, 101, 103, 105, 108] }, + { id: '2', name: '芯片概念', change: 2.87, trend: [100, 99, 101, 102, 104, 106] }, + { id: '3', name: '新能源车', change: 2.15, trend: [100, 101, 100, 102, 103, 105] }, + { id: '4', name: '光伏', change: 1.92, trend: [100, 99, 100, 101, 102, 104] }, + { id: '5', name: '医药生物', change: 1.56, trend: [100, 100, 101, 101, 102, 103] }, + { id: '6', name: '消费电子', change: 1.33, trend: [100, 101, 100, 101, 102, 103] }, +]; + +export const MOCK_PLATFORM_STATS: Array<{ + icon: string; + value: string; + label: string; +}> = [ + { icon: 'building', value: '4300+', label: '上市公司' }, + { icon: 'chart', value: '500+', label: '概念板块' }, + { icon: 'calendar', value: '10年+', label: '历史数据' }, + { icon: 'robot', value: '24/7', label: 'AI分析' }, +]; + +// 星期标题 +export const WEEKDAY_LABELS = ['日', '一', '二', '三', '四', '五', '六']; diff --git a/src/views/Profile/components/MarketDashboard/index.js b/src/views/Profile/components/MarketDashboard/index.js new file mode 100644 index 00000000..d480e06f --- /dev/null +++ b/src/views/Profile/components/MarketDashboard/index.js @@ -0,0 +1,3 @@ +// MarketDashboard 组件导出 +export { default } from './MarketDashboard'; +export { default as MarketDashboard } from './MarketDashboard'; diff --git a/src/views/Profile/components/MarketDashboard/types.ts b/src/views/Profile/components/MarketDashboard/types.ts new file mode 100644 index 00000000..37baba47 --- /dev/null +++ b/src/views/Profile/components/MarketDashboard/types.ts @@ -0,0 +1,88 @@ +// MarketDashboard 类型定义 + +// 迷你趋势线 +export interface MiniTrendLineProps { + data: number[]; + color?: 'green' | 'red'; + width?: number; + height?: number; +} + +// 指数卡片 +export interface IndexCardProps { + name: string; + value: number; + change: number; + trend?: number[]; +} + +// 统计卡片(涨停/成交额) +export interface StatCardProps { + label: string; + value: string | number; + subLabel?: string; + icon?: React.ReactNode; +} + +// 概念项 +export interface ConceptItemProps { + name: string; + change: number; + trend?: number[]; + onClick?: () => void; +} + +// 日期单元格 +export interface DayCellProps { + day: number; + isTrading: boolean; + isToday: boolean; + isWeekend: boolean; + isCurrentMonth?: boolean; +} + +// 统计项(AI平台能力) +export interface StatItemProps { + icon: React.ReactNode; + value: string; + label: string; +} + +// 指数数据 +export interface IndexData { + name: string; + code: string; + value: number; + change: number; + trend: number[]; +} + +// 概念数据 +export interface ConceptData { + id: string; + name: string; + change: number; + trend: number[]; +} + +// 市场统计数据 +export interface MarketStats { + limitUp: number; + limitDown: number; + turnover: string; +} + +// 平台能力数据 +export interface PlatformStat { + icon: string; + value: string; + label: string; +} + +// 完整仪表盘数据 +export interface MarketDashboardData { + indices: IndexData[]; + marketStats: MarketStats; + hotConcepts: ConceptData[]; + platformStats: PlatformStat[]; +}