Files
vf_react/src/views/Community/components/PopularKeywords.js
2025-10-11 12:02:01 +08:00

186 lines
7.0 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 { Card, Tag, Space, Spin, Empty, Button } from 'antd';
import { FireOutlined, RightOutlined } from '@ant-design/icons';
const API_BASE_URL = process.env.NODE_ENV === 'production'
? '/concept-api'
: 'http://192.168.1.58:6801';
// 获取域名前缀
const DOMAIN_PREFIX = process.env.NODE_ENV === 'production'
? ''
: 'https://valuefrontier.cn';
const PopularKeywords = ({ onKeywordClick }) => {
const [keywords, setKeywords] = useState([]);
const [loading, setLoading] = useState(false);
// 加载热门概念涨幅前20
const loadPopularConcepts = async () => {
setLoading(true);
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);
}
} catch (error) {
console.error('Failed to load popular concepts:', error);
setKeywords([]);
} finally {
setLoading(false);
}
};
// 组件挂载时加载数据
useEffect(() => {
loadPopularConcepts();
}, []);
// 根据涨跌幅获取标签颜色
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) => {
// 如果原有的 onKeywordClick 存在,可以选择是否还要调用
// onKeywordClick && onKeywordClick(concept.keyword);
// 跳转到对应概念的页面
const url = `${DOMAIN_PREFIX}/htmls/${encodeURIComponent(concept.keyword)}.html`;
window.open(url, '_blank');
};
// 查看更多概念
const handleViewMore = () => {
const url = `${DOMAIN_PREFIX}/concepts`;
window.open(url, '_blank');
};
return (
<Card
title={
<span>
<FireOutlined style={{ marginRight: 8, color: '#ff4d4f' }} />
热门概念
</span>
}
className="popular-keywords"
style={{ marginBottom: 16 }}
extra={
<span style={{ fontSize: 12, color: '#999' }}>
涨幅TOP20
</span>
}
>
<Spin spinning={loading}>
{keywords && keywords.length > 0 ? (
<>
<Space size={[8, 8]} wrap style={{ marginBottom: 16 }}>
{keywords.map((item) => (
<Tag
key={item.concept_id}
color={getTagColor(item.change_pct)}
style={{
cursor: 'pointer',
marginBottom: 8,
padding: '2px 8px',
transition: 'all 0.3s'
}}
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: 6,
fontWeight: 'bold'
}}>
{formatChangePct(item.change_pct)}
</span>
<span style={{
marginLeft: 4,
fontSize: 11,
opacity: 0.8
}}>
({item.count})
</span>
</Tag>
))}
</Space>
{/* 查看更多按钮 */}
<div style={{
borderTop: '1px solid #f0f0f0',
paddingTop: 12,
textAlign: 'center'
}}>
<Button
type="link"
onClick={handleViewMore}
style={{
color: '#1890ff',
fontWeight: 500
}}
>
查看更多概念
<RightOutlined style={{ fontSize: 12, marginLeft: 4 }} />
</Button>
</div>
</>
) : (
<Empty
description="暂无热门概念"
image={Empty.PRESENTED_IMAGE_SIMPLE}
/>
)}
</Spin>
</Card>
);
};
export default PopularKeywords;