Initial commit

This commit is contained in:
2025-10-11 11:55:25 +08:00
parent 467dad8449
commit 8107dee8d3
2879 changed files with 610575 additions and 0 deletions

View 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;