Initial commit
This commit is contained in:
186
src/views/Community/components/PopularKeywords.js
Normal file
186
src/views/Community/components/PopularKeywords.js
Normal file
@@ -0,0 +1,186 @@
|
||||
// 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;
|
||||
Reference in New Issue
Block a user