feat: 访问"概念中心"页面

2. 点击任意概念卡片进入概念详情
     3. 点击"历史时间轴"按钮(需要Max会员权限)
     4. 查看弹窗底部是否显示风险提示 & mock数据处理
This commit is contained in:
zdl
2025-10-29 19:18:12 +08:00
parent 77aafd5661
commit 35f8b5195a
3 changed files with 153 additions and 31 deletions

View File

@@ -259,8 +259,8 @@ export const conceptHandlers = [
// 跳过周末
if (date.getDay() !== 0 && date.getDay() !== 6) {
timeseries.push({
date: date.toISOString().split('T')[0],
avg_change_pct: (Math.random() * 8 - 2).toFixed(2),
trade_date: date.toISOString().split('T')[0], // 改为 trade_date
avg_change_pct: parseFloat((Math.random() * 8 - 2).toFixed(2)), // 转为数值
stock_count: Math.floor(Math.random() * 30) + 10,
volume: Math.floor(Math.random() * 1000000000)
});

View File

@@ -22,7 +22,13 @@ const generateAvailableDates = () => {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
dates.push(`${year}${month}${day}`);
const dateStr = `${year}${month}${day}`;
// 返回包含 date 和 count 字段的对象
dates.push({
date: dateStr,
count: Math.floor(Math.random() * 80) + 30 // 30-110 只涨停股票
});
count++;
}
}
@@ -73,37 +79,65 @@ const generateSectors = (count = 8) => {
return sectors;
};
// 生成高位股数据
// 生成高位股数据(用于 HighPositionStocks 组件)
const generateHighPositionStocks = () => {
const stocks = [];
const stockNames = [
'宁德时代', '比亚迪', '隆基绿能', '东方财富', '中际旭创',
'京东方A', '海康威视', '立讯精密', '三一重工', '恒瑞医药'
'京东方A', '海康威视', '立讯精密', '三一重工', '恒瑞医药',
'三六零', '东方通信', '贵州茅台', '五粮液', '中国平安'
];
const industries = [
'锂电池', '新能源汽车', '光伏', '金融科技', '通信设备',
'显示器件', '安防设备', '电子元件', '工程机械', '医药制造',
'网络安全', '通信服务', '白酒', '食品饮料', '保险'
];
for (let i = 0; i < stockNames.length; i++) {
const code = `${Math.random() > 0.5 ? '6' : '0'}${String(Math.floor(Math.random() * 100000)).padStart(5, '0')}`;
const continuousDays = Math.floor(Math.random() * 8) + 3;
const continuousDays = Math.floor(Math.random() * 8) + 2; // 2-9连板
const price = parseFloat((Math.random() * 100 + 20).toFixed(2));
const increaseRate = parseFloat((Math.random() * 3 + 8).toFixed(2)); // 8%-11%
const turnoverRate = parseFloat((Math.random() * 20 + 5).toFixed(2)); // 5%-25%
stocks.push({
code: code,
name: stockNames[i],
continuous_limit_days: continuousDays,
total_gain_pct: (continuousDays * 10).toFixed(2),
recent_limit_dates: Array.from({ length: Math.min(continuousDays, 5) }, (_, j) => {
const date = new Date();
date.setDate(date.getDate() - j);
return date.toISOString().split('T')[0].replace(/-/g, '');
}),
sector: ['人工智能', 'ChatGPT', '数字经济'][Math.floor(Math.random() * 3)],
current_price: (Math.random() * 100 + 20).toFixed(2),
market_cap: (Math.random() * 5000 + 500).toFixed(2) + '亿',
stock_code: code,
stock_name: stockNames[i],
price: price,
increase_rate: increaseRate,
continuous_limit_up: continuousDays,
industry: industries[i],
turnover_rate: turnoverRate,
});
}
// 按连板天数降序排序
stocks.sort((a, b) => b.continuous_limit_up - a.continuous_limit_up);
return stocks;
};
// 生成高位股统计数据
const generateHighPositionStatistics = (stocks) => {
if (!stocks || stocks.length === 0) {
return {
total_count: 0,
avg_continuous_days: 0,
max_continuous_days: 0,
};
}
const totalCount = stocks.length;
const sumDays = stocks.reduce((sum, stock) => sum + stock.continuous_limit_up, 0);
const maxDays = Math.max(...stocks.map(s => s.continuous_limit_up));
return {
total_count: totalCount,
avg_continuous_days: parseFloat((sumDays / totalCount).toFixed(1)),
max_continuous_days: maxDays,
};
};
// 生成词云数据
const generateWordCloudData = () => {
const keywords = [
@@ -124,24 +158,83 @@ const generateWordCloudData = () => {
// 生成每日分析数据
const generateDailyAnalysis = (date) => {
const sectors = generateSectors();
const totalStocks = sectors.reduce((sum, sector) => sum + sector.stock_count, 0);
const sectorNames = [
'公告', '人工智能', 'ChatGPT', '数字经济',
'新能源汽车', '光伏', '锂电池',
'半导体', '芯片', '5G通信',
'医疗器械', '创新药', '其他'
];
const stockNameTemplates = [
'龙头', '科技', '新能源', '智能', '数字', '云计算', '创新',
'生物', '医疗', '通信', '电子', '材料', '能源', '互联'
];
// 生成 sector_dataSectorDetails 组件需要的格式)
const sectorData = {};
let totalStocks = 0;
sectorNames.forEach((sectorName, sectorIdx) => {
const stockCount = Math.floor(Math.random() * 12) + 3; // 每个板块 3-15 只股票
const stocks = [];
for (let i = 0; i < stockCount; i++) {
const code = `${Math.random() > 0.5 ? '6' : '0'}${String(Math.floor(Math.random() * 100000)).padStart(5, '0')}`;
const continuousDays = Math.floor(Math.random() * 6) + 1; // 1-6连板
const ztHour = Math.floor(Math.random() * 5) + 9; // 9-13点
const ztMinute = Math.floor(Math.random() * 60);
const ztSecond = Math.floor(Math.random() * 60);
const ztTime = `2024-10-28 ${String(ztHour).padStart(2, '0')}:${String(ztMinute).padStart(2, '0')}:${String(ztSecond).padStart(2, '0')}`;
const stockName = `${stockNameTemplates[i % stockNameTemplates.length]}${sectorName === '公告' ? '公告' : ''}股份${i + 1}`;
stocks.push({
scode: code,
sname: stockName,
zt_time: ztTime,
formatted_time: `${String(ztHour).padStart(2, '0')}:${String(ztMinute).padStart(2, '0')}`,
continuous_days: continuousDays === 1 ? '首板' : `${continuousDays}连板`,
brief: `${sectorName}板块异动,${stockName}${sectorName === '公告' ? '重大公告利好' : '板块热点'}涨停。公司是${sectorName}行业龙头企业之一。`,
summary: `${sectorName}概念持续活跃`,
first_time: `2024-10-${String(28 - (continuousDays - 1)).padStart(2, '0')}`,
change_pct: parseFloat((Math.random() * 2 + 9).toFixed(2)), // 9%-11%
core_sectors: [
sectorName,
sectorNames[Math.floor(Math.random() * sectorNames.length)],
sectorNames[Math.floor(Math.random() * sectorNames.length)]
].filter((v, i, a) => a.indexOf(v) === i) // 去重
});
}
sectorData[sectorName] = {
count: stockCount,
stocks: stocks.sort((a, b) => a.zt_time.localeCompare(b.zt_time)) // 按涨停时间排序
};
totalStocks += stockCount;
});
// 统计数据
const morningCount = Math.floor(totalStocks * 0.35); // 早盘涨停
const announcementCount = sectorData['公告']?.count || 0;
const topSector = sectorNames.filter(s => s !== '公告' && s !== '其他')
.reduce((max, name) =>
(sectorData[name]?.count || 0) > (sectorData[max]?.count || 0) ? name : max
, '人工智能');
return {
date: date,
total_stocks: totalStocks,
total_sectors: sectors.length,
avg_limit_time: '10:35:24',
market_sentiment: Math.random() > 0.5 ? '强势' : '震荡',
limit_up_ratio: (Math.random() * 3 + 1).toFixed(2) + '%',
sectors: sectors,
high_position_stocks: generateHighPositionStocks(),
total_sectors: Object.keys(sectorData).length,
sector_data: sectorData, // 👈 SectorDetails 组件需要的数据
summary: {
early_limit_count: Math.floor(totalStocks * 0.3),
mid_limit_count: Math.floor(totalStocks * 0.5),
late_limit_count: Math.floor(totalStocks * 0.2),
one_word_board: Math.floor(totalStocks * 0.15),
continuous_limit: generateHighPositionStocks().length,
top_sector: topSector,
top_sector_count: sectorData[topSector]?.count || 0,
announcement_stocks: announcementCount,
zt_time_distribution: {
morning: morningCount,
afternoon: totalStocks - morningCount,
}
}
};
};
@@ -225,4 +318,27 @@ export const limitAnalyseHandlers = [
message: '搜索完成',
});
}),
// 5. 获取高位股列表(涨停股票列表)
http.get('http://111.198.58.126:5001/api/limit-analyse/high-position-stocks', async ({ request }) => {
await delay(400);
const url = new URL(request.url);
const date = url.searchParams.get('date');
console.log('[Mock LimitAnalyse] 获取高位股列表:', { date });
const stocks = generateHighPositionStocks();
const statistics = generateHighPositionStatistics(stocks);
return HttpResponse.json({
success: true,
data: {
stocks: stocks,
statistics: statistics,
date: date,
},
message: '高位股数据获取成功',
});
}),
];

View File

@@ -1,6 +1,7 @@
import React, { useState, useEffect } from 'react';
import { logger } from '../../utils/logger';
import { useConceptTimelineEvents } from './hooks/useConceptTimelineEvents';
import RiskDisclaimer from '../../components/RiskDisclaimer';
import {
Modal,
ModalOverlay,
@@ -825,6 +826,11 @@ const ConceptTimelineModal = ({
</VStack>
</Center>
)}
{/* 风险提示 */}
<Box px={6}>
<RiskDisclaimer variant="default" />
</Box>
</ModalBody>
<ModalFooter borderTop="1px solid" borderColor="gray.200">