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.

483 lines
13 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 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**
```typescript
interface CandlestickChartProps {
data: KLineData[]; // K线数据
height?: number; // 图表高度默认400
showVolume?: boolean; // 是否显示成交量默认true
showMaSettings?: boolean; // 是否显示均线设置默认true
}
```
**实现要点**
- 使用 Recharts ComposedChart 组合图表
- 自定义蜡烛图形状(红涨绿跌)
- 支持5条均线MA5/MA10/MA20/MA30/MA60
- 成交量附图颜色与K线对应
- 点击均线标签切换显示/隐藏
**代码片段**
```typescript
// 均线配置
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**
```typescript
interface StockDetailModalProps {
stockCode: string | null; // 股票代码
isOpen: boolean; // 是否打开
onClose: () => void; // 关闭回调
}
```
**实现要点**
- 使用 Framer Motion 实现动画
- 获取个股详情和K线数据
- 显示基本信息、K线图、技术指标、基本面
- 支持日线/周线/月线切换
**代码片段**
```typescript
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**
```typescript
interface SectorDetailModalProps {
sector: Sector | null; // 版块数据
isOpen: boolean; // 是否打开
onClose: () => void; // 关闭回调
onStockClick?: (code: string) => void; // 股票点击回调
}
```
**实现要点**
- 三个标签页历史排名、动量个股、K线走势
- 历史排名使用组合图表(排名线+动量分柱状图)
- 动量个股列表支持点击打开个股详情
- K线使用 CandlestickChart 组件
### 2.4 Navbar 组件
**功能**: 导航栏,含搜索功能
**Props**
```typescript
interface NavbarProps {
onSectorClick?: (sector: Sector) => void; // 版块点击回调
onStockClick?: (code: string) => void; // 个股点击回调
}
```
**实现要点**
- 滚动时添加背景和边框
- 搜索框展开/收起动画
- 实时搜索,分类展示结果
- 点击结果打开对应详情
**代码片段**
```typescript
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 均线计算
**代码片段**
```typescript
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`
```typescript
// 股票基础信息
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 页面加载动画
```typescript
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 数字动画
```typescript
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 弹窗动画
```typescript
<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 颜色系统
```css
/* 主色调 */
--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 字体规范
```css
/* 字体家族 */
font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
/* 数字字体 */
.number-font {
font-family: 'JetBrains Mono', monospace;
font-variant-numeric: tabular-nums;
}
```
### 6.3 间距规范
```css
/* 区块间距 */
--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 单元测试
```typescript
// 示例: 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测试
```typescript
// 示例: 搜索功能测试
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实时数据
- [ ] 用户认证
- [ ] 数据持久化