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 = ({ )} + + {/* 风险提示 */} + + +