From a39d57f9decc17bd3836b7c8430afa93d3e93da1 Mon Sep 17 00:00:00 2001 From: zdl <3489966805@qq.com> Date: Thu, 30 Oct 2025 12:14:27 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=88=9B=E5=BB=BA=E5=8E=9F=E5=AD=90?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=EF=BC=88Atoms=EF=BC=89=20-=20EventTimeline:?= =?UTF-8?q?=20=E6=97=B6=E9=97=B4=E8=BD=B4=E6=98=BE=E7=A4=BA=EF=BC=8860?= =?UTF-8?q?=E8=A1=8C=EF=BC=89=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=E2=94=82=20?= =?UTF-8?q?=E2=94=82=20=E2=94=82=20=E2=94=82=20-=20EventImportanceBadge:?= =?UTF-8?q?=20=E9=87=8D=E8=A6=81=E6=80=A7=E7=AD=89=E7=BA=A7=E6=A0=87?= =?UTF-8?q?=E7=AD=BE=EF=BC=88100=E8=A1=8C=EF=BC=89=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=E2=94=82=20=E2=94=82=20?= =?UTF-8?q?=E2=94=82=20=E2=94=82=20-=20EventStats:=20=E7=BB=9F=E8=AE=A1?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=E7=BB=84=E4=BB=B6=EF=BC=8860=E8=A1=8C?= =?UTF-8?q?=EF=BC=89=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=E2=94=82=20=E2=94=82?= =?UTF-8?q?=20=E2=94=82=20=E2=94=82=20-=20EventFollowButton:=20=E5=85=B3?= =?UTF-8?q?=E6=B3=A8=E6=8C=89=E9=92=AE=EF=BC=8840=E8=A1=8C=EF=BC=89=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=E2=94=82=20=E2=94=82=20=E2=94=82=20=E2=94=82?= =?UTF-8?q?=20-=20EventPriceDisplay:=20=E4=BB=B7=E6=A0=BC=E5=8F=98?= =?UTF-8?q?=E5=8A=A8=E6=98=BE=E7=A4=BA=EF=BC=88130=E8=A1=8C=EF=BC=89=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=E2=94=82=20=E2=94=82=20=E2=94=82=20=E2=94=82=20-=20EventDes?= =?UTF-8?q?cription:=20=E6=8F=8F=E8=BF=B0=E6=96=87=E6=9C=AC=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=EF=BC=8860=E8=A1=8C=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/EventCard/EventDescription.js | 56 ++++++++ .../components/EventCard/EventFollowButton.js | 43 +++++++ .../EventCard/EventImportanceBadge.js | 96 ++++++++++++++ .../components/EventCard/EventPriceDisplay.js | 121 ++++++++++++++++++ .../components/EventCard/EventStats.js | 58 +++++++++ .../components/EventCard/EventTimeline.js | 62 +++++++++ 6 files changed, 436 insertions(+) create mode 100644 src/views/Community/components/EventCard/EventDescription.js create mode 100644 src/views/Community/components/EventCard/EventFollowButton.js create mode 100644 src/views/Community/components/EventCard/EventImportanceBadge.js create mode 100644 src/views/Community/components/EventCard/EventPriceDisplay.js create mode 100644 src/views/Community/components/EventCard/EventStats.js create mode 100644 src/views/Community/components/EventCard/EventTimeline.js diff --git a/src/views/Community/components/EventCard/EventDescription.js b/src/views/Community/components/EventCard/EventDescription.js new file mode 100644 index 00000000..0554bfe0 --- /dev/null +++ b/src/views/Community/components/EventCard/EventDescription.js @@ -0,0 +1,56 @@ +// src/views/Community/components/EventCard/EventDescription.js +import React, { useState } from 'react'; +import { Box, Text, Button } from '@chakra-ui/react'; + +/** + * 事件描述组件(支持展开/收起) + * @param {Object} props + * @param {string} props.description - 描述文本 + * @param {string} props.textColor - 文字颜色 + * @param {number} props.minLength - 触发展开/收起的最小长度(默认 120) + * @param {number} props.noOfLines - 未展开时显示的行数(默认 3) + */ +const EventDescription = ({ + description, + textColor, + minLength = 120, + noOfLines = 3 +}) => { + const [isExpanded, setIsExpanded] = useState(false); + + // 如果没有描述,不渲染 + if (!description) { + return null; + } + + const handleToggle = (e) => { + e.stopPropagation(); + setIsExpanded(!isExpanded); + }; + + return ( + + + {description} + + {description.length > minLength && ( + + )} + + ); +}; + +export default EventDescription; diff --git a/src/views/Community/components/EventCard/EventFollowButton.js b/src/views/Community/components/EventCard/EventFollowButton.js new file mode 100644 index 00000000..9b7a3e3c --- /dev/null +++ b/src/views/Community/components/EventCard/EventFollowButton.js @@ -0,0 +1,43 @@ +// src/views/Community/components/EventCard/EventFollowButton.js +import React from 'react'; +import { Button } from '@chakra-ui/react'; +import { StarIcon } from '@chakra-ui/icons'; + +/** + * 事件关注按钮组件 + * @param {Object} props + * @param {boolean} props.isFollowing - 是否已关注 + * @param {number} props.followerCount - 关注数 + * @param {Function} props.onToggle - 切换关注状态的回调函数 + * @param {string} props.size - 按钮尺寸('xs' | 'sm' | 'md',默认 'sm') + * @param {boolean} props.showCount - 是否显示关注数(默认 true) + */ +const EventFollowButton = ({ + isFollowing, + followerCount = 0, + onToggle, + size = 'sm', + showCount = true +}) => { + const iconSize = size === 'xs' ? '10px' : '12px'; + + const handleClick = (e) => { + e.stopPropagation(); + onToggle?.(); + }; + + return ( + + ); +}; + +export default EventFollowButton; diff --git a/src/views/Community/components/EventCard/EventImportanceBadge.js b/src/views/Community/components/EventCard/EventImportanceBadge.js new file mode 100644 index 00000000..7470f795 --- /dev/null +++ b/src/views/Community/components/EventCard/EventImportanceBadge.js @@ -0,0 +1,96 @@ +// src/views/Community/components/EventCard/EventImportanceBadge.js +import React from 'react'; +import { Badge, Tooltip, VStack, HStack, Text, Divider, Circle } from '@chakra-ui/react'; +import { InfoIcon } from '@chakra-ui/icons'; +import { getImportanceConfig, getAllImportanceLevels } from '../../../../constants/importanceLevels'; + +/** + * 事件重要性等级标签组件 + * @param {Object} props + * @param {string} props.importance - 重要性等级(S/A/B/C/D) + * @param {boolean} props.showTooltip - 是否显示详细提示框(默认 false) + * @param {boolean} props.showIcon - 是否显示信息图标(默认 false) + * @param {string} props.size - 标签大小(xs/sm/md/lg,默认 xs) + */ +const EventImportanceBadge = ({ + importance, + showTooltip = false, + showIcon = false, + size = 'xs' +}) => { + const importanceConfig = getImportanceConfig(importance); + + // 简单模式:只显示标签 + if (!showTooltip) { + return ( + + {importance || 'C'}级 + + ); + } + + // 详细模式:带提示框的标签 + return ( + + + 重要性等级说明 + + + {getAllImportanceLevels().map((level) => ( + + + + {level.level}级 + {level.description} + + + ))} + + } + placement="top" + hasArrow + bg="white" + color="gray.800" + fontSize="md" + p={3} + borderRadius="lg" + borderWidth="1px" + borderColor="gray.200" + boxShadow="lg" + > + + {showIcon && } + {importance || 'C'}级 + + + ); +}; + +export default EventImportanceBadge; diff --git a/src/views/Community/components/EventCard/EventPriceDisplay.js b/src/views/Community/components/EventCard/EventPriceDisplay.js new file mode 100644 index 00000000..54371cec --- /dev/null +++ b/src/views/Community/components/EventCard/EventPriceDisplay.js @@ -0,0 +1,121 @@ +// src/views/Community/components/EventCard/EventPriceDisplay.js +import React from 'react'; +import { HStack, Badge, Text, Tooltip } from '@chakra-ui/react'; +import { PriceArrow } from '../../../../utils/priceFormatters'; + +/** + * 事件价格变动显示组件 + * @param {Object} props + * @param {number|null} props.avgChange - 平均涨跌幅 + * @param {number|null} props.maxChange - 最大涨跌幅 + * @param {number|null} props.weekChange - 周涨跌幅 + * @param {boolean} props.compact - 是否为紧凑模式(只显示平均值,默认 false) + * @param {boolean} props.inline - 是否内联显示(默认 false) + */ +const EventPriceDisplay = ({ + avgChange, + maxChange, + weekChange, + compact = false, + inline = false +}) => { + // 获取颜色方案 + const getColorScheme = (value) => { + if (value == null) return 'gray'; + return value > 0 ? 'red' : value < 0 ? 'green' : 'gray'; + }; + + // 格式化百分比 + const formatPercent = (value) => { + if (value == null) return '--'; + return `${value > 0 ? '+' : ''}${value.toFixed(2)}%`; + }; + + // 紧凑模式:只显示平均值,内联在标题后 + if (compact && avgChange != null) { + return ( + + + + {formatPercent(avgChange)} + + + ); + } + + // 详细模式:显示所有价格变动 + return ( + + {/* 平均涨幅 - 始终显示,无数据时显示 -- */} + + + 平均 + + {formatPercent(avgChange)} + + + + + {/* 最大涨幅 - 始终显示,无数据时显示 -- */} + + + 最大 + + {formatPercent(maxChange)} + + + + + {/* 周涨幅 - 始终显示,无数据时显示 -- */} + + + + {weekChange != null && } + + {formatPercent(weekChange)} + + + + + ); +}; + +export default EventPriceDisplay; diff --git a/src/views/Community/components/EventCard/EventStats.js b/src/views/Community/components/EventCard/EventStats.js new file mode 100644 index 00000000..f97cdce4 --- /dev/null +++ b/src/views/Community/components/EventCard/EventStats.js @@ -0,0 +1,58 @@ +// src/views/Community/components/EventCard/EventStats.js +import React from 'react'; +import { HStack, Text, Tooltip } from '@chakra-ui/react'; +import { ViewIcon, ChatIcon, StarIcon } from '@chakra-ui/icons'; + +/** + * 事件统计信息组件(浏览量、帖子数、关注数) + * @param {Object} props + * @param {number} props.viewCount - 浏览量 + * @param {number} props.postCount - 帖子数 + * @param {number} props.followerCount - 关注数 + * @param {string} props.size - 尺寸('sm' | 'md',默认 'sm') + * @param {number} props.spacing - 间距(默认 3) + * @param {Object} props.display - 响应式显示控制(默认 { base: 'none', md: 'flex' }) + * @param {string} props.mutedColor - 文字颜色(可选) + */ +const EventStats = ({ + viewCount = 0, + postCount = 0, + followerCount = 0, + size = 'sm', + spacing = 3, + display = { base: 'none', md: 'flex' }, + mutedColor +}) => { + const fontSize = size === 'sm' ? 'xs' : 'sm'; + const iconSize = size === 'sm' ? '12px' : '16px'; + + return ( + + {/* 浏览量 */} + + + + {viewCount} + + + + {/* 帖子数 */} + + + + {postCount} + + + + {/* 关注数 */} + + + + {followerCount} + + + + ); +}; + +export default EventStats; diff --git a/src/views/Community/components/EventCard/EventTimeline.js b/src/views/Community/components/EventCard/EventTimeline.js new file mode 100644 index 00000000..d2bfdce0 --- /dev/null +++ b/src/views/Community/components/EventCard/EventTimeline.js @@ -0,0 +1,62 @@ +// src/views/Community/components/EventCard/EventTimeline.js +import React from 'react'; +import { Box, VStack, Text, useColorModeValue } from '@chakra-ui/react'; +import moment from 'moment'; + +/** + * 事件时间轴组件 + * @param {Object} props + * @param {string} props.createdAt - 事件创建时间 + * @param {Object} props.timelineStyle - 时间轴样式配置 + * @param {string} props.borderColor - 竖线边框颜色 + * @param {string} props.minHeight - 竖线最小高度(例如:'40px' 或 '80px') + */ +const EventTimeline = ({ createdAt, timelineStyle, borderColor, minHeight = '40px' }) => { + return ( + + {/* 时间长方形卡片 */} + + {/* 日期 YYYY-MM-DD */} + + {moment(createdAt).format('YYYY-MM-DD')} + + {/* 时间 HH:mm */} + + {moment(createdAt).format('HH:mm')} + + + {/* 时间轴竖线 */} + + + ); +}; + +export default EventTimeline;