refactor(Community): 移除热门事件模块,搜索框添加金色边框

- 删除 HotEvents 目录及相关组件(HotEvents.js, HotEventsSection.js)
- Community/index.js 移除 HotEventsSection 引用和 fetchHotEvents 调用
- CompactSearchBox 搜索框边框改为金色(PROFESSIONAL_COLORS.gold[500])

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
zdl
2026-01-09 15:33:04 +08:00
parent 2e64581056
commit 7bb6e6c423
6 changed files with 4 additions and 559 deletions

View File

@@ -1,225 +0,0 @@
/* Hot Events Section */
.hot-events-section {
padding-bottom: 24px;
}
.section-title {
display: flex;
align-items: center;
font-size: 20px;
font-weight: 600;
margin-bottom: 4px;
}
.section-subtitle {
font-size: 14px;
color: #8c8c8c;
margin-bottom: 24px;
}
/* Carousel */
.carousel-wrapper {
position: relative;
}
.carousel-counter {
position: absolute;
top: 8px; /* 容器内部顶部 */
right: 48px; /* 避开右侧箭头 */
z-index: 100; /* 确保在卡片和箭头上方 */
background: rgba(24, 144, 255, 0.95);
color: white;
font-size: 13px;
font-weight: 600;
padding: 4px 10px;
border-radius: 12px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
pointer-events: none; /* 不阻挡鼠标事件 */
}
.hot-events-carousel {
padding: 0; /* 移除左右padding箭头使用绝对定位 */
position: relative;
}
.hot-events-carousel .carousel-item {
padding: 0 8px;
}
/* 自定义箭头样式 */
.custom-carousel-arrow {
width: 40px !important;
height: 40px !important;
background: rgba(255, 255, 255, 0.9) !important;
border-radius: 50% !important;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15) !important;
transition: all 0.3s ease !important;
z-index: 10 !important;
user-select: none !important; /* 防止连续点击时选中文本 */
}
.custom-carousel-arrow:hover {
background: rgba(255, 255, 255, 1) !important;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2) !important;
}
.custom-carousel-arrow:hover .anticon {
color: #096dd9 !important;
}
/* 箭头位置 - 绝对定位,悬浮在卡片边缘 */
.hot-events-carousel .slick-prev.custom-carousel-arrow {
left: 8px !important;
position: absolute;
}
.hot-events-carousel .slick-next.custom-carousel-arrow {
right: 8px !important;
position: absolute;
}
/* 隐藏可能重复的默认箭头 */
.hot-events-carousel .slick-arrow:not(.custom-carousel-arrow) {
display: none !important;
}
/* 禁用状态 */
.custom-carousel-arrow.slick-disabled {
opacity: 0.3 !important;
cursor: not-allowed !important;
}
/* Card */
.hot-event-card {
border-radius: 8px;
overflow: hidden;
transition: transform 0.2s ease, box-shadow 0.2s ease;
margin: 0 auto;
}
.hot-event-card:hover {
transform: translateY(-4px);
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.08);
}
/* Card body padding */
.hot-event-card .ant-card-body {
padding: 12px;
}
/* Cover image - 高度减半 */
.event-cover {
position: relative;
width: 100%;
height: 80px;
overflow: hidden;
}
.event-cover img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
/* 重要度徽章 - 长方形圆角 */
.importance-badge {
position: absolute;
top: 8px;
left: 8px;
padding: 3px 10px;
border-radius: 10px;
font-size: 12px;
font-weight: 600;
color: #fff;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.25);
}
/* S级 - 深红 */
.importance-s {
background: #c0392b;
}
/* A级 - 浅红 */
.importance-a {
background: #e74c3c;
}
/* B级 - 深橙 */
.importance-b {
background: #d35400;
}
/* C级 - 浅橙 */
.importance-c {
background: #f39c12;
}
/* Card content */
.event-header {
font-size: 16px;
font-weight: 600;
color: #000;
margin-bottom: 8px;
line-height: 1.4;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
word-break: break-word;
}
/* 标题文字 */
.event-title {
cursor: pointer;
}
/* 涨幅标签 - 底部显示 */
.event-tag .ant-tag {
font-size: 12px;
padding: 0 8px;
height: 20px;
line-height: 20px;
margin: 0;
}
/* 详情描述 - 三行省略 */
.event-description {
margin: 8px 0;
font-size: 14px;
color: #595959;
line-height: 1.5;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
max-height: 4.5em;
cursor: pointer;
}
.event-footer {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 12px;
color: #8c8c8c;
margin-top: 8px;
}
/* 时间样式 - 年月日高亮 */
.time {
white-space: nowrap;
display: flex;
align-items: center;
gap: 4px;
}
.time-date {
color: #1890ff;
font-weight: 600;
}
.time-hour {
color: #8c8c8c;
}

View File

@@ -1,219 +0,0 @@
// src/views/Community/components/HotEvents/HotEvents.js
import React, { useState } from 'react';
import { Card, Badge, Tag, Empty, Carousel, Tooltip } from 'antd';
import { ArrowUpOutlined, ArrowDownOutlined, LeftOutlined, RightOutlined } from '@ant-design/icons';
import { useDisclosure, useBreakpointValue } from '@chakra-ui/react';
import EventDetailModal from '../EventDetailModal';
import dayjs from 'dayjs';
import './HotEvents.css';
import defaultEventImage from '@assets/img/default-event.jpg';
// 自定义箭头组件
const CustomArrow = ({ className, style, onClick, direction }) => {
const Icon = direction === 'left' ? LeftOutlined : RightOutlined;
return (
<div
className={`${className} custom-carousel-arrow`}
style={{
...style,
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}
onClick={onClick}
>
<Icon style={{ fontSize: '20px', color: '#1890ff' }} />
</div>
);
};
const HotEvents = ({ events, onPageChange, onEventClick }) => {
const [currentSlide, setCurrentSlide] = useState(0);
const { isOpen: isModalOpen, onOpen: onModalOpen, onClose: onModalClose } = useDisclosure();
const [modalEvent, setModalEvent] = useState(null);
// H5 端不显示 Tooltip避免触摸触发后无法消除的黑色悬浮框
const isMobile = useBreakpointValue({ base: true, md: false });
const renderPriceChange = (value) => {
if (value === null || value === undefined) {
return <Tag color="default">--</Tag>;
}
const isPositive = value > 0;
const icon = isPositive ? <ArrowUpOutlined /> : <ArrowDownOutlined />;
const color = isPositive ? '#ff4d4f' : '#52c41a';
return (
<Tag color={color}>
{icon} {Math.abs(value).toFixed(2)}%
</Tag>
);
};
const getImportanceColor = (importance) => {
const colors = {
S: 'red',
A: 'orange',
B: 'blue',
C: 'green'
};
return colors[importance] || 'default';
};
const handleCardClick = (event) => {
// 🎯 追踪热点事件点击
if (onEventClick) {
onEventClick({
eventId: event.id,
eventTitle: event.title,
importance: event.importance,
source: 'hot_events_section',
timestamp: new Date().toISOString(),
});
}
setModalEvent(event);
onModalOpen();
};
// 计算总页数
const totalPages = Math.ceil((events?.length || 0) / 4);
// Carousel 配置
const carouselSettings = {
dots: false, // 隐藏圆点导航
infinite: true, // 始终启用无限循环,确保箭头显示
speed: 500,
slidesToShow: 4,
slidesToScroll: 1,
arrows: true, // 保留左右箭头
prevArrow: <CustomArrow direction="left" />,
nextArrow: <CustomArrow direction="right" />,
autoplay: false,
// 触控板/触摸优化
swipeToSlide: true, // 允许滑动到任意位置
touchThreshold: 10, // 滑动灵敏度
swipe: true, // 启用滑动
draggable: true, // PC 端拖拽支持
useCSS: true, // CSS 动画更流畅
cssEase: 'ease-out', // 滑动缓动效果
beforeChange: (_current, next) => {
// 计算实际页码(考虑无限循环)
const actualPage = next % totalPages;
setCurrentSlide(actualPage);
// 通知父组件页码变化
if (onPageChange) {
onPageChange(actualPage + 1, totalPages);
}
},
responsive: [
{
breakpoint: 1200,
settings: {
slidesToShow: 3,
slidesToScroll: 1,
}
},
{
breakpoint: 992,
settings: {
slidesToShow: 2,
slidesToScroll: 1,
}
},
{
breakpoint: 576,
settings: {
slidesToShow: 1,
slidesToScroll: 1,
}
}
]
};
return (
<div className="hot-events-section">
{events && events.length > 0 ? (
<Carousel {...carouselSettings} className="hot-events-carousel">
{events.map((event, index) => (
<div key={event.id} className="carousel-item">
<Card
hoverable
className="hot-event-card"
onClick={() => handleCardClick(event)}
cover={
<div className="event-cover">
<img
alt={event.title}
src={`/images/events/${['first', 'second', 'third', 'fourth'][index] || 'first'}.jpg`}
onError={e => {
e.target.onerror = null;
e.target.src = defaultEventImage;
}}
/>
{event.importance && (
<span className={`importance-badge importance-${event.importance.toLowerCase()}`}>
{event.importance}
</span>
)}
</div>
}
>
{/* Custom layout without Card.Meta */}
<div className="event-header">
{isMobile ? (
<span className="event-title">
{event.title}
</span>
) : (
<Tooltip title={event.title}>
<span className="event-title">
{event.title}
</span>
</Tooltip>
)}
</div>
{isMobile ? (
<div className="event-description">
{event.description}
</div>
) : (
<Tooltip title={event.description}>
<div className="event-description">
{event.description}
</div>
</Tooltip>
)}
<div className="event-footer">
<span className="event-tag">
{renderPriceChange(event.related_avg_chg)}
</span>
<span className="time">
<span className="time-date">{dayjs(event.created_at).format('YYYY-MM-DD')}</span>
{' '}
<span className="time-hour">{dayjs(event.created_at).format('HH:mm')}</span>
</span>
</div>
</Card>
</div>
))}
</Carousel>
) : (
<Card>
<Empty description="暂无热点信息" />
</Card>
)}
{/* 事件详情弹窗 */}
<EventDetailModal
open={isModalOpen}
onClose={onModalClose}
event={modalEvent}
/>
</div>
);
};
export default HotEvents;

View File

@@ -1,97 +0,0 @@
// src/views/Community/components/HotEvents/HotEventsSection.js
// 热点事件区域组件
import React, { useState } from 'react';
import {
Card,
CardHeader,
CardBody,
Heading,
Badge,
Box,
useColorModeValue
} from '@chakra-ui/react';
import HotEvents from './HotEvents';
import { PROFESSIONAL_COLORS } from '@constants/professionalTheme';
/**
* 热点事件区域组件
* @param {Array} events - 热点事件列表
* @param {Function} onEventClick - 事件点击追踪回调
*/
const HotEventsSection = ({ events, onEventClick }) => {
const cardBg = PROFESSIONAL_COLORS.background.card;
const borderColor = PROFESSIONAL_COLORS.border.default;
const [currentPage, setCurrentPage] = useState(1);
const [totalPages, setTotalPages] = useState(1);
// 处理页码变化
const handlePageChange = (page, total) => {
setCurrentPage(page);
setTotalPages(total);
};
// 如果没有热点事件,不渲染组件
if (!events || events.length === 0) {
return null;
}
return (
<Card
mt={6}
mb={8}
bg={cardBg}
boxShadow="0 4px 12px rgba(0, 0, 0, 0.3), 0 0 20px rgba(255, 195, 0, 0.1)"
borderWidth="1px"
borderColor={borderColor}
position="relative"
zIndex={1}
animation="fadeInUp 0.8s ease-out 0.4s both"
sx={{
'@keyframes fadeInUp': {
'0%': { opacity: 0, transform: 'translateY(30px)' },
'100%': { opacity: 1, transform: 'translateY(0)' }
}
}}
>
<CardHeader pb={3} display="flex" justifyContent="space-between" alignItems="flex-start">
<Box>
<Heading
size="lg"
bgGradient={PROFESSIONAL_COLORS.gradients.gold}
bgClip="text"
fontWeight="extrabold"
>
🔥 热点事件
</Heading>
<p className="section-subtitle" style={{paddingTop: '8px', color: PROFESSIONAL_COLORS.text.secondary}}>
展示最近5天内涨幅最高的事件助您把握市场热点
</p>
</Box>
{/* 页码指示器 */}
{totalPages > 1 && (
<Badge
bg={PROFESSIONAL_COLORS.gold[500]}
color="black"
fontSize="sm"
px={3}
py={1}
borderRadius="full"
ml={4}
>
{currentPage} / {totalPages}
</Badge>
)}
</CardHeader>
<CardBody py={4} px={4}>
<HotEvents
events={events}
onPageChange={handlePageChange}
onEventClick={onEventClick}
/>
</CardBody>
</Card>
);
};
export default HotEventsSection;

View File

@@ -1,5 +0,0 @@
// src/views/Community/components/HotEvents/index.js
// 热点事件模块
export { default as HotEvents } from './HotEvents';
export { default as HotEventsSection } from './HotEventsSection';

View File

@@ -496,7 +496,7 @@ const CompactSearchBox = ({
gap={isMobile ? 8 : 12}
style={{
background: 'rgba(255, 255, 255, 0.03)',
border: `1px solid ${PROFESSIONAL_COLORS.border.light}`,
border: `1px solid ${PROFESSIONAL_COLORS.gold[500]}`,
borderRadius: '24px',
padding: isMobile ? '2px 4px' : '8px 16px',
marginBottom: isMobile ? 8 : 12

View File

@@ -3,8 +3,7 @@ import React, { useEffect, useRef, lazy, Suspense, useMemo } from 'react';
import { useNavigate, useLocation, useSearchParams } from 'react-router-dom';
import { useSelector, useDispatch } from 'react-redux';
import {
fetchPopularKeywords,
fetchHotEvents
fetchPopularKeywords
} from '@/store/slices/communityDataSlice';
import {
Box,
@@ -13,7 +12,6 @@ import {
// 导入组件
import DynamicNewsCard from './components/DynamicNews/DynamicNewsCard';
import HotEventsSection from './components/HotEvents/HotEventsSection';
// ⚡ HeroPanel 懒加载:包含 ECharts (~600KB),首屏不需要立即渲染
const HeroPanel = lazy(() => import('./components/HeroPanel'));
@@ -42,7 +40,7 @@ const Community = () => {
}, [searchParams]);
// Redux状态
const { popularKeywords, hotEvents } = useSelector(state => state.communityData);
const { popularKeywords } = useSelector(state => state.communityData);
// 专业配色 - 深色主题
const bgColor = PROFESSIONAL_COLORS.background.primary;
@@ -66,10 +64,9 @@ const Community = () => {
const { events, pagination, loading, lastUpdateTime } = useEventData(filters);
// 加载热门关键词和热点事件(动态新闻由 DynamicNewsCard 内部管理)
// 加载热门关键词(动态新闻由 DynamicNewsCard 内部管理)
useEffect(() => {
dispatch(fetchPopularKeywords());
dispatch(fetchHotEvents());
}, [dispatch]);
// ⚡ 页面卸载前刷新待发送的 PostHog 事件(性能优化)
@@ -183,12 +180,6 @@ const Community = () => {
trackRelatedStockClicked: communityEvents.trackRelatedStockClicked,
}}
/>
{/* 热点事件区域 - 移至底部 */}
<HotEventsSection
events={hotEvents}
onEventClick={communityEvents.trackNewsArticleClicked}
/>
</Box>
</Box>
);