feat:添加mock接口
1. ✅ Profile 和 Settings 页面(2个文件) 2. ✅ EventDetail 页面(1个文件) 3. ✅ 身份验证组件(WechatRegister.js) 4. ✅ Company 页面(CompanyOverview, index, FinancialPanorama, MarketDataView) 5. ✅ Concept 页面(ConceptTimelineModal, ConceptStatsPanel, index)
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
// src/views/Company/FinancialPanorama.jsx
|
// src/views/Company/FinancialPanorama.jsx
|
||||||
import React, { useState, useEffect, useMemo } from 'react';
|
import React, { useState, useEffect, useMemo } from 'react';
|
||||||
|
import { logger } from '../../utils/logger';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Container,
|
Container,
|
||||||
@@ -112,6 +113,7 @@ const FinancialPanorama = ({ stockCode: propStockCode }) => {
|
|||||||
// 加载所有财务数据
|
// 加载所有财务数据
|
||||||
const loadFinancialData = async () => {
|
const loadFinancialData = async () => {
|
||||||
if (!stockCode || stockCode.length !== 6) {
|
if (!stockCode || stockCode.length !== 6) {
|
||||||
|
logger.warn('FinancialPanorama', 'loadFinancialData', '无效的股票代码', { stockCode });
|
||||||
toast({
|
toast({
|
||||||
title: '请输入有效的6位股票代码',
|
title: '请输入有效的6位股票代码',
|
||||||
status: 'warning',
|
status: 'warning',
|
||||||
@@ -120,9 +122,10 @@ const FinancialPanorama = ({ stockCode: propStockCode }) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.debug('FinancialPanorama', '开始加载财务数据', { stockCode, selectedPeriods });
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 并行加载所有数据
|
// 并行加载所有数据
|
||||||
const [
|
const [
|
||||||
@@ -158,19 +161,14 @@ const FinancialPanorama = ({ stockCode: propStockCode }) => {
|
|||||||
if (rankRes.success) setIndustryRank(rankRes.data);
|
if (rankRes.success) setIndustryRank(rankRes.data);
|
||||||
if (comparisonRes.success) setComparison(comparisonRes.data);
|
if (comparisonRes.success) setComparison(comparisonRes.data);
|
||||||
|
|
||||||
toast({
|
// ❌ 移除数据加载成功toast
|
||||||
title: '财务数据加载成功',
|
logger.info('FinancialPanorama', '财务数据加载成功', { stockCode });
|
||||||
status: 'success',
|
|
||||||
duration: 2000,
|
|
||||||
});
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err.message);
|
setError(err.message);
|
||||||
toast({
|
logger.error('FinancialPanorama', 'loadFinancialData', err, { stockCode, selectedPeriods });
|
||||||
title: '数据加载失败',
|
|
||||||
description: err.message,
|
// ❌ 移除数据加载失败toast
|
||||||
status: 'error',
|
// toast({ title: '数据加载失败', description: err.message, status: 'error', duration: 5000 });
|
||||||
duration: 5000,
|
|
||||||
});
|
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
@@ -1501,6 +1499,7 @@ const FinancialPanorama = ({ stockCode: propStockCode }) => {
|
|||||||
|
|
||||||
const loadCompareData = async () => {
|
const loadCompareData = async () => {
|
||||||
if (!compareStock || compareStock.length !== 6) {
|
if (!compareStock || compareStock.length !== 6) {
|
||||||
|
logger.warn('FinancialPanorama', 'loadCompareData', '无效的对比股票代码', { compareStock });
|
||||||
toast({
|
toast({
|
||||||
title: '请输入有效的6位股票代码',
|
title: '请输入有效的6位股票代码',
|
||||||
status: 'warning',
|
status: 'warning',
|
||||||
@@ -1508,7 +1507,8 @@ const FinancialPanorama = ({ stockCode: propStockCode }) => {
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.debug('FinancialPanorama', '开始加载对比数据', { currentStock, compareStock });
|
||||||
setCompareLoading(true);
|
setCompareLoading(true);
|
||||||
try {
|
try {
|
||||||
const [stockInfoRes, metricsRes, comparisonRes] = await Promise.all([
|
const [stockInfoRes, metricsRes, comparisonRes] = await Promise.all([
|
||||||
@@ -1516,25 +1516,20 @@ const FinancialPanorama = ({ stockCode: propStockCode }) => {
|
|||||||
financialService.getFinancialMetrics(compareStock, 4),
|
financialService.getFinancialMetrics(compareStock, 4),
|
||||||
financialService.getPeriodComparison(compareStock, 4)
|
financialService.getPeriodComparison(compareStock, 4)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
setCompareData({
|
setCompareData({
|
||||||
stockInfo: stockInfoRes.data,
|
stockInfo: stockInfoRes.data,
|
||||||
metrics: metricsRes.data,
|
metrics: metricsRes.data,
|
||||||
comparison: comparisonRes.data
|
comparison: comparisonRes.data
|
||||||
});
|
});
|
||||||
|
|
||||||
toast({
|
// ❌ 移除对比数据加载成功toast
|
||||||
title: '对比数据加载成功',
|
logger.info('FinancialPanorama', '对比数据加载成功', { currentStock, compareStock });
|
||||||
status: 'success',
|
|
||||||
duration: 2000,
|
|
||||||
});
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast({
|
logger.error('FinancialPanorama', 'loadCompareData', error, { currentStock, compareStock });
|
||||||
title: '加载对比数据失败',
|
|
||||||
description: error.message,
|
// ❌ 移除对比数据加载失败toast
|
||||||
status: 'error',
|
// toast({ title: '加载对比数据失败', description: error.message, status: 'error', duration: 3000 });
|
||||||
duration: 3000,
|
|
||||||
});
|
|
||||||
} finally {
|
} finally {
|
||||||
setCompareLoading(false);
|
setCompareLoading(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// src/views/Market/MarketDataPro.jsx
|
// src/views/Market/MarketDataPro.jsx
|
||||||
import React, { useState, useEffect, useMemo } from 'react';
|
import React, { useState, useEffect, useMemo } from 'react';
|
||||||
|
import { logger } from '../../utils/logger';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Container,
|
Container,
|
||||||
@@ -151,7 +152,7 @@ const marketService = {
|
|||||||
}
|
}
|
||||||
return response.json();
|
return response.json();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`API request failed for ${url}:`, error);
|
logger.error('marketService', 'apiRequest', error, { url });
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -317,6 +318,7 @@ const MarketDataView = ({ stockCode: propStockCode }) => {
|
|||||||
|
|
||||||
// 加载数据
|
// 加载数据
|
||||||
const loadMarketData = async () => {
|
const loadMarketData = async () => {
|
||||||
|
logger.debug('MarketDataView', '开始加载市场数据', { stockCode, selectedPeriod });
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
const [summaryRes, tradeRes, fundingRes, bigDealRes, unusualRes, pledgeRes, riseAnalysisRes] = await Promise.all([
|
const [summaryRes, tradeRes, fundingRes, bigDealRes, unusualRes, pledgeRes, riseAnalysisRes] = await Promise.all([
|
||||||
@@ -337,12 +339,12 @@ const MarketDataView = ({ stockCode: propStockCode }) => {
|
|||||||
if (pledgeRes.success) setPledgeData(pledgeRes.data);
|
if (pledgeRes.success) setPledgeData(pledgeRes.data);
|
||||||
if (riseAnalysisRes.success) {
|
if (riseAnalysisRes.success) {
|
||||||
setRiseAnalysisData(riseAnalysisRes.data);
|
setRiseAnalysisData(riseAnalysisRes.data);
|
||||||
|
|
||||||
// 创建分析数据映射
|
// 创建分析数据映射
|
||||||
const tempAnalysisMap = {};
|
const tempAnalysisMap = {};
|
||||||
if (tradeRes.success && tradeRes.data && riseAnalysisRes.data) {
|
if (tradeRes.success && tradeRes.data && riseAnalysisRes.data) {
|
||||||
riseAnalysisRes.data.forEach(analysis => {
|
riseAnalysisRes.data.forEach(analysis => {
|
||||||
const dateIndex = tradeRes.data.findIndex(item =>
|
const dateIndex = tradeRes.data.findIndex(item =>
|
||||||
item.date.substring(0, 10) === analysis.trade_date
|
item.date.substring(0, 10) === analysis.trade_date
|
||||||
);
|
);
|
||||||
if (dateIndex !== -1) {
|
if (dateIndex !== -1) {
|
||||||
@@ -352,22 +354,14 @@ const MarketDataView = ({ stockCode: propStockCode }) => {
|
|||||||
}
|
}
|
||||||
setAnalysisMap(tempAnalysisMap);
|
setAnalysisMap(tempAnalysisMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
toast({
|
// ❌ 移除数据加载成功toast
|
||||||
title: '数据加载成功',
|
logger.info('MarketDataView', '市场数据加载成功', { stockCode });
|
||||||
status: 'success',
|
|
||||||
duration: 2000,
|
|
||||||
isClosable: true,
|
|
||||||
});
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load market data:', error);
|
logger.error('MarketDataView', 'loadMarketData', error, { stockCode, selectedPeriod });
|
||||||
toast({
|
|
||||||
title: '数据加载失败',
|
// ❌ 移除数据加载失败toast
|
||||||
description: error.message,
|
// toast({ title: '数据加载失败', description: error.message, status: 'error', duration: 5000, isClosable: true });
|
||||||
status: 'error',
|
|
||||||
duration: 5000,
|
|
||||||
isClosable: true,
|
|
||||||
});
|
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
@@ -375,6 +369,7 @@ const MarketDataView = ({ stockCode: propStockCode }) => {
|
|||||||
|
|
||||||
// 获取分钟频数据
|
// 获取分钟频数据
|
||||||
const loadMinuteData = async () => {
|
const loadMinuteData = async () => {
|
||||||
|
logger.debug('MarketDataView', '开始加载分钟频数据', { stockCode });
|
||||||
setMinuteLoading(true);
|
setMinuteLoading(true);
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
@@ -394,19 +389,17 @@ const MarketDataView = ({ stockCode: propStockCode }) => {
|
|||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
if (data.data && Array.isArray(data.data)) {
|
if (data.data && Array.isArray(data.data)) {
|
||||||
setMinuteData(data);
|
setMinuteData(data);
|
||||||
|
logger.info('MarketDataView', '分钟频数据加载成功', { stockCode, dataPoints: data.data.length });
|
||||||
} else {
|
} else {
|
||||||
setMinuteData({ data: [], code: stockCode, name: '', trade_date: '', type: 'minute' });
|
setMinuteData({ data: [], code: stockCode, name: '', trade_date: '', type: 'minute' });
|
||||||
|
logger.warn('MarketDataView', '分钟频数据为空', { stockCode });
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load minute data:', error);
|
logger.error('MarketDataView', 'loadMinuteData', error, { stockCode });
|
||||||
toast({
|
|
||||||
title: '分钟数据加载失败',
|
// ❌ 移除分钟数据加载失败toast
|
||||||
description: error.message,
|
// toast({ title: '分钟数据加载失败', description: error.message, status: 'error', duration: 3000, isClosable: true });
|
||||||
status: 'error',
|
|
||||||
duration: 3000,
|
|
||||||
isClosable: true,
|
|
||||||
});
|
|
||||||
setMinuteData({ data: [], code: stockCode, name: '', trade_date: '', type: 'minute' });
|
setMinuteData({ data: [], code: stockCode, name: '', trade_date: '', type: 'minute' });
|
||||||
} finally {
|
} finally {
|
||||||
setMinuteLoading(false);
|
setMinuteLoading(false);
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import { SearchIcon, MoonIcon, SunIcon, StarIcon } from '@chakra-ui/icons';
|
|||||||
import { FaChartLine, FaMoneyBillWave, FaChartBar, FaInfoCircle } from 'react-icons/fa';
|
import { FaChartLine, FaMoneyBillWave, FaChartBar, FaInfoCircle } from 'react-icons/fa';
|
||||||
import HomeNavbar from '../../components/Navbars/HomeNavbar';
|
import HomeNavbar from '../../components/Navbars/HomeNavbar';
|
||||||
import { useAuth } from '../../contexts/AuthContext';
|
import { useAuth } from '../../contexts/AuthContext';
|
||||||
|
import { logger } from '../../utils/logger';
|
||||||
import FinancialPanorama from './FinancialPanorama';
|
import FinancialPanorama from './FinancialPanorama';
|
||||||
import ForecastReport from './ForecastReport';
|
import ForecastReport from './ForecastReport';
|
||||||
import MarketDataView from './MarketDataView';
|
import MarketDataView from './MarketDataView';
|
||||||
@@ -99,10 +100,12 @@ const CompanyIndex = () => {
|
|||||||
|
|
||||||
const handleWatchlistToggle = async () => {
|
const handleWatchlistToggle = async () => {
|
||||||
if (!stockCode) {
|
if (!stockCode) {
|
||||||
|
logger.warn('CompanyIndex', 'handleWatchlistToggle', '无效的股票代码', { stockCode });
|
||||||
toast({ title: '无效的股票代码', status: 'error', duration: 2000 });
|
toast({ title: '无效的股票代码', status: 'error', duration: 2000 });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!isAuthenticated) {
|
if (!isAuthenticated) {
|
||||||
|
logger.warn('CompanyIndex', 'handleWatchlistToggle', '用户未登录', { stockCode });
|
||||||
toast({ title: '请先登录后再加入自选', status: 'warning', duration: 2000 });
|
toast({ title: '请先登录后再加入自选', status: 'warning', duration: 2000 });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -110,25 +113,39 @@ const CompanyIndex = () => {
|
|||||||
setIsWatchlistLoading(true);
|
setIsWatchlistLoading(true);
|
||||||
const base = getApiBase();
|
const base = getApiBase();
|
||||||
if (isInWatchlist) {
|
if (isInWatchlist) {
|
||||||
const resp = await fetch(base + `/api/account/watchlist/${stockCode}`, {
|
logger.debug('CompanyIndex', '准备从自选移除', { stockCode });
|
||||||
|
const url = base + `/api/account/watchlist/${stockCode}`;
|
||||||
|
logger.api.request('DELETE', url, { stockCode });
|
||||||
|
|
||||||
|
const resp = await fetch(url, {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
credentials: 'include'
|
credentials: 'include'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
logger.api.response('DELETE', url, resp.status);
|
||||||
if (!resp.ok) throw new Error('删除失败');
|
if (!resp.ok) throw new Error('删除失败');
|
||||||
setIsInWatchlist(false);
|
setIsInWatchlist(false);
|
||||||
toast({ title: '已从自选移除', status: 'info', duration: 1500 });
|
toast({ title: '已从自选移除', status: 'info', duration: 1500 });
|
||||||
} else {
|
} else {
|
||||||
const resp = await fetch(base + '/api/account/watchlist', {
|
logger.debug('CompanyIndex', '准备添加到自选', { stockCode });
|
||||||
|
const url = base + '/api/account/watchlist';
|
||||||
|
const body = { stock_code: stockCode };
|
||||||
|
logger.api.request('POST', url, body);
|
||||||
|
|
||||||
|
const resp = await fetch(url, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
body: JSON.stringify({ stock_code: stockCode })
|
body: JSON.stringify(body)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
logger.api.response('POST', url, resp.status);
|
||||||
if (!resp.ok) throw new Error('添加失败');
|
if (!resp.ok) throw new Error('添加失败');
|
||||||
setIsInWatchlist(true);
|
setIsInWatchlist(true);
|
||||||
toast({ title: '已加入自选', status: 'success', duration: 1500 });
|
toast({ title: '已加入自选', status: 'success', duration: 1500 });
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
logger.error('CompanyIndex', 'handleWatchlistToggle', error, { stockCode, isInWatchlist });
|
||||||
toast({ title: '操作失败,请稍后重试', status: 'error', duration: 2000 });
|
toast({ title: '操作失败,请稍后重试', status: 'error', duration: 2000 });
|
||||||
} finally {
|
} finally {
|
||||||
setIsWatchlistLoading(false);
|
setIsWatchlistLoading(false);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { logger } from '../../utils/logger';
|
||||||
import {
|
import {
|
||||||
Modal,
|
Modal,
|
||||||
ModalOverlay,
|
ModalOverlay,
|
||||||
@@ -111,13 +112,13 @@ const ConceptTimelineModal = ({
|
|||||||
`end_date=${endDateStr}`
|
`end_date=${endDateStr}`
|
||||||
).then(res => {
|
).then(res => {
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
console.error('Price API error:', res.status);
|
logger.error('ConceptTimelineModal', 'fetchTimelineData - Price API', new Error(`HTTP ${res.status}`), { conceptId, startDateStr, endDateStr });
|
||||||
throw new Error(`Price API error: ${res.status}`);
|
throw new Error(`Price API error: ${res.status}`);
|
||||||
}
|
}
|
||||||
return res.json();
|
return res.json();
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.error('Price API error:', err);
|
logger.error('ConceptTimelineModal', 'fetchTimelineData - Price API', err, { conceptId });
|
||||||
return { timeseries: [] };
|
return { timeseries: [] };
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -138,13 +139,13 @@ const ConceptTimelineModal = ({
|
|||||||
.then(async res => {
|
.then(async res => {
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
const text = await res.text();
|
const text = await res.text();
|
||||||
console.error('News API error:', res.status, text);
|
logger.error('ConceptTimelineModal', 'fetchTimelineData - News API', new Error(`HTTP ${res.status}`), { conceptName, status: res.status, response: text.substring(0, 200) });
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
return res.json();
|
return res.json();
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.error('News API error:', err);
|
logger.error('ConceptTimelineModal', 'fetchTimelineData - News API', err, { conceptName });
|
||||||
return [];
|
return [];
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -165,13 +166,13 @@ const ConceptTimelineModal = ({
|
|||||||
.then(async res => {
|
.then(async res => {
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
const text = await res.text();
|
const text = await res.text();
|
||||||
console.error('Report API error:', res.status, text);
|
logger.error('ConceptTimelineModal', 'fetchTimelineData - Report API', new Error(`HTTP ${res.status}`), { conceptName, status: res.status, response: text.substring(0, 200) });
|
||||||
return { results: [] };
|
return { results: [] };
|
||||||
}
|
}
|
||||||
return res.json();
|
return res.json();
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.error('Report API error:', err);
|
logger.error('ConceptTimelineModal', 'fetchTimelineData - Report API', err, { conceptName });
|
||||||
return { results: [] };
|
return { results: [] };
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -296,15 +297,12 @@ const ConceptTimelineModal = ({
|
|||||||
|
|
||||||
|
|
||||||
setTimelineData(timeline);
|
setTimelineData(timeline);
|
||||||
|
logger.info('ConceptTimelineModal', '时间轴数据加载成功', { conceptId, conceptName, timelineCount: timeline.length });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取时间轴数据失败:', error);
|
logger.error('ConceptTimelineModal', 'fetchTimelineData', error, { conceptId, conceptName });
|
||||||
toast({
|
|
||||||
title: '获取数据失败',
|
// ❌ 移除获取数据失败toast
|
||||||
description: error.message,
|
// toast({ title: '获取数据失败', description: error.message, status: 'error', duration: 3000, isClosable: true });
|
||||||
status: 'error',
|
|
||||||
duration: 3000,
|
|
||||||
isClosable: true,
|
|
||||||
});
|
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { logger } from '../../../utils/logger';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
SimpleGrid,
|
SimpleGrid,
|
||||||
@@ -96,7 +97,7 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
|
|||||||
throw new Error(`Concept API错误: ${response.status}`);
|
throw new Error(`Concept API错误: ${response.status}`);
|
||||||
}
|
}
|
||||||
} catch (conceptApiError) {
|
} catch (conceptApiError) {
|
||||||
console.warn('concept-api路由失败,尝试直接访问:', conceptApiError.message);
|
logger.warn('ConceptStatsPanel', 'concept-api路由失败,尝试直接访问', { error: conceptApiError.message, days, startDate, endDate });
|
||||||
|
|
||||||
// 备用方案:直接访问concept_api服务(开发环境回退)
|
// 备用方案:直接访问concept_api服务(开发环境回退)
|
||||||
try {
|
try {
|
||||||
@@ -105,6 +106,7 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
|
|||||||
result = await response.json();
|
result = await response.json();
|
||||||
if (result.success && result.data) {
|
if (result.success && result.data) {
|
||||||
setStatsData(result.data);
|
setStatsData(result.data);
|
||||||
|
logger.info('ConceptStatsPanel', '统计数据加载成功(备用API)', { dataKeys: Object.keys(result.data) });
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
throw new Error(result.note || '直接API返回错误');
|
throw new Error(result.note || '直接API返回错误');
|
||||||
@@ -113,20 +115,16 @@ const ConceptStatsPanel = ({ apiBaseUrl, onConceptClick }) => {
|
|||||||
throw new Error(`直接API错误: ${response.status}`);
|
throw new Error(`直接API错误: ${response.status}`);
|
||||||
}
|
}
|
||||||
} catch (directError) {
|
} catch (directError) {
|
||||||
console.error('所有API都失败:', directError);
|
logger.error('ConceptStatsPanel', '所有API都失败', directError, { days, startDate, endDate });
|
||||||
throw new Error('无法访问概念统计API');
|
throw new Error('无法访问概念统计API');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取统计数据失败:', error);
|
logger.error('ConceptStatsPanel', 'fetchStatsData', error, { days, startDate, endDate });
|
||||||
toast({
|
|
||||||
title: '获取统计数据失败',
|
// ❌ 移除获取统计数据失败toast
|
||||||
description: '正在使用默认数据,请稍后刷新重试',
|
// toast({ title: '获取统计数据失败', description: '正在使用默认数据,请稍后刷新重试', status: 'warning', duration: 3000, isClosable: true });
|
||||||
status: 'warning',
|
|
||||||
duration: 3000,
|
|
||||||
isClosable: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
// 使用简化的默认数据作为最后的fallback
|
// 使用简化的默认数据作为最后的fallback
|
||||||
setStatsData({
|
setStatsData({
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import React, { useState, useEffect, useCallback } from 'react';
|
import React, { useState, useEffect, useCallback } from 'react';
|
||||||
import { useSearchParams, useNavigate } from 'react-router-dom';
|
import { useSearchParams, useNavigate } from 'react-router-dom';
|
||||||
|
import { logger } from '../../utils/logger';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Container,
|
Container,
|
||||||
@@ -175,7 +176,7 @@ const ConceptCenter = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取最新交易日期失败:', error);
|
logger.error('ConceptCenter', 'fetchLatestTradeDate', error);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}, []);
|
}, []);
|
||||||
@@ -286,17 +287,14 @@ const ConceptCenter = () => {
|
|||||||
setSelectedDate(new Date(data.price_date));
|
setSelectedDate(new Date(data.price_date));
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast({
|
logger.error('ConceptCenter', 'fetchConcepts', error, { query, page, date: date?.toISOString(), sortToUse });
|
||||||
title: '获取数据失败',
|
|
||||||
description: error.message,
|
// ❌ 移除获取数据失败toast
|
||||||
status: 'error',
|
// toast({ title: '获取数据失败', description: error.message, status: 'error', duration: 3000, isClosable: true });
|
||||||
duration: 3000,
|
|
||||||
isClosable: true,
|
|
||||||
});
|
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}, [pageSize, sortBy, toast]);
|
}, [pageSize, sortBy]);
|
||||||
|
|
||||||
// 清除搜索
|
// 清除搜索
|
||||||
const handleClearSearch = () => {
|
const handleClearSearch = () => {
|
||||||
@@ -401,7 +399,7 @@ const ConceptCenter = () => {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn(`获取股票 ${seccode} 行情数据失败:`, error);
|
logger.warn('ConceptCenter', `获取股票行情数据失败`, { stockCode: seccode, error: error.message });
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
@@ -413,10 +411,11 @@ const ConceptCenter = () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
setStockMarketData(newMarketData);
|
setStockMarketData(newMarketData);
|
||||||
|
logger.info('ConceptCenter', '股票行情数据批量加载完成', { totalStocks: stocks.length, loadedCount: Object.keys(newMarketData).length });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('批量获取股票行情数据失败:', error);
|
logger.error('ConceptCenter', 'fetchStockMarketData', error, { stockCount: stocks?.length });
|
||||||
} finally {
|
} finally {
|
||||||
setLoadingStockData(false);
|
setLoadingStockData(false);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user