feat: 添加mock数据
This commit is contained in:
@@ -545,3 +545,236 @@ export function getEventRelatedStocks(eventId) {
|
|||||||
const count = 3 + (parseInt(eventId) % 4);
|
const count = 3 + (parseInt(eventId) % 4);
|
||||||
return generateRelatedStocks(eventId, count);
|
return generateRelatedStocks(eventId, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================== Mock 事件列表数据 ====================
|
||||||
|
|
||||||
|
// 事件类型池
|
||||||
|
const eventTypes = ['政策发布', '行业动向', '公司公告', '市场研判', '技术突破', '财报发布', '投融资', '高管变动'];
|
||||||
|
|
||||||
|
// 行业池
|
||||||
|
const industries = ['半导体', '新能源', '人工智能', '医药', '消费', '金融', '房地产', '通信', '互联网', '军工', '化工', '机械'];
|
||||||
|
|
||||||
|
// 事件标题模板
|
||||||
|
const eventTitleTemplates = [
|
||||||
|
'{industry}行业迎来重大政策利好',
|
||||||
|
'{company}发布{quarter}财报,业绩超预期',
|
||||||
|
'{industry}板块集体大涨,{company}涨停',
|
||||||
|
'央行宣布{policy},影响{industry}行业',
|
||||||
|
'{company}与{partner}达成战略合作',
|
||||||
|
'{industry}技术取得重大突破',
|
||||||
|
'{company}拟投资{amount}亿元布局{industry}',
|
||||||
|
'国家发改委:支持{industry}产业发展',
|
||||||
|
'{industry}龙头{company}涨价{percent}%',
|
||||||
|
'{company}回购股份,彰显信心',
|
||||||
|
];
|
||||||
|
|
||||||
|
// 生成随机公司名
|
||||||
|
function generateCompanyName(industry) {
|
||||||
|
const prefixes = ['华为', '中兴', '阿里', '腾讯', '比亚迪', '宁德时代', '隆基', '恒瑞', '茅台', '五粮液', '海康', '中芯'];
|
||||||
|
const suffixes = ['科技', '集团', '股份', '控股', '实业', ''];
|
||||||
|
const prefix = prefixes[Math.floor(Math.random() * prefixes.length)];
|
||||||
|
const suffix = suffixes[Math.floor(Math.random() * suffixes.length)];
|
||||||
|
return `${prefix}${suffix}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成事件标题
|
||||||
|
function generateEventTitle(industry, seed) {
|
||||||
|
const template = eventTitleTemplates[seed % eventTitleTemplates.length];
|
||||||
|
return template
|
||||||
|
.replace('{industry}', industry)
|
||||||
|
.replace('{company}', generateCompanyName(industry))
|
||||||
|
.replace('{partner}', generateCompanyName(industry))
|
||||||
|
.replace('{quarter}', ['一季度', '半年度', '三季度', '年度'][seed % 4])
|
||||||
|
.replace('{policy}', ['降准0.5%', '降息25BP', 'MLF下调', '提高赤字率'][seed % 4])
|
||||||
|
.replace('{amount}', [50, 100, 200, 500][seed % 4])
|
||||||
|
.replace('{percent}', [5, 10, 15, 20][seed % 4]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成事件描述
|
||||||
|
function generateEventDescription(industry, importance, seed) {
|
||||||
|
const impacts = {
|
||||||
|
S: '重大利好,预计将对行业格局产生深远影响,相关概念股有望持续受益。机构预计该事件将带动行业整体估值提升15-20%,龙头企业市值增长空间广阔。',
|
||||||
|
A: '重要利好,市场情绪积极,短期内资金流入明显。分析师普遍认为该事件将推动行业景气度上行,相关公司业绩有望超预期增长。',
|
||||||
|
B: '中性偏好,对部分细分领域有一定促进作用。虽然不是行业性机会,但优质标的仍有结构性行情,建议关注业绩确定性强的公司。',
|
||||||
|
C: '影响有限,市场反应平淡,但长期来看仍有积极意义。事件对行业发展方向有指引作用,关注后续政策跟进和落地情况。',
|
||||||
|
};
|
||||||
|
|
||||||
|
const details = [
|
||||||
|
`根据最新消息,${industry}领域将获得新一轮政策支持,产业链相关企业订单饱满。`,
|
||||||
|
`${industry}板块近期表现活跃,多只个股创出年内新高,资金持续流入。`,
|
||||||
|
`行业专家指出,${industry}产业正处于高速发展期,市场空间广阔,龙头企业优势明显。`,
|
||||||
|
`券商研报显示,${industry}行业估值处于历史低位,当前具备较高配置价值。`,
|
||||||
|
];
|
||||||
|
|
||||||
|
return impacts[importance] + details[seed % details.length];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成关键词
|
||||||
|
function generateKeywords(industry, seed) {
|
||||||
|
const commonKeywords = ['政策', '利好', '业绩', '涨停', '龙头', '突破', '合作', '投资'];
|
||||||
|
const industryKeywords = {
|
||||||
|
'半导体': ['芯片', '晶圆', '封测', 'AI芯片', '国产替代'],
|
||||||
|
'新能源': ['电池', '光伏', '储能', '新能源车', '锂电'],
|
||||||
|
'人工智能': ['大模型', 'AI应用', '算力', '数据', '机器学习'],
|
||||||
|
'医药': ['创新药', 'CRO', '医疗器械', '生物制药', '仿制药'],
|
||||||
|
'消费': ['白酒', '食品', '家电', '零售', '免税'],
|
||||||
|
};
|
||||||
|
|
||||||
|
const keywords = [
|
||||||
|
...commonKeywords.slice(seed % 3, seed % 3 + 3),
|
||||||
|
...(industryKeywords[industry] || []).slice(0, 2)
|
||||||
|
];
|
||||||
|
|
||||||
|
return keywords.slice(0, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成 Mock 事件列表
|
||||||
|
* @param {Object} params - 查询参数
|
||||||
|
* @returns {Object} - {events: [], pagination: {}}
|
||||||
|
*/
|
||||||
|
export function generateMockEvents(params = {}) {
|
||||||
|
const {
|
||||||
|
page = 1,
|
||||||
|
per_page = 10,
|
||||||
|
sort = 'new',
|
||||||
|
importance = 'all',
|
||||||
|
date_range = '',
|
||||||
|
q = '',
|
||||||
|
industry_code = '',
|
||||||
|
} = params;
|
||||||
|
|
||||||
|
// 生成100个事件用于测试
|
||||||
|
const totalEvents = 100;
|
||||||
|
const allEvents = [];
|
||||||
|
|
||||||
|
const importanceLevels = ['S', 'A', 'B', 'C'];
|
||||||
|
const baseDate = new Date('2025-01-15');
|
||||||
|
|
||||||
|
for (let i = 0; i < totalEvents; i++) {
|
||||||
|
const industry = industries[i % industries.length];
|
||||||
|
const imp = importanceLevels[i % importanceLevels.length];
|
||||||
|
const eventType = eventTypes[i % eventTypes.length];
|
||||||
|
|
||||||
|
// 生成随机日期(最近30天内)
|
||||||
|
const createdAt = new Date(baseDate);
|
||||||
|
createdAt.setDate(createdAt.getDate() - (i % 30));
|
||||||
|
|
||||||
|
// 生成随机热度和收益率
|
||||||
|
const hotScore = Math.max(50, 100 - i);
|
||||||
|
const relatedAvgChg = (Math.random() * 20 - 5).toFixed(2); // -5% 到 15%
|
||||||
|
const relatedMaxChg = (Math.random() * 30).toFixed(2); // 0% 到 30%
|
||||||
|
|
||||||
|
allEvents.push({
|
||||||
|
id: i + 1,
|
||||||
|
title: generateEventTitle(industry, i),
|
||||||
|
description: generateEventDescription(industry, imp, i),
|
||||||
|
content: generateEventDescription(industry, imp, i),
|
||||||
|
event_type: eventType,
|
||||||
|
importance: imp,
|
||||||
|
status: 'published',
|
||||||
|
created_at: createdAt.toISOString(),
|
||||||
|
updated_at: createdAt.toISOString(),
|
||||||
|
hot_score: hotScore,
|
||||||
|
view_count: Math.floor(Math.random() * 10000),
|
||||||
|
related_avg_chg: parseFloat(relatedAvgChg),
|
||||||
|
related_max_chg: parseFloat(relatedMaxChg),
|
||||||
|
keywords: generateKeywords(industry, i),
|
||||||
|
is_ai_generated: i % 4 === 0, // 25% 的事件是AI生成
|
||||||
|
industry: industry,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 筛选
|
||||||
|
let filteredEvents = allEvents;
|
||||||
|
|
||||||
|
// 重要性筛选
|
||||||
|
if (importance && importance !== 'all') {
|
||||||
|
filteredEvents = filteredEvents.filter(e => e.importance === importance);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关键词搜索
|
||||||
|
if (q) {
|
||||||
|
const query = q.toLowerCase();
|
||||||
|
filteredEvents = filteredEvents.filter(e =>
|
||||||
|
e.title.toLowerCase().includes(query) ||
|
||||||
|
e.description.toLowerCase().includes(query) ||
|
||||||
|
e.keywords.some(k => k.toLowerCase().includes(query))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 行业筛选
|
||||||
|
if (industry_code) {
|
||||||
|
filteredEvents = filteredEvents.filter(e =>
|
||||||
|
e.industry.includes(industry_code) || e.keywords.includes(industry_code)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 日期范围筛选
|
||||||
|
if (date_range) {
|
||||||
|
const [startStr, endStr] = date_range.split(' 至 ');
|
||||||
|
if (startStr && endStr) {
|
||||||
|
const start = new Date(startStr);
|
||||||
|
const end = new Date(endStr);
|
||||||
|
filteredEvents = filteredEvents.filter(e => {
|
||||||
|
const eventDate = new Date(e.created_at);
|
||||||
|
return eventDate >= start && eventDate <= end;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 排序
|
||||||
|
if (sort === 'hot') {
|
||||||
|
filteredEvents.sort((a, b) => b.hot_score - a.hot_score);
|
||||||
|
} else if (sort === 'returns') {
|
||||||
|
filteredEvents.sort((a, b) => b.related_avg_chg - a.related_avg_chg);
|
||||||
|
} else {
|
||||||
|
// 默认按时间排序 (new)
|
||||||
|
filteredEvents.sort((a, b) => new Date(b.created_at) - new Date(a.created_at));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页
|
||||||
|
const start = (page - 1) * per_page;
|
||||||
|
const end = start + per_page;
|
||||||
|
const paginatedEvents = filteredEvents.slice(start, end);
|
||||||
|
|
||||||
|
return {
|
||||||
|
events: paginatedEvents,
|
||||||
|
pagination: {
|
||||||
|
page: page,
|
||||||
|
per_page: per_page,
|
||||||
|
total: filteredEvents.length,
|
||||||
|
total_pages: Math.ceil(filteredEvents.length / per_page),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成热点事件
|
||||||
|
* @param {number} limit - 返回数量
|
||||||
|
* @returns {Array} - 热点事件列表
|
||||||
|
*/
|
||||||
|
export function generateHotEvents(limit = 5) {
|
||||||
|
const { events } = generateMockEvents({ sort: 'hot', per_page: limit });
|
||||||
|
return events;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成热门关键词
|
||||||
|
* @param {number} limit - 返回数量
|
||||||
|
* @returns {Array} - 热门关键词列表
|
||||||
|
*/
|
||||||
|
export function generatePopularKeywords(limit = 20) {
|
||||||
|
const allKeywords = [
|
||||||
|
'人工智能', '芯片', '新能源', '锂电池', '光伏', '储能',
|
||||||
|
'消费', '白酒', '医药', 'CRO', '半导体', '国产替代',
|
||||||
|
'军工', '航空', '5G', '通信', '互联网', '云计算',
|
||||||
|
'大数据', '区块链', '元宇宙', '新基建', '数字经济',
|
||||||
|
];
|
||||||
|
|
||||||
|
return allKeywords.slice(0, limit).map((keyword, index) => ({
|
||||||
|
keyword,
|
||||||
|
count: Math.max(10, 100 - index * 3),
|
||||||
|
trend: index % 3 === 0 ? 'up' : index % 3 === 1 ? 'down' : 'stable',
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,13 +2,116 @@
|
|||||||
// 事件相关的 Mock API Handlers
|
// 事件相关的 Mock API Handlers
|
||||||
|
|
||||||
import { http, HttpResponse } from 'msw';
|
import { http, HttpResponse } from 'msw';
|
||||||
import { getEventRelatedStocks } from '../data/events';
|
import { getEventRelatedStocks, generateMockEvents, generateHotEvents, generatePopularKeywords } from '../data/events';
|
||||||
import { getMockFutureEvents, getMockEventCountsForMonth } from '../data/account';
|
import { getMockFutureEvents, getMockEventCountsForMonth } from '../data/account';
|
||||||
|
|
||||||
// 模拟网络延迟
|
// 模拟网络延迟
|
||||||
const delay = (ms = 300) => new Promise(resolve => setTimeout(resolve, ms));
|
const delay = (ms = 300) => new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
|
||||||
export const eventHandlers = [
|
export const eventHandlers = [
|
||||||
|
// ==================== 事件列表相关 ====================
|
||||||
|
|
||||||
|
// 获取事件列表
|
||||||
|
http.get('/api/events/', async ({ request }) => {
|
||||||
|
await delay(500);
|
||||||
|
|
||||||
|
const url = new URL(request.url);
|
||||||
|
const params = {
|
||||||
|
page: parseInt(url.searchParams.get('page') || '1'),
|
||||||
|
per_page: parseInt(url.searchParams.get('per_page') || '10'),
|
||||||
|
sort: url.searchParams.get('sort') || 'new',
|
||||||
|
importance: url.searchParams.get('importance') || 'all',
|
||||||
|
date_range: url.searchParams.get('date_range') || '',
|
||||||
|
q: url.searchParams.get('q') || '',
|
||||||
|
industry_code: url.searchParams.get('industry_code') || '',
|
||||||
|
industry_classification: url.searchParams.get('industry_classification') || '',
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('[Mock] 获取事件列表:', params);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = generateMockEvents(params);
|
||||||
|
|
||||||
|
return HttpResponse.json({
|
||||||
|
success: true,
|
||||||
|
data: result,
|
||||||
|
message: '获取成功'
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[Mock] 获取事件列表失败:', error);
|
||||||
|
return HttpResponse.json(
|
||||||
|
{
|
||||||
|
success: false,
|
||||||
|
error: '获取事件列表失败',
|
||||||
|
data: { events: [], pagination: {} }
|
||||||
|
},
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
// 获取热点事件
|
||||||
|
http.get('/api/events/hot', async ({ request }) => {
|
||||||
|
await delay(300);
|
||||||
|
|
||||||
|
const url = new URL(request.url);
|
||||||
|
const limit = parseInt(url.searchParams.get('limit') || '5');
|
||||||
|
|
||||||
|
console.log('[Mock] 获取热点事件, limit:', limit);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const hotEvents = generateHotEvents(limit);
|
||||||
|
|
||||||
|
return HttpResponse.json({
|
||||||
|
success: true,
|
||||||
|
data: hotEvents,
|
||||||
|
message: '获取成功'
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[Mock] 获取热点事件失败:', error);
|
||||||
|
return HttpResponse.json(
|
||||||
|
{
|
||||||
|
success: false,
|
||||||
|
error: '获取热点事件失败',
|
||||||
|
data: []
|
||||||
|
},
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
// 获取热门关键词
|
||||||
|
http.get('/api/events/keywords/popular', async ({ request }) => {
|
||||||
|
await delay(300);
|
||||||
|
|
||||||
|
const url = new URL(request.url);
|
||||||
|
const limit = parseInt(url.searchParams.get('limit') || '20');
|
||||||
|
|
||||||
|
console.log('[Mock] 获取热门关键词, limit:', limit);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const keywords = generatePopularKeywords(limit);
|
||||||
|
|
||||||
|
return HttpResponse.json({
|
||||||
|
success: true,
|
||||||
|
data: keywords,
|
||||||
|
message: '获取成功'
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[Mock] 获取热门关键词失败:', error);
|
||||||
|
return HttpResponse.json(
|
||||||
|
{
|
||||||
|
success: false,
|
||||||
|
error: '获取热门关键词失败',
|
||||||
|
data: []
|
||||||
|
},
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
// ==================== 事件详情相关 ====================
|
||||||
|
|
||||||
// 获取事件相关股票
|
// 获取事件相关股票
|
||||||
http.get('/api/events/:eventId/stocks', async ({ params }) => {
|
http.get('/api/events/:eventId/stocks', async ({ params }) => {
|
||||||
await delay(300);
|
await delay(300);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// 行业分类相关的 Mock API Handlers
|
// 行业分类相关的 Mock API Handlers
|
||||||
|
|
||||||
import { http, HttpResponse } from 'msw';
|
import { http, HttpResponse } from 'msw';
|
||||||
import { industryTreeData } from '../data/industries';
|
import { industryData } from '../../data/industryData';
|
||||||
|
|
||||||
// 模拟网络延迟
|
// 模拟网络延迟
|
||||||
const delay = (ms = 300) => new Promise(resolve => setTimeout(resolve, ms));
|
const delay = (ms = 300) => new Promise(resolve => setTimeout(resolve, ms));
|
||||||
@@ -15,14 +15,14 @@ export const industryHandlers = [
|
|||||||
const url = new URL(request.url);
|
const url = new URL(request.url);
|
||||||
const classification = url.searchParams.get('classification');
|
const classification = url.searchParams.get('classification');
|
||||||
|
|
||||||
console.log('[Mock] 获取行业分类树形数据', { classification });
|
console.log('[Mock] 获取行业分类树形数据(真实数据)', { classification });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let data = industryTreeData;
|
let data = industryData;
|
||||||
|
|
||||||
// 如果指定了分类体系,只返回该体系的数据
|
// 如果指定了分类体系,只返回该体系的数据
|
||||||
if (classification) {
|
if (classification) {
|
||||||
data = industryTreeData.filter(item => item.value === classification);
|
data = industryData.filter(item => item.value === classification);
|
||||||
}
|
}
|
||||||
|
|
||||||
return HttpResponse.json({
|
return HttpResponse.json({
|
||||||
|
|||||||
Reference in New Issue
Block a user