更新Company页面的UI为FUI风格
This commit is contained in:
7
app.py
7
app.py
@@ -18875,5 +18875,12 @@ if __name__ == '__main__':
|
|||||||
# 初始化事件轮询机制(WebSocket 推送)
|
# 初始化事件轮询机制(WebSocket 推送)
|
||||||
initialize_event_polling()
|
initialize_event_polling()
|
||||||
|
|
||||||
|
# 启动时预热股票缓存(股票名称 + 前收盘价)
|
||||||
|
print("[启动] 正在预热股票缓存...")
|
||||||
|
try:
|
||||||
|
preload_stock_cache()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[启动] 预热缓存失败(不影响服务启动): {e}")
|
||||||
|
|
||||||
# 使用 socketio.run 替代 app.run 以支持 WebSocket
|
# 使用 socketio.run 替代 app.run 以支持 WebSocket
|
||||||
socketio.run(app, host='0.0.0.0', port=5001, debug=False, allow_unsafe_werkzeug=True)
|
socketio.run(app, host='0.0.0.0', port=5001, debug=False, allow_unsafe_werkzeug=True)
|
||||||
@@ -352,6 +352,7 @@ const DynamicNewsDetailPanel = ({ event, showHeader = true }) => {
|
|||||||
|
|
||||||
{/* 相关概念(可折叠) - 需要 PRO 权限 */}
|
{/* 相关概念(可折叠) - 需要 PRO 权限 */}
|
||||||
<RelatedConceptsSection
|
<RelatedConceptsSection
|
||||||
|
eventId={event.id}
|
||||||
eventTitle={event.title}
|
eventTitle={event.title}
|
||||||
effectiveTradingDate={event.trading_date || event.created_at}
|
effectiveTradingDate={event.trading_date || event.created_at}
|
||||||
eventTime={event.created_at}
|
eventTime={event.created_at}
|
||||||
|
|||||||
@@ -19,8 +19,9 @@ import ConceptStockItem from './ConceptStockItem';
|
|||||||
/**
|
/**
|
||||||
* 详细概念卡片组件
|
* 详细概念卡片组件
|
||||||
* @param {Object} props
|
* @param {Object} props
|
||||||
* @param {Object} props.concept - 概念对象(兼容 v1/v2 API)
|
* @param {Object} props.concept - 概念对象(兼容 v1/v2 API 和 related_concepts 表数据)
|
||||||
* - concept: 概念名称
|
* - concept: 概念名称
|
||||||
|
* - reason: 关联原因(来自 related_concepts 表)
|
||||||
* - stock_count: 相关股票数量
|
* - stock_count: 相关股票数量
|
||||||
* - score: 相关度(0-1)
|
* - score: 相关度(0-1)
|
||||||
* - price_info.avg_change_pct: 平均涨跌幅
|
* - price_info.avg_change_pct: 平均涨跌幅
|
||||||
@@ -34,6 +35,8 @@ const DetailedConceptCard = ({ concept, onClick }) => {
|
|||||||
const borderColor = useColorModeValue('gray.200', 'gray.600');
|
const borderColor = useColorModeValue('gray.200', 'gray.600');
|
||||||
const headingColor = useColorModeValue('gray.700', 'gray.200');
|
const headingColor = useColorModeValue('gray.700', 'gray.200');
|
||||||
const stockCountColor = useColorModeValue('gray.500', 'gray.400');
|
const stockCountColor = useColorModeValue('gray.500', 'gray.400');
|
||||||
|
const reasonBg = useColorModeValue('blue.50', 'blue.900');
|
||||||
|
const reasonColor = useColorModeValue('gray.700', 'gray.200');
|
||||||
|
|
||||||
// 计算相关度百分比
|
// 计算相关度百分比
|
||||||
const relevanceScore = Math.round((concept.score || 0) * 100);
|
const relevanceScore = Math.round((concept.score || 0) * 100);
|
||||||
@@ -43,6 +46,9 @@ const DetailedConceptCard = ({ concept, onClick }) => {
|
|||||||
const changeColor = changePct > 0 ? 'red' : changePct < 0 ? 'green' : 'gray';
|
const changeColor = changePct > 0 ? 'red' : changePct < 0 ? 'green' : 'gray';
|
||||||
const changeSymbol = changePct > 0 ? '+' : '';
|
const changeSymbol = changePct > 0 ? '+' : '';
|
||||||
|
|
||||||
|
// 判断是否来自数据库(有 reason 字段)
|
||||||
|
const isFromDatabase = !!concept.reason;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
bg={cardBg}
|
bg={cardBg}
|
||||||
@@ -67,17 +73,27 @@ const DetailedConceptCard = ({ concept, onClick }) => {
|
|||||||
{concept.concept}
|
{concept.concept}
|
||||||
</Text>
|
</Text>
|
||||||
<HStack spacing={2} flexWrap="wrap">
|
<HStack spacing={2} flexWrap="wrap">
|
||||||
|
{/* 数据库数据显示"AI分析"标签,搜索数据显示相关度 */}
|
||||||
|
{isFromDatabase ? (
|
||||||
|
<Badge colorScheme="green" fontSize="xs">
|
||||||
|
AI 分析
|
||||||
|
</Badge>
|
||||||
|
) : (
|
||||||
<Badge colorScheme="purple" fontSize="xs">
|
<Badge colorScheme="purple" fontSize="xs">
|
||||||
相关度: {relevanceScore}%
|
相关度: {relevanceScore}%
|
||||||
</Badge>
|
</Badge>
|
||||||
|
)}
|
||||||
|
{/* 只有搜索数据才显示股票数量 */}
|
||||||
|
{!isFromDatabase && concept.stock_count > 0 && (
|
||||||
<Badge colorScheme="orange" fontSize="xs">
|
<Badge colorScheme="orange" fontSize="xs">
|
||||||
{concept.stock_count} 只股票
|
{concept.stock_count} 只股票
|
||||||
</Badge>
|
</Badge>
|
||||||
|
)}
|
||||||
</HStack>
|
</HStack>
|
||||||
</VStack>
|
</VStack>
|
||||||
|
|
||||||
{/* 右侧:涨跌幅 */}
|
{/* 右侧:涨跌幅(仅搜索数据有) */}
|
||||||
{concept.price_info?.avg_change_pct && (
|
{!isFromDatabase && concept.price_info?.avg_change_pct && (
|
||||||
<Box textAlign="right">
|
<Box textAlign="right">
|
||||||
<Text fontSize="xs" color={stockCountColor} mb={1}>
|
<Text fontSize="xs" color={stockCountColor} mb={1}>
|
||||||
平均涨跌幅
|
平均涨跌幅
|
||||||
@@ -97,8 +113,30 @@ const DetailedConceptCard = ({ concept, onClick }) => {
|
|||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
{/* 概念描述 */}
|
{/* 关联原因(来自数据库,突出显示) */}
|
||||||
{concept.description && (
|
{concept.reason && (
|
||||||
|
<Box
|
||||||
|
bg={reasonBg}
|
||||||
|
p={3}
|
||||||
|
borderRadius="md"
|
||||||
|
borderLeft="4px solid"
|
||||||
|
borderLeftColor="blue.400"
|
||||||
|
>
|
||||||
|
<Text fontSize="xs" fontWeight="bold" color="blue.500" mb={1}>
|
||||||
|
关联原因
|
||||||
|
</Text>
|
||||||
|
<Text
|
||||||
|
fontSize="sm"
|
||||||
|
color={reasonColor}
|
||||||
|
lineHeight="1.8"
|
||||||
|
>
|
||||||
|
{concept.reason}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 概念描述(仅搜索数据有,且没有 reason 时显示) */}
|
||||||
|
{!concept.reason && concept.description && (
|
||||||
<Text
|
<Text
|
||||||
fontSize="sm"
|
fontSize="sm"
|
||||||
color={stockCountColor}
|
color={stockCountColor}
|
||||||
|
|||||||
@@ -14,10 +14,11 @@ import {
|
|||||||
/**
|
/**
|
||||||
* 简单概念卡片组件
|
* 简单概念卡片组件
|
||||||
* @param {Object} props
|
* @param {Object} props
|
||||||
* @param {Object} props.concept - 概念对象
|
* @param {Object} props.concept - 概念对象(兼容搜索数据和数据库数据)
|
||||||
* - name: 概念名称
|
* - concept: 概念名称
|
||||||
|
* - reason: 关联原因(来自数据库)
|
||||||
* - stock_count: 相关股票数量
|
* - stock_count: 相关股票数量
|
||||||
* - relevance: 相关度(0-100)
|
* - score: 相关度(0-1)
|
||||||
* @param {Function} props.onClick - 点击回调
|
* @param {Function} props.onClick - 点击回调
|
||||||
* @param {Function} props.getRelevanceColor - 获取相关度颜色的函数
|
* @param {Function} props.getRelevanceColor - 获取相关度颜色的函数
|
||||||
*/
|
*/
|
||||||
@@ -34,13 +35,16 @@ const SimpleConceptCard = ({ concept, onClick, getRelevanceColor }) => {
|
|||||||
const changeColor = changePct !== null ? (changePct > 0 ? 'red' : changePct < 0 ? 'green' : 'gray') : null;
|
const changeColor = changePct !== null ? (changePct > 0 ? 'red' : changePct < 0 ? 'green' : 'gray') : null;
|
||||||
const changeSymbol = changePct !== null && changePct > 0 ? '+' : '';
|
const changeSymbol = changePct !== null && changePct > 0 ? '+' : '';
|
||||||
|
|
||||||
|
// 判断是否来自数据库(有 reason 字段)
|
||||||
|
const isFromDatabase = !!concept.reason;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<VStack
|
<VStack
|
||||||
align="stretch"
|
align="stretch"
|
||||||
spacing={1}
|
spacing={1}
|
||||||
bg={cardBg}
|
bg={cardBg}
|
||||||
borderWidth="1px"
|
borderWidth="1px"
|
||||||
borderColor={borderColor}
|
borderColor={isFromDatabase ? 'green.300' : borderColor}
|
||||||
borderRadius="md"
|
borderRadius="md"
|
||||||
px={2}
|
px={2}
|
||||||
py={1}
|
py={1}
|
||||||
@@ -61,15 +65,23 @@ const SimpleConceptCard = ({ concept, onClick, getRelevanceColor }) => {
|
|||||||
wordBreak="break-word"
|
wordBreak="break-word"
|
||||||
lineHeight="1.4"
|
lineHeight="1.4"
|
||||||
>
|
>
|
||||||
{concept.concept}{' '}
|
{concept.concept}
|
||||||
|
{/* 只有搜索数据才显示股票数量 */}
|
||||||
|
{!isFromDatabase && concept.stock_count > 0 && (
|
||||||
<Text as="span" color="gray.500">
|
<Text as="span" color="gray.500">
|
||||||
({concept.stock_count})
|
{' '}({concept.stock_count})
|
||||||
</Text>
|
</Text>
|
||||||
|
)}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
{/* 第二行:相关度 + 涨跌幅 */}
|
{/* 第二行:标签 */}
|
||||||
<Flex justify="space-between" align="center" gap={1} flexWrap="wrap">
|
<Flex justify="space-between" align="center" gap={1} flexWrap="wrap">
|
||||||
{/* 相关度标签 */}
|
{/* 数据库数据显示"AI分析",搜索数据显示相关度 */}
|
||||||
|
{isFromDatabase ? (
|
||||||
|
<Badge colorScheme="green" fontSize="10px" px={1.5} py={0.5}>
|
||||||
|
AI 分析
|
||||||
|
</Badge>
|
||||||
|
) : (
|
||||||
<Box
|
<Box
|
||||||
bg={relevanceColors.bg}
|
bg={relevanceColors.bg}
|
||||||
color={relevanceColors.color}
|
color={relevanceColors.color}
|
||||||
@@ -82,9 +94,10 @@ const SimpleConceptCard = ({ concept, onClick, getRelevanceColor }) => {
|
|||||||
相关度: {relevanceScore}%
|
相关度: {relevanceScore}%
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* 涨跌幅数据 */}
|
{/* 涨跌幅数据(仅搜索数据有) */}
|
||||||
{changePct !== null && (
|
{!isFromDatabase && changePct !== null && (
|
||||||
<Badge
|
<Badge
|
||||||
colorScheme={changeColor}
|
colorScheme={changeColor}
|
||||||
fontSize="10px"
|
fontSize="10px"
|
||||||
|
|||||||
@@ -26,7 +26,8 @@ import { getApiBase } from '@utils/apiConfig';
|
|||||||
/**
|
/**
|
||||||
* 相关概念区组件
|
* 相关概念区组件
|
||||||
* @param {Object} props
|
* @param {Object} props
|
||||||
* @param {string} props.eventTitle - 事件标题(用于搜索概念)
|
* @param {number} props.eventId - 事件ID(用于获取 related_concepts 表数据)
|
||||||
|
* @param {string} props.eventTitle - 事件标题(备用,当 eventId 不存在时使用搜索)
|
||||||
* @param {string} props.effectiveTradingDate - 有效交易日期(涨跌幅数据日期)
|
* @param {string} props.effectiveTradingDate - 有效交易日期(涨跌幅数据日期)
|
||||||
* @param {string|Object} props.eventTime - 事件发生时间
|
* @param {string|Object} props.eventTime - 事件发生时间
|
||||||
* @param {React.ReactNode} props.subscriptionBadge - 订阅徽章组件(可选)
|
* @param {React.ReactNode} props.subscriptionBadge - 订阅徽章组件(可选)
|
||||||
@@ -34,6 +35,7 @@ import { getApiBase } from '@utils/apiConfig';
|
|||||||
* @param {Function} props.onLockedClick - 锁定时的点击回调(触发付费弹窗)
|
* @param {Function} props.onLockedClick - 锁定时的点击回调(触发付费弹窗)
|
||||||
*/
|
*/
|
||||||
const RelatedConceptsSection = ({
|
const RelatedConceptsSection = ({
|
||||||
|
eventId,
|
||||||
eventTitle,
|
eventTitle,
|
||||||
effectiveTradingDate,
|
effectiveTradingDate,
|
||||||
eventTime,
|
eventTime,
|
||||||
@@ -57,6 +59,7 @@ const RelatedConceptsSection = ({
|
|||||||
const textColor = useColorModeValue('gray.600', 'gray.400');
|
const textColor = useColorModeValue('gray.600', 'gray.400');
|
||||||
|
|
||||||
console.log('[RelatedConceptsSection] 组件渲染', {
|
console.log('[RelatedConceptsSection] 组件渲染', {
|
||||||
|
eventId,
|
||||||
eventTitle,
|
eventTitle,
|
||||||
effectiveTradingDate,
|
effectiveTradingDate,
|
||||||
eventTime,
|
eventTime,
|
||||||
@@ -65,16 +68,76 @@ const RelatedConceptsSection = ({
|
|||||||
error
|
error
|
||||||
});
|
});
|
||||||
|
|
||||||
// 搜索相关概念
|
// 获取相关概念 - 优先使用 eventId 从数据库获取
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const searchConcepts = async () => {
|
const fetchConcepts = async () => {
|
||||||
console.log('[RelatedConceptsSection] useEffect 触发', {
|
console.log('[RelatedConceptsSection] useEffect 触发', {
|
||||||
|
eventId,
|
||||||
eventTitle,
|
eventTitle,
|
||||||
effectiveTradingDate
|
effectiveTradingDate
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 优先使用 eventId 获取数据库中的相关概念
|
||||||
|
if (eventId) {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
setError(null);
|
||||||
|
|
||||||
|
const apiUrl = `${getApiBase()}/api/events/${eventId}/concepts`;
|
||||||
|
console.log('[RelatedConceptsSection] 从数据库获取相关概念', { url: apiUrl });
|
||||||
|
|
||||||
|
const response = await fetch(apiUrl, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
credentials: 'include'
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
// 如果是 403,说明需要订阅,不是错误
|
||||||
|
if (response.status === 403) {
|
||||||
|
console.log('[RelatedConceptsSection] 需要订阅才能查看');
|
||||||
|
setConcepts([]);
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
console.log('[RelatedConceptsSection] 数据库响应', data);
|
||||||
|
|
||||||
|
if (data.success && Array.isArray(data.data)) {
|
||||||
|
// 转换数据格式,使其与原有展示逻辑兼容
|
||||||
|
const formattedConcepts = data.data.map(item => ({
|
||||||
|
concept: item.concept,
|
||||||
|
reason: item.reason,
|
||||||
|
concept_code: item.concept_code,
|
||||||
|
// 保留原有字段以兼容 DetailedConceptCard
|
||||||
|
score: 1, // 数据库中的都是高相关度
|
||||||
|
description: item.reason, // reason 作为描述
|
||||||
|
stocks: [], // 暂无股票数据
|
||||||
|
stock_count: 0
|
||||||
|
}));
|
||||||
|
console.log('[RelatedConceptsSection] 设置概念数据', formattedConcepts);
|
||||||
|
setConcepts(formattedConcepts);
|
||||||
|
} else {
|
||||||
|
setConcepts([]);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('[RelatedConceptsSection] 获取概念失败', err);
|
||||||
|
logger.error('RelatedConceptsSection', 'fetchConcepts', err);
|
||||||
|
setError('加载概念数据失败');
|
||||||
|
setConcepts([]);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 降级方案:使用 eventTitle 搜索概念(兼容旧逻辑)
|
||||||
if (!eventTitle || !effectiveTradingDate) {
|
if (!eventTitle || !effectiveTradingDate) {
|
||||||
console.log('[RelatedConceptsSection] 缺少必要参数,跳过搜索', {
|
console.log('[RelatedConceptsSection] 缺少必要参数,跳过搜索', {
|
||||||
|
hasEventId: !!eventId,
|
||||||
hasEventTitle: !!eventTitle,
|
hasEventTitle: !!eventTitle,
|
||||||
hasEffectiveTradingDate: !!effectiveTradingDate
|
hasEffectiveTradingDate: !!effectiveTradingDate
|
||||||
});
|
});
|
||||||
@@ -86,19 +149,14 @@ const RelatedConceptsSection = ({
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
|
|
||||||
// 格式化交易日期 - 统一使用 moment 处理
|
// 格式化交易日期
|
||||||
let formattedTradeDate;
|
let formattedTradeDate;
|
||||||
try {
|
try {
|
||||||
// 不管传入的是什么格式,都用 moment 解析并格式化为 YYYY-MM-DD
|
|
||||||
formattedTradeDate = dayjs(effectiveTradingDate).format('YYYY-MM-DD');
|
formattedTradeDate = dayjs(effectiveTradingDate).format('YYYY-MM-DD');
|
||||||
|
|
||||||
// 验证日期是否有效
|
|
||||||
if (!dayjs(formattedTradeDate, 'YYYY-MM-DD', true).isValid()) {
|
if (!dayjs(formattedTradeDate, 'YYYY-MM-DD', true).isValid()) {
|
||||||
console.warn('[RelatedConceptsSection] 无效日期,使用当前日期');
|
|
||||||
formattedTradeDate = dayjs().format('YYYY-MM-DD');
|
formattedTradeDate = dayjs().format('YYYY-MM-DD');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('[RelatedConceptsSection] 日期格式化失败,使用当前日期', error);
|
|
||||||
formattedTradeDate = dayjs().format('YYYY-MM-DD');
|
formattedTradeDate = dayjs().format('YYYY-MM-DD');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,67 +169,37 @@ const RelatedConceptsSection = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const apiUrl = `${getApiBase()}/concept-api/search`;
|
const apiUrl = `${getApiBase()}/concept-api/search`;
|
||||||
console.log('[RelatedConceptsSection] 发送请求', {
|
console.log('[RelatedConceptsSection] 降级:使用搜索接口', { url: apiUrl, requestBody });
|
||||||
url: apiUrl,
|
|
||||||
requestBody
|
|
||||||
});
|
|
||||||
logger.debug('RelatedConceptsSection', '搜索概念', requestBody);
|
|
||||||
|
|
||||||
const response = await fetch(apiUrl, {
|
const response = await fetch(apiUrl, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: { 'Content-Type': 'application/json' },
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify(requestBody)
|
body: JSON.stringify(requestBody)
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('[RelatedConceptsSection] 响应状态', {
|
|
||||||
ok: response.ok,
|
|
||||||
status: response.status,
|
|
||||||
statusText: response.statusText
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`HTTP error! status: ${response.status}`);
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
console.log('[RelatedConceptsSection] 响应数据', {
|
|
||||||
hasResults: !!data.results,
|
|
||||||
resultsCount: data.results?.length || 0,
|
|
||||||
hasDataConcepts: !!(data.data && data.data.concepts),
|
|
||||||
data: data
|
|
||||||
});
|
|
||||||
logger.debug('RelatedConceptsSection', '概念搜索响应', {
|
|
||||||
hasResults: !!data.results,
|
|
||||||
resultsCount: data.results?.length || 0
|
|
||||||
});
|
|
||||||
|
|
||||||
// 设置概念数据
|
|
||||||
if (data.results && Array.isArray(data.results)) {
|
if (data.results && Array.isArray(data.results)) {
|
||||||
console.log('[RelatedConceptsSection] 设置概念数据 (results)', data.results);
|
|
||||||
setConcepts(data.results);
|
setConcepts(data.results);
|
||||||
} else if (data.data && data.data.concepts) {
|
} else if (data.data && data.data.concepts) {
|
||||||
// 向后兼容
|
|
||||||
console.log('[RelatedConceptsSection] 设置概念数据 (data.concepts)', data.data.concepts);
|
|
||||||
setConcepts(data.data.concepts);
|
setConcepts(data.data.concepts);
|
||||||
} else {
|
} else {
|
||||||
console.log('[RelatedConceptsSection] 没有找到概念数据,设置为空数组');
|
|
||||||
setConcepts([]);
|
setConcepts([]);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[RelatedConceptsSection] 搜索概念失败', err);
|
console.error('[RelatedConceptsSection] 搜索概念失败', err);
|
||||||
logger.error('RelatedConceptsSection', 'searchConcepts', err);
|
|
||||||
setError('加载概念数据失败');
|
setError('加载概念数据失败');
|
||||||
setConcepts([]);
|
setConcepts([]);
|
||||||
} finally {
|
} finally {
|
||||||
console.log('[RelatedConceptsSection] 加载完成');
|
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
searchConcepts();
|
fetchConcepts();
|
||||||
}, [eventTitle, effectiveTradingDate]);
|
}, [eventId, eventTitle, effectiveTradingDate]);
|
||||||
|
|
||||||
// 加载中状态
|
// 加载中状态
|
||||||
if (loading) {
|
if (loading) {
|
||||||
|
|||||||
Reference in New Issue
Block a user