Files
vf_react/src/views/Community/components/PopularKeywords.js

202 lines
7.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// src/views/Community/components/PopularKeywords.js
import React, { useState, useEffect } from 'react';
import { Tag, Space, Button } from 'antd';
import { useNavigate } from 'react-router-dom';
import { RightOutlined } from '@ant-design/icons';
import { logger } from '../../../utils/logger';
// 使用相对路径,让 MSW 在开发环境可以拦截请求
const API_BASE_URL = '/concept-api';
// 获取域名前缀
const DOMAIN_PREFIX = process.env.NODE_ENV === 'production'
? ''
: 'https://valuefrontier.cn';
const PopularKeywords = ({ onKeywordClick, keywords: propKeywords }) => {
const [keywords, setKeywords] = useState([]);
const navigate = useNavigate();
// 加载热门概念涨幅前20
const loadPopularConcepts = async () => {
try {
const response = await fetch(`${API_BASE_URL}/search`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
query: '', // 空查询获取所有概念
size: 20, // 获取前20个
page: 1,
sort_by: 'change_pct' // 按涨跌幅排序
})
});
const data = await response.json();
if (data.results) {
// 转换数据格式
const formattedData = data.results.map(item => ({
keyword: item.concept,
count: item.stock_count,
change_pct: item.price_info?.avg_change_pct || 0,
concept_id: item.concept_id
}));
setKeywords(formattedData);
logger.debug('PopularKeywords', '热门概念加载成功(自己请求)', {
count: formattedData.length
});
}
} catch (error) {
logger.error('PopularKeywords', 'loadPopularConcepts', error);
setKeywords([]);
}
};
// 处理从父组件传入的数据
useEffect(() => {
if (propKeywords && propKeywords.length > 0) {
// 使用父组件传入的数据
setKeywords(propKeywords);
logger.debug('PopularKeywords', '使用父组件传入的数据', {
count: propKeywords.length
});
} else {
// 没有 prop 数据,自己加载
loadPopularConcepts();
}
}, [propKeywords]);
// 根据涨跌幅获取标签颜色
const getTagColor = (changePct) => {
if (changePct > 5) return 'red';
if (changePct > 3) return 'volcano';
if (changePct > 1) return 'orange';
if (changePct > 0) return 'gold';
if (changePct === 0) return 'default';
if (changePct > -1) return 'lime';
if (changePct > -3) return 'green';
if (changePct > -5) return 'cyan';
return 'blue';
};
// 格式化涨跌幅显示
const formatChangePct = (pct) => {
if (pct === null || pct === undefined) return '';
const formatted = pct.toFixed(2);
if (pct > 0) return `+${formatted}%`;
return `${formatted}%`;
};
// ✅ 修复:处理概念标签点击
const handleConceptClick = (concept) => {
// 优先调用父组件传入的回调(用于搜索框显示和触发搜索)
if (onKeywordClick) {
onKeywordClick(concept.keyword);
logger.debug('PopularKeywords', '调用 onKeywordClick 回调', {
keyword: concept.keyword
});
} else {
// 如果没有回调,则跳转到对应概念的页面(原有行为)
const url = `${DOMAIN_PREFIX}/htmls/${encodeURIComponent(concept.keyword)}.html`;
window.open(url, '_blank');
logger.debug('PopularKeywords', '跳转到概念页面', {
keyword: concept.keyword,
url
});
}
};
// 处理"更多概念"按钮点击 - 跳转到概念中心
const handleMoreClick = () => {
navigate('/concepts');
};
return (
<>
{keywords && keywords.length > 0 && (
<div style={{ position: 'relative' }}>
<Space
size={[6, 6]}
wrap
style={{
alignItems: 'center',
maxHeight: '29px', // 约两行的高度 (每行约28-30px)
overflow: 'hidden',
paddingRight: '90px' // 为右侧按钮留出空间
}}
>
{/* 标题 */}
<span style={{
color: '#ff4d4f',
fontSize: 13,
fontWeight: 500,
marginRight: 4
}}>
热门概念:
</span>
{/* 所有标签 */}
{keywords.map((item, index) => (
<Tag
key={item.concept_id || `keyword-${index}`}
color={getTagColor(item.change_pct)}
style={{
cursor: 'pointer',
padding: '1px 6px',
fontSize: 12,
transition: 'all 0.3s',
margin: 0
}}
onClick={() => handleConceptClick(item)}
onMouseEnter={(e) => {
e.currentTarget.style.transform = 'scale(1.05)';
e.currentTarget.style.boxShadow = '0 2px 8px rgba(0,0,0,0.15)';
}}
onMouseLeave={(e) => {
e.currentTarget.style.transform = 'scale(1)';
e.currentTarget.style.boxShadow = 'none';
}}
>
<span>{item.keyword}</span>
<span style={{
marginLeft: 4,
fontWeight: 'bold'
}}>
{formatChangePct(item.change_pct)}
</span>
{/* <span style={{
marginLeft: 3,
fontSize: 10,
opacity: 0.75
}}> */}
{/* ({item.count}股) */}
{/* </span> */}
</Tag>
))}
</Space>
{/* 更多概念按钮 - 固定在第二行右侧 */}
<Button
type="link"
size="small"
onClick={handleMoreClick}
style={{
position: 'absolute',
top: 0,
right: 0,
fontSize: 12,
padding: '0 4px',
height: 'auto'
}}
>
更多概念 <RightOutlined style={{ fontSize: 10 }} />
</Button>
</div>
)}
</>
);
};
export default PopularKeywords;