You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
13 KiB
13 KiB
A股智投分析平台 - 前端实现文档
一、组件清单
1.1 公共组件
| 组件名 | 文件路径 | 功能描述 | 复杂度 |
|---|---|---|---|
| Navbar | components/Navbar.tsx |
导航栏,含搜索功能 | 高 |
| CandlestickChart | components/CandlestickChart.tsx |
K线蜡烛图+均线+成交量 | 高 |
| StockDetailModal | components/StockDetailModal.tsx |
个股详情弹窗 | 高 |
| SectorDetailModal | components/SectorDetailModal.tsx |
版块详情弹窗 | 高 |
| Footer | components/Footer.tsx |
页脚 | 低 |
1.2 页面区块组件
| 组件名 | 文件路径 | 功能描述 | 复杂度 |
|---|---|---|---|
| MarketOverview | sections/MarketOverview.tsx |
市场概览 | 中 |
| MomentumSectors | sections/MomentumSectors.tsx |
动量版块分析 | 高 |
| HighLowStocks | sections/HighLowStocks.tsx |
新高新低个股 | 中 |
| PriceDistribution | sections/PriceDistribution.tsx |
涨跌幅分布 | 中 |
| MomentumRecommendation | sections/MomentumRecommendation.tsx |
动量股推荐 | 中 |
二、核心组件详解
2.1 CandlestickChart 组件
功能: K线蜡烛图,支持均线和成交量
Props
interface CandlestickChartProps {
data: KLineData[]; // K线数据
height?: number; // 图表高度,默认400
showVolume?: boolean; // 是否显示成交量,默认true
showMaSettings?: boolean; // 是否显示均线设置,默认true
}
实现要点
- 使用 Recharts ComposedChart 组合图表
- 自定义蜡烛图形状(红涨绿跌)
- 支持5条均线(MA5/MA10/MA20/MA30/MA60)
- 成交量附图,颜色与K线对应
- 点击均线标签切换显示/隐藏
代码片段
// 均线配置
const defaultMaPeriods: MaPeriod[] = [
{ key: 'ma5', label: 'MA5', days: 5, color: '#ff9f43', visible: true },
{ key: 'ma10', label: 'MA10', days: 10, color: '#3498db', visible: true },
{ key: 'ma20', label: 'MA20', days: 20, color: '#9b59b6', visible: true },
{ key: 'ma30', label: 'MA30', days: 30, color: '#e74c3c', visible: false },
{ key: 'ma60', label: 'MA60', days: 60, color: '#2ecc71', visible: false },
];
// 渲染蜡烛图
const renderCandle = (props, maxPrice, minPrice, pricePadding) => {
const { x, y, width, height, payload } = props;
const { open, close } = payload;
const isUp = close >= open;
const color = isUp ? '#ff3b30' : '#00c853';
// 计算影线坐标
// 计算实体坐标
// 返回SVG元素
};
2.2 StockDetailModal 组件
功能: 个股详情弹窗
Props
interface StockDetailModalProps {
stockCode: string | null; // 股票代码
isOpen: boolean; // 是否打开
onClose: () => void; // 关闭回调
}
实现要点
- 使用 Framer Motion 实现动画
- 获取个股详情和K线数据
- 显示基本信息、K线图、技术指标、基本面
- 支持日线/周线/月线切换
代码片段
const [stock, setStock] = useState<StockDetail | null>(null);
const [klineData, setKlineData] = useState<KLineData[]>([]);
const [timeRange, setTimeRange] = useState<'day' | 'week' | 'month'>('day');
useEffect(() => {
if (stockCode && isOpen) {
setStock(stockDataService.getStockDetail(stockCode));
setKlineData(stockDataService.getKLineData(stockCode, 60));
}
}, [stockCode, isOpen]);
2.3 SectorDetailModal 组件
功能: 版块详情弹窗
Props
interface SectorDetailModalProps {
sector: Sector | null; // 版块数据
isOpen: boolean; // 是否打开
onClose: () => void; // 关闭回调
onStockClick?: (code: string) => void; // 股票点击回调
}
实现要点
- 三个标签页:历史排名、动量个股、K线走势
- 历史排名使用组合图表(排名线+动量分柱状图)
- 动量个股列表支持点击打开个股详情
- K线使用 CandlestickChart 组件
2.4 Navbar 组件
功能: 导航栏,含搜索功能
Props
interface NavbarProps {
onSectorClick?: (sector: Sector) => void; // 版块点击回调
onStockClick?: (code: string) => void; // 个股点击回调
}
实现要点
- 滚动时添加背景和边框
- 搜索框展开/收起动画
- 实时搜索,分类展示结果
- 点击结果打开对应详情
代码片段
const [searchOpen, setSearchOpen] = useState(false);
const [searchKeyword, setSearchKeyword] = useState('');
const [searchResults, setSearchResults] = useState({ sectors: [], stocks: [] });
// 搜索逻辑
useEffect(() => {
if (searchKeyword.trim().length >= 1) {
const sectors = stockDataService.searchSectors(searchKeyword);
const stocks = stockDataService.searchStocks(searchKeyword);
setSearchResults({ sectors, stocks });
}
}, [searchKeyword]);
三、数据服务
3.1 StockDataService
文件: services/stockData.ts
主要方法
| 方法名 | 功能 | 返回值 |
|---|---|---|
getMarketIndices() |
获取市场指数 | MarketIndex[] |
getUpDownStats() |
获取涨跌家数 | {up, down, flat} |
getSectorsWithMomentum() |
获取版块列表(带动量) | Sector[] |
getSectorRankHistory(name) |
获取版块历史排名 | SectorMomentumHistory[] |
getSectorStocks(name) |
获取版块内股票 | Stock[] |
getSectorMomentumStocks(name) |
获取版块内动量股票 | MomentumStock[] |
getSectorKLineData(name, days) |
获取版块K线 | KLineData[] |
getNewHighStocks() |
获取创新高股票 | HighLowStock[] |
getNewLowStocks() |
获取创新低股票 | HighLowStock[] |
getPriceDistribution() |
获取涨跌幅分布 | PriceDistribution[] |
getMomentumStocks() |
获取动量股推荐 | MomentumStock[] |
getStockDetail(code) |
获取个股详情 | StockDetail |
getKLineData(code, days) |
获取个股K线 | KLineData[] |
searchSectors(keyword) |
搜索版块 | Sector[] |
searchStocks(keyword) |
搜索股票 | Stock[] |
3.2 均线计算
代码片段
private calculateMA(data: KLineData[]): KLineData[] {
const periods = [5, 10, 20, 30, 60];
return data.map((item, index) => {
const ma: Record<string, number> = {};
for (const period of periods) {
if (index >= period - 1) {
const sum = data
.slice(index - period + 1, index + 1)
.reduce((acc, d) => acc + d.close, 0);
ma[`ma${period}`] = this.formatNumber(sum / period);
}
}
return { ...item, ...ma };
});
}
四、类型定义
4.1 核心类型
文件: types/index.ts
// 股票基础信息
export interface Stock {
code: string; // 股票代码
name: string; // 股票名称
price: number; // 当前价格
change: number; // 涨跌额
changePercent: number; // 涨跌幅
volume: number; // 成交量
turnover: number; // 成交额
marketCap?: number; // 总市值
pe?: number; // 市盈率
pb?: number; // 市净率
industry?: string; // 所属行业
}
// 版块信息
export interface Sector {
name: string; // 版块名称
code: string; // 版块代码
change: number; // 涨跌额
changePercent: number; // 涨跌幅
volume: number; // 成交量
turnover: number; // 成交额
leadingStock?: string; // 领涨股
momentumScore?: number; // 动量分数
rank?: number; // 当前排名
previousRank?: number; // 昨日排名
rankChange?: number; // 排名变化
}
// K线数据
export interface KLineData {
date: string; // 日期
open: number; // 开盘价
high: number; // 最高价
low: number; // 最低价
close: number; // 收盘价
volume: number; // 成交量
ma5?: number; // 5日均线
ma10?: number; // 10日均线
ma20?: number; // 20日均线
ma30?: number; // 30日均线
ma60?: number; // 60日均线
}
// 均线周期配置
export interface MaPeriod {
key: string; // 标识
label: string; // 显示名称
days: number; // 周期天数
color: string; // 颜色
visible: boolean; // 是否显示
}
五、动画实现
5.1 页面加载动画
const containerVariants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.1,
delayChildren: 0.2
}
}
};
const itemVariants = {
hidden: { opacity: 0, y: 30 },
visible: {
opacity: 1,
y: 0,
transition: {
duration: 0.6,
ease: [0.165, 0.84, 0.44, 1] as const
}
}
};
5.2 数字动画
function AnimatedNumber({ value, decimals = 2 }: { value: number; decimals?: number }) {
const [displayValue, setDisplayValue] = useState(0);
const prevValue = useRef(value);
useEffect(() => {
const start = prevValue.current;
const end = value;
const duration = 800;
const startTime = performance.now();
const animate = (currentTime: number) => {
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);
const easeOut = 1 - Math.pow(1 - progress, 4);
const current = start + (end - start) * easeOut;
setDisplayValue(current);
if (progress < 1) {
requestAnimationFrame(animate);
} else {
prevValue.current = value;
}
};
requestAnimationFrame(animate);
}, [value]);
return <span className="number-font">{displayValue.toFixed(decimals)}</span>;
}
5.3 弹窗动画
<motion.div
initial={{ opacity: 0, scale: 0.9, y: 20 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.9, y: 20 }}
transition={{ duration: 0.3, ease: [0.165, 0.84, 0.44, 1] }}
>
{/* 弹窗内容 */}
</motion.div>
六、样式规范
6.1 颜色系统
/* 主色调 */
--background: #0a0a0a; /* 背景色 */
--card: #1a1a1a; /* 卡片背景 */
--border: #2a2a2a; /* 边框色 */
--accent: #ff6b35; /* 强调色(橙色) */
/* 文字色 */
--text-primary: #ffffff; /* 主文字 */
--text-secondary: #b0b0b0; /* 次要文字 */
/* 功能色 */
--up: #ff3b30; /* 上涨(红) */
--down: #00c853; /* 下跌(绿) */
/* 均线色 */
--ma5: #ff9f43; /* MA5 - 橙色 */
--ma10: #3498db; /* MA10 - 蓝色 */
--ma20: #9b59b6; /* MA20 - 紫色 */
--ma30: #e74c3c; /* MA30 - 红色 */
--ma60: #2ecc71; /* MA60 - 绿色 */
6.2 字体规范
/* 字体家族 */
font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
/* 数字字体 */
.number-font {
font-family: 'JetBrains Mono', monospace;
font-variant-numeric: tabular-nums;
}
6.3 间距规范
/* 区块间距 */
--section-gap: 3rem; /* 48px */
--card-gap: 1rem; /* 16px */
--card-padding: 1.5rem; /* 24px */
/* 圆角 */
--radius-sm: 8px;
--radius-md: 12px;
--radius-lg: 16px;
七、性能优化
7.1 已实现的优化
| 优化项 | 实现方式 |
|---|---|
| 组件懒加载 | 使用动态导入 |
| 数据缓存 | useMemo 缓存计算结果 |
| 动画优化 | 使用 transform 和 opacity |
| 虚拟列表 | 大量数据时使用 |
7.2 待优化项
- 图片懒加载
- Service Worker 缓存
- 代码分割优化
- Tree Shaking
八、测试策略
8.1 单元测试
// 示例: CandlestickChart 测试
describe('CandlestickChart', () => {
it('should render candlestick chart', () => {
const data = [
{ date: '2024-01-15', open: 50, high: 55, low: 48, close: 52, volume: 1000000 }
];
render(<CandlestickChart data={data} />);
expect(screen.getByRole('img')).toBeInTheDocument();
});
});
8.2 E2E测试
// 示例: 搜索功能测试
describe('Search', () => {
it('should search and display results', () => {
cy.visit('/');
cy.get('[data-testid="search-button"]').click();
cy.get('[data-testid="search-input"]').type('茅台');
cy.get('[data-testid="search-result"]').should('contain', '贵州茅台');
});
});
九、待实现功能
9.1 前端待实现
- 用户登录/注册页面
- 自选股管理页面
- 预警设置页面
- 主题切换(深色/浅色)
- 多语言支持
9.2 与后端对接
- 接入真实API
- WebSocket实时数据
- 用户认证
- 数据持久化