From 35f8b5195a0f2e2400bd44ac520fd038b5eeaf41 Mon Sep 17 00:00:00 2001
From: zdl <3489966805@qq.com>
Date: Wed, 29 Oct 2025 19:18:12 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=20=E8=AE=BF=E9=97=AE"=E6=A6=82?=
=?UTF-8?q?=E5=BF=B5=E4=B8=AD=E5=BF=83"=E9=A1=B5=E9=9D=A2=20=20=20=20=20?=
=?UTF-8?q?=202.=20=E7=82=B9=E5=87=BB=E4=BB=BB=E6=84=8F=E6=A6=82=E5=BF=B5?=
=?UTF-8?q?=E5=8D=A1=E7=89=87=E8=BF=9B=E5=85=A5=E6=A6=82=E5=BF=B5=E8=AF=A6?=
=?UTF-8?q?=E6=83=85=20=20=20=20=20=203.=20=E7=82=B9=E5=87=BB"=E5=8E=86?=
=?UTF-8?q?=E5=8F=B2=E6=97=B6=E9=97=B4=E8=BD=B4"=E6=8C=89=E9=92=AE?=
=?UTF-8?q?=EF=BC=88=E9=9C=80=E8=A6=81Max=E4=BC=9A=E5=91=98=E6=9D=83?=
=?UTF-8?q?=E9=99=90=EF=BC=89=20=20=20=20=20=204.=20=E6=9F=A5=E7=9C=8B?=
=?UTF-8?q?=E5=BC=B9=E7=AA=97=E5=BA=95=E9=83=A8=E6=98=AF=E5=90=A6=E6=98=BE?=
=?UTF-8?q?=E7=A4=BA=E9=A3=8E=E9=99=A9=E6=8F=90=E7=A4=BA=20&=20mock?=
=?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=A4=84=E7=90=86?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/mocks/handlers/concept.js | 4 +-
src/mocks/handlers/limitAnalyse.js | 174 ++++++++++++++++++----
src/views/Concept/ConceptTimelineModal.js | 6 +
3 files changed, 153 insertions(+), 31 deletions(-)
diff --git a/src/mocks/handlers/concept.js b/src/mocks/handlers/concept.js
index 1dd57442..52e9720e 100644
--- a/src/mocks/handlers/concept.js
+++ b/src/mocks/handlers/concept.js
@@ -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)
});
diff --git a/src/mocks/handlers/limitAnalyse.js b/src/mocks/handlers/limitAnalyse.js
index b4b8e154..a9d24384 100644
--- a/src/mocks/handlers/limitAnalyse.js
+++ b/src/mocks/handlers/limitAnalyse.js
@@ -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_data(SectorDetails 组件需要的格式)
+ 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: '高位股数据获取成功',
+ });
+ }),
];
diff --git a/src/views/Concept/ConceptTimelineModal.js b/src/views/Concept/ConceptTimelineModal.js
index 214206e4..d8ef14ef 100644
--- a/src/views/Concept/ConceptTimelineModal.js
+++ b/src/views/Concept/ConceptTimelineModal.js
@@ -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 = ({
)}
+
+ {/* 风险提示 */}
+
+
+