|
|
|
|
|
# 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实时数据
|
|
|
|
|
|
- [ ] 用户认证
|
|
|
|
|
|
- [ ] 数据持久化
|