feat(mock): 完善 Mock 数据,修复 API 返回格式
- event.js: 修复 /api/events 返回格式,匹配 useEventData 期望的结构 - stock.js: 添加 /api/stock/:code/quote-detail handler(完整行情数据含买卖盘) - stock.js: 添加 /api/flex-screen/quotes handler(指数行情) - stock.js: 修复 /api/index/:code/kline 支持 minute 类型 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -120,12 +120,14 @@ export const eventHandlers = [
|
||||
try {
|
||||
const result = generateMockEvents(params);
|
||||
|
||||
// 返回格式兼容 NewsPanel 期望的结构
|
||||
// NewsPanel 期望: { success, data: [], pagination: {} }
|
||||
// 返回格式兼容 useEventData 期望的结构
|
||||
// useEventData 期望: { success, data: { events: [], pagination: {} } }
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
data: result.events, // 事件数组
|
||||
pagination: result.pagination, // 分页信息
|
||||
data: {
|
||||
events: result.events, // 事件数组
|
||||
pagination: result.pagination // 分页信息
|
||||
},
|
||||
message: '获取成功'
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
@@ -263,15 +263,15 @@ export const stockHandlers = [
|
||||
try {
|
||||
let data;
|
||||
|
||||
if (type === 'timeline') {
|
||||
if (type === 'timeline' || type === 'minute') {
|
||||
// timeline 和 minute 都使用分时数据
|
||||
data = generateTimelineData(indexCode);
|
||||
} else if (type === 'daily') {
|
||||
data = generateDailyData(indexCode, 30);
|
||||
} else {
|
||||
return HttpResponse.json(
|
||||
{ error: '不支持的类型' },
|
||||
{ status: 400 }
|
||||
);
|
||||
// 其他类型也降级使用 timeline 数据
|
||||
console.log('[Mock Stock] 未知类型,降级使用 timeline:', type);
|
||||
data = generateTimelineData(indexCode);
|
||||
}
|
||||
|
||||
return HttpResponse.json({
|
||||
@@ -558,4 +558,133 @@ export const stockHandlers = [
|
||||
);
|
||||
}
|
||||
}),
|
||||
|
||||
// 获取股票详细行情(quote-detail)
|
||||
http.get('/api/stock/:stockCode/quote-detail', async ({ params }) => {
|
||||
await delay(200);
|
||||
|
||||
const { stockCode } = params;
|
||||
console.log('[Mock Stock] 获取股票详细行情:', { stockCode });
|
||||
|
||||
const stocks = generateStockList();
|
||||
const codeWithoutSuffix = stockCode.replace(/\.(SH|SZ)$/i, '');
|
||||
const stockInfo = stocks.find(s => s.code === codeWithoutSuffix);
|
||||
const stockName = stockInfo?.name || `股票${stockCode}`;
|
||||
|
||||
// 生成基础价格(10-200之间)
|
||||
const basePrice = parseFloat((Math.random() * 190 + 10).toFixed(2));
|
||||
// 涨跌幅(-10% 到 +10%)
|
||||
const changePercent = parseFloat((Math.random() * 20 - 10).toFixed(2));
|
||||
// 涨跌额
|
||||
const change = parseFloat((basePrice * changePercent / 100).toFixed(2));
|
||||
// 昨收
|
||||
const prevClose = parseFloat((basePrice - change).toFixed(2));
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
data: {
|
||||
stock_code: stockCode,
|
||||
stock_name: stockName,
|
||||
price: basePrice,
|
||||
change: change,
|
||||
change_percent: changePercent,
|
||||
prev_close: prevClose,
|
||||
open: parseFloat((prevClose * (1 + (Math.random() * 0.02 - 0.01))).toFixed(2)),
|
||||
high: parseFloat((basePrice * (1 + Math.random() * 0.05)).toFixed(2)),
|
||||
low: parseFloat((basePrice * (1 - Math.random() * 0.05)).toFixed(2)),
|
||||
volume: Math.floor(Math.random() * 100000000),
|
||||
amount: parseFloat((Math.random() * 10000000000).toFixed(2)),
|
||||
turnover_rate: parseFloat((Math.random() * 10).toFixed(2)),
|
||||
amplitude: parseFloat((Math.random() * 8).toFixed(2)),
|
||||
market: stockCode.startsWith('6') ? 'SH' : 'SZ',
|
||||
update_time: new Date().toISOString(),
|
||||
// 买卖盘口
|
||||
bid1: parseFloat((basePrice * 0.998).toFixed(2)),
|
||||
bid1_volume: Math.floor(Math.random() * 10000),
|
||||
bid2: parseFloat((basePrice * 0.996).toFixed(2)),
|
||||
bid2_volume: Math.floor(Math.random() * 10000),
|
||||
bid3: parseFloat((basePrice * 0.994).toFixed(2)),
|
||||
bid3_volume: Math.floor(Math.random() * 10000),
|
||||
bid4: parseFloat((basePrice * 0.992).toFixed(2)),
|
||||
bid4_volume: Math.floor(Math.random() * 10000),
|
||||
bid5: parseFloat((basePrice * 0.990).toFixed(2)),
|
||||
bid5_volume: Math.floor(Math.random() * 10000),
|
||||
ask1: parseFloat((basePrice * 1.002).toFixed(2)),
|
||||
ask1_volume: Math.floor(Math.random() * 10000),
|
||||
ask2: parseFloat((basePrice * 1.004).toFixed(2)),
|
||||
ask2_volume: Math.floor(Math.random() * 10000),
|
||||
ask3: parseFloat((basePrice * 1.006).toFixed(2)),
|
||||
ask3_volume: Math.floor(Math.random() * 10000),
|
||||
ask4: parseFloat((basePrice * 1.008).toFixed(2)),
|
||||
ask4_volume: Math.floor(Math.random() * 10000),
|
||||
ask5: parseFloat((basePrice * 1.010).toFixed(2)),
|
||||
ask5_volume: Math.floor(Math.random() * 10000),
|
||||
// 关键指标
|
||||
pe: parseFloat((Math.random() * 50 + 5).toFixed(2)),
|
||||
pb: parseFloat((Math.random() * 8 + 0.5).toFixed(2)),
|
||||
eps: parseFloat((Math.random() * 5 + 0.1).toFixed(3)),
|
||||
market_cap: `${(Math.random() * 5000 + 100).toFixed(0)}亿`,
|
||||
circulating_market_cap: `${(Math.random() * 3000 + 50).toFixed(0)}亿`,
|
||||
total_shares: `${(Math.random() * 100 + 10).toFixed(2)}亿`,
|
||||
circulating_shares: `${(Math.random() * 80 + 5).toFixed(2)}亿`,
|
||||
week52_high: parseFloat((basePrice * 1.3).toFixed(2)),
|
||||
week52_low: parseFloat((basePrice * 0.7).toFixed(2))
|
||||
},
|
||||
message: '获取成功'
|
||||
});
|
||||
}),
|
||||
|
||||
// FlexScreen 行情数据
|
||||
http.get('/api/flex-screen/quotes', async ({ request }) => {
|
||||
await delay(200);
|
||||
|
||||
const url = new URL(request.url);
|
||||
const codes = url.searchParams.get('codes')?.split(',') || [];
|
||||
|
||||
console.log('[Mock Stock] 获取 FlexScreen 行情:', { codes });
|
||||
|
||||
// 默认主要指数
|
||||
const defaultIndices = ['000001', '399001', '399006'];
|
||||
const targetCodes = codes.length > 0 ? codes : defaultIndices;
|
||||
|
||||
const indexData = {
|
||||
'000001': { name: '上证指数', basePrice: 3200 },
|
||||
'399001': { name: '深证成指', basePrice: 10500 },
|
||||
'399006': { name: '创业板指', basePrice: 2100 },
|
||||
'000300': { name: '沪深300', basePrice: 3800 },
|
||||
'000016': { name: '上证50', basePrice: 2600 },
|
||||
'000905': { name: '中证500', basePrice: 5800 },
|
||||
};
|
||||
|
||||
const quotesData = {};
|
||||
targetCodes.forEach(code => {
|
||||
const codeWithoutSuffix = code.replace(/\.(SH|SZ)$/i, '');
|
||||
const info = indexData[codeWithoutSuffix] || { name: `指数${code}`, basePrice: 3000 };
|
||||
|
||||
const changePercent = parseFloat((Math.random() * 4 - 2).toFixed(2));
|
||||
const price = parseFloat((info.basePrice * (1 + changePercent / 100)).toFixed(2));
|
||||
const change = parseFloat((price - info.basePrice).toFixed(2));
|
||||
|
||||
quotesData[code] = {
|
||||
code: code,
|
||||
name: info.name,
|
||||
price: price,
|
||||
change: change,
|
||||
change_percent: changePercent,
|
||||
prev_close: info.basePrice,
|
||||
open: parseFloat((info.basePrice * (1 + (Math.random() * 0.01 - 0.005))).toFixed(2)),
|
||||
high: parseFloat((price * (1 + Math.random() * 0.01)).toFixed(2)),
|
||||
low: parseFloat((price * (1 - Math.random() * 0.01)).toFixed(2)),
|
||||
volume: parseFloat((Math.random() * 5000 + 2000).toFixed(2)), // 亿手
|
||||
amount: parseFloat((Math.random() * 8000 + 3000).toFixed(2)), // 亿元
|
||||
update_time: new Date().toISOString()
|
||||
};
|
||||
});
|
||||
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
data: quotesData,
|
||||
message: '获取成功'
|
||||
});
|
||||
}),
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user