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:
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -1,5 +0,0 @@
|
||||
// src/views/Community/components/HotEvents/index.js
|
||||
// 热点事件模块
|
||||
|
||||
export { default as HotEvents } from './HotEvents';
|
||||
export { default as HotEventsSection } from './HotEventsSection';
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user