From f46513ab8b07d70e70d42022e7504012e54cde87 Mon Sep 17 00:00:00 2001 From: Leo <98382335+gaoziman@users.noreply.github.com> Date: Thu, 9 Oct 2025 23:46:44 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=8A=9F=E8=83=BD=EF=BC=9A?= =?UTF-8?q?=E6=B4=BB=E5=8A=A8=E8=B5=84=E8=AE=AF=E5=8D=A1=E7=89=87=E7=BB=84?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 创建资讯卡片组件(NewsCard):支持分类标签、摘要、统计数据展示 - 创建活动卡片组件(EventCard):支持状态徽章、报名信息、价格展示 - 实现卡片hover效果和响应式布局 --- src/components/EventCard/index.css | 150 ++++++++++++++++++++++++++++ src/components/EventCard/index.tsx | 133 +++++++++++++++++++++++++ src/components/NewsCard/index.css | 155 +++++++++++++++++++++++++++++ src/components/NewsCard/index.tsx | 101 +++++++++++++++++++ 4 files changed, 539 insertions(+) create mode 100644 src/components/EventCard/index.css create mode 100644 src/components/EventCard/index.tsx create mode 100644 src/components/NewsCard/index.css create mode 100644 src/components/NewsCard/index.tsx diff --git a/src/components/EventCard/index.css b/src/components/EventCard/index.css new file mode 100644 index 0000000..8bb46c6 --- /dev/null +++ b/src/components/EventCard/index.css @@ -0,0 +1,150 @@ +/* 活动卡片样式 */ + +.event-card-link { + text-decoration: none; + display: block; +} + +.event-card { + border-radius: 12px; + overflow: hidden; + transition: all 0.3s ease; + height: 100%; +} + +.event-card:hover { + transform: translateY(-4px); + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12); +} + +/* 封面图 */ +.event-card-cover { + position: relative; + height: 200px; + overflow: hidden; +} + +.event-card-cover img { + width: 100%; + height: 100%; + object-fit: cover; + transition: transform 0.3s ease; +} + +.event-card:hover .event-card-cover img { + transform: scale(1.05); +} + +.event-card-overlay { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient(to bottom, rgba(0, 0, 0, 0.2), transparent); + display: flex; + align-items: flex-start; + padding: 12px; +} + +.type-tag { + font-size: 13px; + font-weight: 500; + border-radius: 4px; + padding: 4px 12px; +} + +/* 卡片内容 */ +.event-card-content { + padding: 4px 0 0 0; +} + +.event-card-title { + font-size: 16px; + font-weight: 600; + color: #2c2c2c; + margin-bottom: 12px; + line-height: 1.5; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + overflow: hidden; + text-overflow: ellipsis; +} + +.event-card-description { + font-size: 14px; + color: #666666; + margin-bottom: 12px; + line-height: 1.6; +} + +.event-card-info { + margin-bottom: 12px; + padding-bottom: 12px; + border-bottom: 1px solid #f0f0f0; +} + +.info-item { + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 8px; + font-size: 13px; +} + +.info-item:last-child { + margin-bottom: 0; +} + +.info-item .anticon { + color: #999999; + flex-shrink: 0; +} + +.info-item .ant-typography { + flex: 1; +} + +.free-tag { + color: #52c41a; + font-weight: 600; +} + +.event-card-footer { + display: flex; + align-items: center; + justify-content: space-between; + padding-top: 12px; + border-top: 1px solid #f0f0f0; +} + +.stat-item { + display: inline-flex; + align-items: center; + gap: 6px; + font-size: 13px; +} + +.stat-item .anticon { + color: #999999; +} + +.enrollment-status .ant-tag { + margin: 0; +} + +/* 响应式 */ +@media (max-width: 768px) { + .event-card-cover { + height: 180px; + } + + .event-card-title { + font-size: 15px; + } + + .event-card-description { + font-size: 13px; + } +} diff --git a/src/components/EventCard/index.tsx b/src/components/EventCard/index.tsx new file mode 100644 index 0000000..cca4e35 --- /dev/null +++ b/src/components/EventCard/index.tsx @@ -0,0 +1,133 @@ +/** + * 活动卡片组件 + */ + +import React from 'react' +import { Card, Tag, Space, Typography, Badge } from 'antd' +import { + EnvironmentOutlined, + CalendarOutlined, + ClockCircleOutlined, + UserOutlined, + DollarOutlined, +} from '@ant-design/icons' +import { Link } from 'react-router-dom' +import type { Event } from '@/types' +import './index.css' + +const { Text, Paragraph } = Typography + +interface EventCardProps { + item: Event + hoverable?: boolean +} + +const typeLabels: Record = { + exhibition: '展览', + workshop: '工作坊', + performance: '演出', + lecture: '讲座', + festival: '节日', +} + +const typeColors: Record = { + exhibition: 'purple', + workshop: 'blue', + performance: 'red', + lecture: 'green', + festival: 'orange', +} + +const statusLabels: Record = { + upcoming: '即将开始', + ongoing: '进行中', + finished: '已结束', + cancelled: '已取消', +} + +const statusColors: Record = { + upcoming: 'blue', + ongoing: 'green', + finished: 'default', + cancelled: 'red', +} + +const EventCard: React.FC = ({ item, hoverable = true }) => { + return ( + + + + {item.title} +
+ + {typeLabels[item.type]} + +
+ + } + className="event-card" + > +
+

{item.title}

+ + + {item.description} + + +
+
+ + {item.location} +
+
+ + + {item.startDate} + {item.startDate !== item.endDate && ` ~ ${item.endDate}`} + +
+ {item.startTime && ( +
+ + {item.startTime} - {item.endTime} +
+ )} +
+ + + {item.isFree ? '免费' : `¥${item.price}`} + +
+
+ +
+ + + + + {item.enrolled}/{item.capacity || 0} + + + + {item.capacity && item.enrolled >= item.capacity ? ( + 已满 + ) : ( + 可报名 + )} + + +
+
+
+
+ + ) +} + +export default EventCard diff --git a/src/components/NewsCard/index.css b/src/components/NewsCard/index.css new file mode 100644 index 0000000..2c6fd7e --- /dev/null +++ b/src/components/NewsCard/index.css @@ -0,0 +1,155 @@ +/* 资讯卡片样式 */ + +.news-card-link { + text-decoration: none; + display: block; +} + +.news-card { + border-radius: 12px; + overflow: hidden; + transition: all 0.3s ease; + height: 100%; +} + +.news-card:hover { + transform: translateY(-4px); + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12); +} + +/* 封面图 */ +.news-card-cover { + position: relative; + height: 220px; + overflow: hidden; +} + +.news-card-cover img { + width: 100%; + height: 100%; + object-fit: cover; + transition: transform 0.3s ease; +} + +.news-card:hover .news-card-cover img { + transform: scale(1.05); +} + +.news-card-overlay { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient(to bottom, rgba(0, 0, 0, 0.2), transparent); + display: flex; + align-items: flex-start; + padding: 12px; +} + +.category-tag { + font-size: 13px; + font-weight: 500; + border-radius: 4px; + padding: 4px 12px; +} + +/* 卡片内容 */ +.news-card-content { + padding: 4px 0 0 0; +} + +.news-card-title { + font-size: 16px; + font-weight: 600; + color: #2c2c2c; + margin-bottom: 8px; + line-height: 1.5; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + overflow: hidden; + text-overflow: ellipsis; +} + +.news-card-subtitle { + font-size: 13px; + color: #666666; + margin-bottom: 12px; + line-height: 1.5; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 1; + overflow: hidden; + text-overflow: ellipsis; +} + +.news-card-summary { + font-size: 14px; + color: #666666; + margin-bottom: 12px; + line-height: 1.6; +} + +.news-card-meta { + margin-bottom: 12px; + padding-bottom: 12px; + border-bottom: 1px solid #f0f0f0; +} + +.meta-item { + display: inline-flex; + align-items: center; + gap: 6px; + font-size: 13px; +} + +.meta-item .anticon { + color: #999999; +} + +.news-card-tags { + margin-bottom: 12px; + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.news-tag { + font-size: 12px; + border-radius: 4px; + margin: 0; +} + +.news-card-footer { + display: flex; + align-items: center; + padding-top: 12px; + border-top: 1px solid #f0f0f0; +} + +.stat-item { + display: inline-flex; + align-items: center; + gap: 6px; + font-size: 13px; +} + +.stat-item .anticon { + color: #999999; +} + +/* 响应式 */ +@media (max-width: 768px) { + .news-card-cover { + height: 180px; + } + + .news-card-title { + font-size: 15px; + } + + .news-card-summary { + font-size: 13px; + } +} diff --git a/src/components/NewsCard/index.tsx b/src/components/NewsCard/index.tsx new file mode 100644 index 0000000..935fd40 --- /dev/null +++ b/src/components/NewsCard/index.tsx @@ -0,0 +1,101 @@ +/** + * 资讯卡片组件 + */ + +import React from 'react' +import { Card, Tag, Space, Typography } from 'antd' +import { EyeOutlined, HeartOutlined, CalendarOutlined, UserOutlined } from '@ant-design/icons' +import { Link } from 'react-router-dom' +import type { NewsArticle } from '@/types' +import './index.css' + +const { Text, Paragraph } = Typography + +interface NewsCardProps { + item: NewsArticle + hoverable?: boolean +} + +const categoryLabels: Record = { + exhibition: '展览', + activity: '活动', + policy: '政策', + research: '研究', + story: '故事', +} + +const categoryColors: Record = { + exhibition: 'purple', + activity: 'blue', + policy: 'red', + research: 'green', + story: 'orange', +} + +const NewsCard: React.FC = ({ item, hoverable = true }) => { + return ( + + + {item.title} +
+ + {categoryLabels[item.category]} + +
+ + } + className="news-card" + > +
+

{item.title}

+ {item.subtitle && ( +

{item.subtitle}

+ )} + + + {item.summary} + + +
+ + + + {item.author} + + + + {item.publishDate} + + +
+ +
+ {item.tags.slice(0, 3).map((tag) => ( + + {tag} + + ))} +
+ +
+ + + + {item.viewCount.toLocaleString()} + + + + {item.likeCount.toLocaleString()} + + +
+
+
+ + ) +} + +export default NewsCard