feat: 为"股票行情"和"财务全景"标签页添加 Mock 数据支持
问题:
- 点击"股票行情"标签页:MarketDataView 组件需要市场数据接口
- 点击"财务全景"标签页:FinancialPanorama 组件需要财务数据接口
- 这些接口都没有 mock 数据,导致页面显示空白
需要添加的接口:
股票行情 (MarketDataView) - 7个接口
1. /api/market/trade/:stockCode - 成交数据
2. /api/market/funding/:stockCode - 资金流向
3. /api/market/bigdeal/:stockCode - 大单统计
4. /api/market/unusual/:stockCode - 异动分析
5. /api/market/pledge/:stockCode - 股权质押
6. /api/market/summary/:stockCode - 市场摘要
7. /api/market/rise-analysis/:stockCode - 涨停分析
8. /api/stock/:stockCode/latest-minute - 最新分时数据
财务全景 (FinancialPanorama) - 9个接口
1. /api/financial/stock-info/:stockCode - 股票基本信息
2. /api/financial/balance-sheet/:stockCode - 资产负债表
3. /api/financial/income-statement/:stockCode - 利润表
4. /api/financial/cashflow/:stockCode - 现金流量表
5. /api/financial/financial-metrics/:stockCode - 财务指标
6. /api/financial/main-business/:stockCode - 主营业务
7. /api/financial/forecast/:stockCode - 业绩预告
8. /api/financial/industry-rank/:stockCode - 行业排名
9. /api/financial/comparison/:stockCode - 期间对比
实施步骤:
1. 创建 src/mocks/data/market.js - 市场数据
2. 创建 src/mocks/data/financial.js - 财务数据
3. 创建 src/mocks/handlers/market.js - 市场接口handlers
4. 创建 src/mocks/handlers/financial.js - 财务接口handlers
5. 更新 src/mocks/handlers/index.js - 注册新handlers
数据内容:
- 为平安银行 (000001) 提供完整真实数据
- 其他股票代码生成合理的模拟数据
This commit is contained in:
139
src/mocks/data/financial.js
Normal file
139
src/mocks/data/financial.js
Normal file
@@ -0,0 +1,139 @@
|
||||
// src/mocks/data/financial.js
|
||||
// 财务数据相关的 Mock 数据
|
||||
|
||||
// 生成财务数据
|
||||
export const generateFinancialData = (stockCode) => {
|
||||
const periods = ['2024-09-30', '2024-06-30', '2024-03-31', '2023-12-31'];
|
||||
|
||||
return {
|
||||
stockCode,
|
||||
|
||||
// 股票基本信息
|
||||
stockInfo: {
|
||||
code: stockCode,
|
||||
name: stockCode === '000001' ? '平安银行' : '示例公司',
|
||||
industry: stockCode === '000001' ? '银行' : '制造业',
|
||||
list_date: '1991-04-03',
|
||||
market: 'SZ'
|
||||
},
|
||||
|
||||
// 资产负债表
|
||||
balanceSheet: periods.map((period, i) => ({
|
||||
period,
|
||||
total_assets: 5024560 - i * 50000, // 百万元
|
||||
total_liabilities: 4698880 - i * 48000,
|
||||
shareholders_equity: 325680 - i * 2000,
|
||||
current_assets: 2512300 - i * 25000,
|
||||
non_current_assets: 2512260 - i * 25000,
|
||||
current_liabilities: 3456780 - i * 35000,
|
||||
non_current_liabilities: 1242100 - i * 13000
|
||||
})),
|
||||
|
||||
// 利润表
|
||||
incomeStatement: periods.map((period, i) => ({
|
||||
period,
|
||||
revenue: 162350 - i * 4000, // 百万元
|
||||
operating_cost: 45620 - i * 1200,
|
||||
gross_profit: 116730 - i * 2800,
|
||||
operating_profit: 68450 - i * 1500,
|
||||
net_profit: 52860 - i * 1200,
|
||||
eps: 2.72 - i * 0.06
|
||||
})),
|
||||
|
||||
// 现金流量表
|
||||
cashflow: periods.map((period, i) => ({
|
||||
period,
|
||||
operating_cashflow: 125600 - i * 3000, // 百万元
|
||||
investing_cashflow: -45300 - i * 1000,
|
||||
financing_cashflow: -38200 + i * 500,
|
||||
net_cashflow: 42100 - i * 1500,
|
||||
cash_ending: 456780 - i * 10000
|
||||
})),
|
||||
|
||||
// 财务指标
|
||||
financialMetrics: periods.map((period, i) => ({
|
||||
period,
|
||||
roe: 16.23 - i * 0.3, // %
|
||||
roa: 1.05 - i * 0.02,
|
||||
gross_margin: 71.92 - i * 0.5,
|
||||
net_margin: 32.56 - i * 0.3,
|
||||
current_ratio: 0.73 + i * 0.01,
|
||||
quick_ratio: 0.71 + i * 0.01,
|
||||
debt_ratio: 93.52 + i * 0.05,
|
||||
asset_turnover: 0.41 - i * 0.01,
|
||||
inventory_turnover: 0, // 银行无库存
|
||||
receivable_turnover: 0 // 银行特殊
|
||||
})),
|
||||
|
||||
// 主营业务
|
||||
mainBusiness: {
|
||||
by_product: [
|
||||
{ name: '对公业务', revenue: 68540, ratio: 42.2, yoy_growth: 6.8 },
|
||||
{ name: '零售业务', revenue: 81320, ratio: 50.1, yoy_growth: 11.2 },
|
||||
{ name: '金融市场业务', revenue: 12490, ratio: 7.7, yoy_growth: 3.5 }
|
||||
],
|
||||
by_region: [
|
||||
{ name: '华南地区', revenue: 56800, ratio: 35.0, yoy_growth: 9.2 },
|
||||
{ name: '华东地区', revenue: 48705, ratio: 30.0, yoy_growth: 8.5 },
|
||||
{ name: '华北地区', revenue: 32470, ratio: 20.0, yoy_growth: 7.8 },
|
||||
{ name: '其他地区', revenue: 24375, ratio: 15.0, yoy_growth: 6.5 }
|
||||
]
|
||||
},
|
||||
|
||||
// 业绩预告
|
||||
forecast: {
|
||||
period: '2024',
|
||||
forecast_net_profit_min: 580000, // 百万元
|
||||
forecast_net_profit_max: 620000,
|
||||
yoy_growth_min: 10.0, // %
|
||||
yoy_growth_max: 17.0,
|
||||
forecast_type: '预增',
|
||||
reason: '受益于零售业务快速增长及资产质量改善,预计全年业绩保持稳定增长',
|
||||
publish_date: '2024-10-15'
|
||||
},
|
||||
|
||||
// 行业排名
|
||||
industryRank: {
|
||||
industry: '银行',
|
||||
total_companies: 42,
|
||||
rankings: [
|
||||
{ metric: '总资产', rank: 8, value: 5024560, percentile: 19 },
|
||||
{ metric: '营业收入', rank: 9, value: 162350, percentile: 21 },
|
||||
{ metric: '净利润', rank: 8, value: 52860, percentile: 19 },
|
||||
{ metric: 'ROE', rank: 12, value: 16.23, percentile: 29 },
|
||||
{ metric: '不良贷款率', rank: 18, value: 1.02, percentile: 43 }
|
||||
]
|
||||
},
|
||||
|
||||
// 期间对比
|
||||
periodComparison: {
|
||||
periods: ['Q3-2024', 'Q2-2024', 'Q1-2024', 'Q4-2023'],
|
||||
metrics: [
|
||||
{
|
||||
name: '营业收入',
|
||||
unit: '百万元',
|
||||
values: [41500, 40800, 40200, 40850],
|
||||
yoy: [8.2, 7.8, 8.5, 9.2]
|
||||
},
|
||||
{
|
||||
name: '净利润',
|
||||
unit: '百万元',
|
||||
values: [13420, 13180, 13050, 13210],
|
||||
yoy: [12.5, 11.2, 10.8, 12.3]
|
||||
},
|
||||
{
|
||||
name: 'ROE',
|
||||
unit: '%',
|
||||
values: [16.23, 15.98, 15.75, 16.02],
|
||||
yoy: [1.2, 0.8, 0.5, 1.0]
|
||||
},
|
||||
{
|
||||
name: 'EPS',
|
||||
unit: '元',
|
||||
values: [0.69, 0.68, 0.67, 0.68],
|
||||
yoy: [12.3, 11.5, 10.5, 12.0]
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
};
|
||||
141
src/mocks/data/market.js
Normal file
141
src/mocks/data/market.js
Normal file
@@ -0,0 +1,141 @@
|
||||
// src/mocks/data/market.js
|
||||
// 市场行情相关的 Mock 数据
|
||||
|
||||
// 生成市场数据
|
||||
export const generateMarketData = (stockCode) => {
|
||||
const basePrice = 13.50; // 基准价格(平安银行约13.5元)
|
||||
|
||||
return {
|
||||
stockCode,
|
||||
|
||||
// 成交数据
|
||||
tradeData: {
|
||||
success: true,
|
||||
data: Array(30).fill(null).map((_, i) => ({
|
||||
date: new Date(Date.now() - (29 - i) * 24 * 60 * 60 * 1000).toISOString().split('T')[0],
|
||||
volume: Math.floor(Math.random() * 500000000) + 100000000, // 1-6亿股
|
||||
amount: Math.floor(Math.random() * 7000000000) + 1300000000, // 13-80亿元
|
||||
turnover_rate: (Math.random() * 2 + 0.5).toFixed(2), // 0.5-2.5%
|
||||
change_pct: (Math.random() * 6 - 3).toFixed(2) // -3% to +3%
|
||||
}))
|
||||
},
|
||||
|
||||
// 资金流向
|
||||
fundingData: {
|
||||
success: true,
|
||||
data: {
|
||||
main_inflow: 125600000, // 主力净流入(元)
|
||||
retail_inflow: -45300000, // 散户净流入
|
||||
large_inflow: 89200000, // 大单净流入
|
||||
medium_inflow: 23400000, // 中单净流入
|
||||
small_outflow: -68600000, // 小单净流出
|
||||
trend: Array(30).fill(null).map((_, i) => ({
|
||||
date: new Date(Date.now() - (29 - i) * 24 * 60 * 60 * 1000).toISOString().split('T')[0],
|
||||
main_inflow: Math.floor(Math.random() * 200000000) - 100000000
|
||||
}))
|
||||
}
|
||||
},
|
||||
|
||||
// 大单统计
|
||||
bigDealData: {
|
||||
success: true,
|
||||
data: {
|
||||
today: {
|
||||
buy_large: 234500000,
|
||||
sell_large: 189600000,
|
||||
net_large: 44900000,
|
||||
buy_count: 1256,
|
||||
sell_count: 1089
|
||||
},
|
||||
history: Array(30).fill(null).map((_, i) => ({
|
||||
date: new Date(Date.now() - (29 - i) * 24 * 60 * 60 * 1000).toISOString().split('T')[0],
|
||||
net_large: Math.floor(Math.random() * 200000000) - 100000000
|
||||
}))
|
||||
}
|
||||
},
|
||||
|
||||
// 异动分析
|
||||
unusualData: {
|
||||
success: true,
|
||||
data: {
|
||||
events: [
|
||||
{ time: '14:35:22', type: '快速拉升', change: '+2.3%', description: '5分钟内上涨2.3%' },
|
||||
{ time: '11:28:45', type: '大单买入', amount: '5680万', description: '单笔大单买入' },
|
||||
{ time: '10:15:30', type: '量比异动', ratio: '3.2', description: '量比达到3.2倍' }
|
||||
],
|
||||
volume_ratio: 1.85, // 量比
|
||||
turnover_rate: 1.23, // 换手率
|
||||
amplitude: 3.45 // 振幅%
|
||||
}
|
||||
},
|
||||
|
||||
// 股权质押
|
||||
pledgeData: {
|
||||
success: true,
|
||||
data: {
|
||||
total_pledged: 25.6, // 质押比例%
|
||||
major_shareholders: [
|
||||
{ name: '中国平安保险集团', pledged_shares: 0, total_shares: 10168542300, pledge_ratio: 0 },
|
||||
{ name: '深圳市投资控股', pledged_shares: 50000000, total_shares: 382456100, pledge_ratio: 13.08 }
|
||||
],
|
||||
update_date: '2024-09-30'
|
||||
}
|
||||
},
|
||||
|
||||
// 市场摘要
|
||||
summaryData: {
|
||||
success: true,
|
||||
data: {
|
||||
current_price: basePrice,
|
||||
change: 0.25,
|
||||
change_pct: 1.89,
|
||||
open: 13.35,
|
||||
high: 13.68,
|
||||
low: 13.28,
|
||||
volume: 345678900,
|
||||
amount: 4678900000,
|
||||
turnover_rate: 1.78,
|
||||
pe_ratio: 4.96,
|
||||
pb_ratio: 0.72,
|
||||
total_market_cap: 262300000000,
|
||||
circulating_market_cap: 262300000000
|
||||
}
|
||||
},
|
||||
|
||||
// 涨停分析
|
||||
riseAnalysisData: {
|
||||
success: true,
|
||||
data: {
|
||||
is_limit_up: false,
|
||||
limit_up_price: basePrice * 1.10,
|
||||
current_price: basePrice,
|
||||
distance_to_limit: 8.92, // %
|
||||
consecutive_days: 0,
|
||||
reason: '',
|
||||
concept_tags: ['银行', '深圳国资', 'MSCI', '沪深300']
|
||||
}
|
||||
},
|
||||
|
||||
// 最新分时数据
|
||||
latestMinuteData: {
|
||||
success: true,
|
||||
data: Array(240).fill(null).map((_, i) => {
|
||||
const minute = 9 * 60 + 30 + i; // 从9:30开始
|
||||
const hour = Math.floor(minute / 60);
|
||||
const min = minute % 60;
|
||||
const time = `${hour.toString().padStart(2, '0')}:${min.toString().padStart(2, '0')}`;
|
||||
const randomChange = (Math.random() - 0.5) * 0.1;
|
||||
return {
|
||||
time,
|
||||
price: (basePrice + randomChange).toFixed(2),
|
||||
volume: Math.floor(Math.random() * 2000000) + 500000,
|
||||
avg_price: (basePrice + randomChange * 0.8).toFixed(2)
|
||||
};
|
||||
}),
|
||||
code: stockCode,
|
||||
name: stockCode === '000001' ? '平安银行' : '示例股票',
|
||||
trade_date: new Date().toISOString().split('T')[0],
|
||||
type: 'minute'
|
||||
}
|
||||
};
|
||||
};
|
||||
121
src/mocks/handlers/financial.js
Normal file
121
src/mocks/handlers/financial.js
Normal file
@@ -0,0 +1,121 @@
|
||||
// src/mocks/handlers/financial.js
|
||||
// 财务数据相关的 Mock Handlers
|
||||
|
||||
import { http, HttpResponse } from 'msw';
|
||||
import { generateFinancialData } from '../data/financial';
|
||||
|
||||
// 模拟延迟
|
||||
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
||||
|
||||
export const financialHandlers = [
|
||||
// 1. 股票基本信息
|
||||
http.get('/api/financial/stock-info/:stockCode', async ({ params }) => {
|
||||
await delay(150);
|
||||
const { stockCode } = params;
|
||||
const data = generateFinancialData(stockCode);
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
data: data.stockInfo
|
||||
});
|
||||
}),
|
||||
|
||||
// 2. 资产负债表
|
||||
http.get('/api/financial/balance-sheet/:stockCode', async ({ params, request }) => {
|
||||
await delay(250);
|
||||
const { stockCode } = params;
|
||||
const url = new URL(request.url);
|
||||
const limit = parseInt(url.searchParams.get('limit') || '4', 10);
|
||||
|
||||
const data = generateFinancialData(stockCode);
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
data: data.balanceSheet.slice(0, limit)
|
||||
});
|
||||
}),
|
||||
|
||||
// 3. 利润表
|
||||
http.get('/api/financial/income-statement/:stockCode', async ({ params, request }) => {
|
||||
await delay(250);
|
||||
const { stockCode } = params;
|
||||
const url = new URL(request.url);
|
||||
const limit = parseInt(url.searchParams.get('limit') || '4', 10);
|
||||
|
||||
const data = generateFinancialData(stockCode);
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
data: data.incomeStatement.slice(0, limit)
|
||||
});
|
||||
}),
|
||||
|
||||
// 4. 现金流量表
|
||||
http.get('/api/financial/cashflow/:stockCode', async ({ params, request }) => {
|
||||
await delay(250);
|
||||
const { stockCode } = params;
|
||||
const url = new URL(request.url);
|
||||
const limit = parseInt(url.searchParams.get('limit') || '4', 10);
|
||||
|
||||
const data = generateFinancialData(stockCode);
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
data: data.cashflow.slice(0, limit)
|
||||
});
|
||||
}),
|
||||
|
||||
// 5. 财务指标
|
||||
http.get('/api/financial/financial-metrics/:stockCode', async ({ params, request }) => {
|
||||
await delay(250);
|
||||
const { stockCode } = params;
|
||||
const url = new URL(request.url);
|
||||
const limit = parseInt(url.searchParams.get('limit') || '4', 10);
|
||||
|
||||
const data = generateFinancialData(stockCode);
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
data: data.financialMetrics.slice(0, limit)
|
||||
});
|
||||
}),
|
||||
|
||||
// 6. 主营业务
|
||||
http.get('/api/financial/main-business/:stockCode', async ({ params }) => {
|
||||
await delay(200);
|
||||
const { stockCode } = params;
|
||||
const data = generateFinancialData(stockCode);
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
data: data.mainBusiness
|
||||
});
|
||||
}),
|
||||
|
||||
// 7. 业绩预告
|
||||
http.get('/api/financial/forecast/:stockCode', async ({ params }) => {
|
||||
await delay(200);
|
||||
const { stockCode } = params;
|
||||
const data = generateFinancialData(stockCode);
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
data: data.forecast
|
||||
});
|
||||
}),
|
||||
|
||||
// 8. 行业排名
|
||||
http.get('/api/financial/industry-rank/:stockCode', async ({ params }) => {
|
||||
await delay(250);
|
||||
const { stockCode } = params;
|
||||
const data = generateFinancialData(stockCode);
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
data: data.industryRank
|
||||
});
|
||||
}),
|
||||
|
||||
// 9. 期间对比
|
||||
http.get('/api/financial/comparison/:stockCode', async ({ params }) => {
|
||||
await delay(250);
|
||||
const { stockCode } = params;
|
||||
const data = generateFinancialData(stockCode);
|
||||
return HttpResponse.json({
|
||||
success: true,
|
||||
data: data.periodComparison
|
||||
});
|
||||
}),
|
||||
];
|
||||
@@ -10,6 +10,8 @@ import { industryHandlers } from './industry';
|
||||
import { conceptHandlers } from './concept';
|
||||
import { stockHandlers } from './stock';
|
||||
import { companyHandlers } from './company';
|
||||
import { marketHandlers } from './market';
|
||||
import { financialHandlers } from './financial';
|
||||
|
||||
// 可以在这里添加更多的 handlers
|
||||
// import { userHandlers } from './user';
|
||||
@@ -24,5 +26,7 @@ export const handlers = [
|
||||
...conceptHandlers,
|
||||
...stockHandlers,
|
||||
...companyHandlers,
|
||||
...marketHandlers,
|
||||
...financialHandlers,
|
||||
// ...userHandlers,
|
||||
];
|
||||
|
||||
74
src/mocks/handlers/market.js
Normal file
74
src/mocks/handlers/market.js
Normal file
@@ -0,0 +1,74 @@
|
||||
// src/mocks/handlers/market.js
|
||||
// 市场行情相关的 Mock Handlers
|
||||
|
||||
import { http, HttpResponse } from 'msw';
|
||||
import { generateMarketData } from '../data/market';
|
||||
|
||||
// 模拟延迟
|
||||
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
||||
|
||||
export const marketHandlers = [
|
||||
// 1. 成交数据
|
||||
http.get('/api/market/trade/:stockCode', async ({ params, request }) => {
|
||||
await delay(200);
|
||||
const { stockCode } = params;
|
||||
const data = generateMarketData(stockCode);
|
||||
return HttpResponse.json(data.tradeData);
|
||||
}),
|
||||
|
||||
// 2. 资金流向
|
||||
http.get('/api/market/funding/:stockCode', async ({ params }) => {
|
||||
await delay(200);
|
||||
const { stockCode } = params;
|
||||
const data = generateMarketData(stockCode);
|
||||
return HttpResponse.json(data.fundingData);
|
||||
}),
|
||||
|
||||
// 3. 大单统计
|
||||
http.get('/api/market/bigdeal/:stockCode', async ({ params }) => {
|
||||
await delay(200);
|
||||
const { stockCode } = params;
|
||||
const data = generateMarketData(stockCode);
|
||||
return HttpResponse.json(data.bigDealData);
|
||||
}),
|
||||
|
||||
// 4. 异动分析
|
||||
http.get('/api/market/unusual/:stockCode', async ({ params }) => {
|
||||
await delay(200);
|
||||
const { stockCode } = params;
|
||||
const data = generateMarketData(stockCode);
|
||||
return HttpResponse.json(data.unusualData);
|
||||
}),
|
||||
|
||||
// 5. 股权质押
|
||||
http.get('/api/market/pledge/:stockCode', async ({ params }) => {
|
||||
await delay(200);
|
||||
const { stockCode } = params;
|
||||
const data = generateMarketData(stockCode);
|
||||
return HttpResponse.json(data.pledgeData);
|
||||
}),
|
||||
|
||||
// 6. 市场摘要
|
||||
http.get('/api/market/summary/:stockCode', async ({ params }) => {
|
||||
await delay(200);
|
||||
const { stockCode } = params;
|
||||
const data = generateMarketData(stockCode);
|
||||
return HttpResponse.json(data.summaryData);
|
||||
}),
|
||||
|
||||
// 7. 涨停分析
|
||||
http.get('/api/market/rise-analysis/:stockCode', async ({ params }) => {
|
||||
await delay(200);
|
||||
const { stockCode } = params;
|
||||
const data = generateMarketData(stockCode);
|
||||
return HttpResponse.json(data.riseAnalysisData);
|
||||
}),
|
||||
|
||||
// 8. 最新分时数据
|
||||
http.get('/api/stock/:stockCode/latest-minute', async ({ params }) => {
|
||||
await delay(300);
|
||||
const { stockCode } = params;
|
||||
const data = generateMarketData(stockCode);
|
||||
return HttpResponse.json(data.latestMinuteData);
|
||||
}),
|
||||
];
|
||||
Reference in New Issue
Block a user