Files
vf_react/src/mocks/handlers/limitAnalyse.js
zdl 43e0a2aad7 feat: 访问"概念中心"页面
2. 点击任意概念卡片进入概念详情
     3. 点击"历史时间轴"按钮(需要Max会员权限)
     4. 查看弹窗底部是否显示风险提示 & mock数据处理
2025-10-29 19:18:12 +08:00

345 lines
13 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// src/mocks/handlers/limitAnalyse.js
// 涨停分析相关的 Mock Handlers
import { http, HttpResponse } from 'msw';
// 模拟延迟
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
// 生成可用日期列表最近30个交易日
const generateAvailableDates = () => {
const dates = [];
const today = new Date();
let count = 0;
for (let i = 0; i < 60 && count < 30; i++) {
const date = new Date(today);
date.setDate(date.getDate() - i);
const dayOfWeek = date.getDay();
// 跳过周末
if (dayOfWeek !== 0 && dayOfWeek !== 6) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const dateStr = `${year}${month}${day}`;
// 返回包含 date 和 count 字段的对象
dates.push({
date: dateStr,
count: Math.floor(Math.random() * 80) + 30 // 30-110 只涨停股票
});
count++;
}
}
return dates;
};
// 生成板块数据
const generateSectors = (count = 8) => {
const sectorNames = [
'人工智能', 'ChatGPT', '数字经济',
'新能源汽车', '光伏', '锂电池',
'半导体', '芯片', '5G通信',
'医疗器械', '创新药', '中药',
'白酒', '食品饮料', '消费电子',
'军工', '航空航天', '新材料'
];
const sectors = [];
for (let i = 0; i < Math.min(count, sectorNames.length); i++) {
const stockCount = Math.floor(Math.random() * 15) + 5;
const stocks = [];
for (let j = 0; j < stockCount; j++) {
stocks.push({
code: `${Math.random() > 0.5 ? '6' : '0'}${String(Math.floor(Math.random() * 100000)).padStart(5, '0')}`,
name: `${sectorNames[i]}股票${j + 1}`,
latest_limit_time: `${Math.floor(Math.random() * 4) + 9}:${String(Math.floor(Math.random() * 60)).padStart(2, '0')}:${String(Math.floor(Math.random() * 60)).padStart(2, '0')}`,
limit_up_count: Math.floor(Math.random() * 3) + 1,
price: (Math.random() * 100 + 10).toFixed(2),
change_pct: (Math.random() * 5 + 5).toFixed(2),
turnover_rate: (Math.random() * 30 + 5).toFixed(2),
volume: Math.floor(Math.random() * 100000000 + 10000000),
amount: (Math.random() * 1000000000 + 100000000).toFixed(2),
limit_type: Math.random() > 0.7 ? '一字板' : (Math.random() > 0.5 ? 'T字板' : '普通涨停'),
封单金额: (Math.random() * 500000000).toFixed(2),
});
}
sectors.push({
sector_name: sectorNames[i],
stock_count: stockCount,
avg_limit_time: `${Math.floor(Math.random() * 2) + 10}:${String(Math.floor(Math.random() * 60)).padStart(2, '0')}`,
stocks: stocks,
});
}
return sectors;
};
// 生成高位股数据(用于 HighPositionStocks 组件)
const generateHighPositionStocks = () => {
const stocks = [];
const stockNames = [
'宁德时代', '比亚迪', '隆基绿能', '东方财富', '中际旭创',
'京东方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) + 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({
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 = [
'人工智能', 'ChatGPT', 'AI芯片', '大模型', '算力',
'新能源', '光伏', '锂电池', '储能', '充电桩',
'半导体', '芯片', 'EDA', '国产替代', '集成电路',
'医疗', '创新药', 'CXO', '医疗器械', '生物医药',
'消费', '白酒', '食品', '零售', '餐饮',
'金融', '券商', '保险', '银行', '金融科技'
];
return keywords.map(keyword => ({
text: keyword,
value: Math.floor(Math.random() * 50) + 10,
category: ['科技', '新能源', '医疗', '消费', '金融'][Math.floor(Math.random() * 5)],
}));
};
// 生成每日分析数据
const generateDailyAnalysis = (date) => {
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: Object.keys(sectorData).length,
sector_data: sectorData, // 👈 SectorDetails 组件需要的数据
summary: {
top_sector: topSector,
top_sector_count: sectorData[topSector]?.count || 0,
announcement_stocks: announcementCount,
zt_time_distribution: {
morning: morningCount,
afternoon: totalStocks - morningCount,
}
}
};
};
// Mock Handlers
export const limitAnalyseHandlers = [
// 1. 获取可用日期列表
http.get('http://111.198.58.126:5001/api/v1/dates/available', async () => {
await delay(300);
const availableDates = generateAvailableDates();
return HttpResponse.json({
success: true,
events: availableDates,
message: '可用日期列表获取成功',
});
}),
// 2. 获取每日分析数据
http.get('http://111.198.58.126:5001/api/v1/analysis/daily/:date', async ({ params }) => {
await delay(500);
const { date } = params;
const data = generateDailyAnalysis(date);
return HttpResponse.json({
success: true,
data: data,
message: `${date} 每日分析数据获取成功`,
});
}),
// 3. 获取词云数据
http.get('http://111.198.58.126:5001/api/v1/analysis/wordcloud/:date', async ({ params }) => {
await delay(300);
const { date } = params;
const wordCloudData = generateWordCloudData();
return HttpResponse.json({
success: true,
data: wordCloudData,
message: `${date} 词云数据获取成功`,
});
}),
// 4. 混合搜索POST
http.post('http://111.198.58.126:5001/api/v1/stocks/search/hybrid', async ({ request }) => {
await delay(400);
const body = await request.json();
const { query, type = 'all', mode = 'hybrid' } = body;
// 生成模拟搜索结果
const results = [];
const count = Math.floor(Math.random() * 10) + 5;
for (let i = 0; i < count; i++) {
results.push({
code: `${Math.random() > 0.5 ? '6' : '0'}${String(Math.floor(Math.random() * 100000)).padStart(5, '0')}`,
name: `${query || '搜索'}相关股票${i + 1}`,
sector: ['人工智能', 'ChatGPT', '新能源'][Math.floor(Math.random() * 3)],
limit_date: new Date().toISOString().split('T')[0].replace(/-/g, ''),
limit_time: `${Math.floor(Math.random() * 4) + 9}:${String(Math.floor(Math.random() * 60)).padStart(2, '0')}`,
price: (Math.random() * 100 + 10).toFixed(2),
change_pct: (Math.random() * 10).toFixed(2),
match_score: (Math.random() * 0.5 + 0.5).toFixed(2),
});
}
return HttpResponse.json({
success: true,
data: {
query: query,
type: type,
mode: mode,
results: results,
total: results.length,
},
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: '高位股数据获取成功',
});
}),
];