移动端适配
All checks were successful
Deploy to Server / deploy (push) Successful in 36s

This commit is contained in:
jeremygan2021
2026-02-23 17:48:52 +08:00
parent cb66dd92c3
commit 52b16911b1
2 changed files with 108 additions and 20 deletions

View File

@@ -173,6 +173,60 @@
} }
} }
.activityTitle {
font-size: 28px;
margin-bottom: 16px;
color: #fff;
line-height: 1.3;
@media (max-width: 768px) {
font-size: 22px;
margin-bottom: 12px;
}
}
.metaInfo {
display: flex;
gap: 20px;
margin-bottom: 16px;
color: rgba(255, 255, 255, 0.7);
flex-wrap: wrap;
@media (max-width: 768px) {
gap: 12px;
margin-bottom: 12px;
}
}
.metaItem {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
@media (max-width: 768px) {
font-size: 13px;
gap: 6px;
background: rgba(255, 255, 255, 0.05);
padding: 4px 8px;
border-radius: 4px;
}
}
.statusWrapper {
display: flex;
gap: 10px;
}
.headerGradient {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 50%;
background: linear-gradient(to top, #1f1f1f, transparent);
}
.time { .time {
color: var(--text-secondary); color: var(--text-secondary);
font-size: 14px; font-size: 14px;
@@ -188,6 +242,11 @@
min-height: 300px; min-height: 300px;
width: 100%; width: 100%;
overflow: hidden; overflow: hidden;
@media (max-width: 768px) {
height: 40vh;
min-height: 250px;
}
} }
.detailImage { .detailImage {
@@ -202,6 +261,11 @@
position: relative; position: relative;
z-index: 10; z-index: 10;
padding: 0 var(--spacing-lg) 100px; /* Bottom padding for fixed footer */ padding: 0 var(--spacing-lg) 100px; /* Bottom padding for fixed footer */
@media (max-width: 768px) {
padding: 0 var(--spacing-md) 80px;
margin-top: -40px;
}
} }
.infoCard { .infoCard {
@@ -211,6 +275,11 @@
box-shadow: var(--box-shadow-base); box-shadow: var(--box-shadow-base);
margin-bottom: var(--spacing-lg); margin-bottom: var(--spacing-lg);
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
@media (max-width: 768px) {
padding: var(--spacing-md);
margin-bottom: var(--spacing-md);
}
} }
.richText { .richText {
@@ -218,6 +287,10 @@
line-height: 1.8; line-height: 1.8;
font-size: 16px; font-size: 16px;
@media (max-width: 768px) {
font-size: 15px;
}
img { img {
max-width: 100%; max-width: 100%;
border-radius: var(--border-radius-base); border-radius: var(--border-radius-base);
@@ -227,6 +300,11 @@
h1, h2, h3 { h1, h2, h3 {
color: var(--text-primary); color: var(--text-primary);
margin-top: var(--spacing-lg); margin-top: var(--spacing-lg);
@media (max-width: 768px) {
margin-top: var(--spacing-md);
font-size: 1.2em; /* slightly smaller headings */
}
} }
} }

View File

@@ -37,7 +37,7 @@ const ActivityDetail = () => {
const headerColor = useTransform(scrollY, [0, 60], ['rgba(255,255,255,1)', 'rgba(0,0,0,0.85)']); const headerColor = useTransform(scrollY, [0, 60], ['rgba(255,255,255,1)', 'rgba(0,0,0,0.85)']);
const titleOpacity = useTransform(scrollY, [100, 200], [0, 1]); const titleOpacity = useTransform(scrollY, [100, 200], [0, 1]);
const { data: activity, isLoading, error, refetch } = useQuery({ const { data: activity, isLoading, error } = useQuery({
queryKey: ['activity', id], queryKey: ['activity', id],
queryFn: async () => { queryFn: async () => {
try { try {
@@ -140,7 +140,7 @@ const ActivityDetail = () => {
clearInterval(timer); clearInterval(timer);
} }
} catch (error) { } catch {
// ignore error during polling // ignore error during polling
} }
}, 3000); }, 3000);
@@ -188,7 +188,7 @@ const ActivityDetail = () => {
text: '来看看这个精彩活动!', text: '来看看这个精彩活动!',
url: url url: url
}); });
} catch (err) { } catch {
console.log('Share canceled'); console.log('Share canceled');
} }
} else { } else {
@@ -256,6 +256,23 @@ const ActivityDetail = () => {
); );
} }
if (!activity) {
return (
<div style={{ padding: 40, background: '#1f1f1f', minHeight: '100vh', display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
<Result
status="404"
title="活动未找到"
subTitle="抱歉,该活动可能已被删除或不存在。"
extra={[
<Button type="primary" key="back" onClick={() => navigate(-1)}>
返回列表
</Button>
]}
/>
</div>
);
}
const cleanUrl = (url) => { const cleanUrl = (url) => {
if (!url) return ''; if (!url) return '';
return url.replace(/[`\s]/g, ''); return url.replace(/[`\s]/g, '');
@@ -313,45 +330,38 @@ const ActivityDetail = () => {
alt={activity.title} alt={activity.title}
className={styles.detailImage} className={styles.detailImage}
/> />
<div style={{ <div className={styles.headerGradient} />
position: 'absolute',
bottom: 0,
left: 0,
width: '100%',
height: '50%',
background: 'linear-gradient(to top, #1f1f1f, transparent)'
}} />
</div> </div>
{/* Content */} {/* Content */}
<div className={styles.detailContent}> <div className={styles.detailContent}>
<div className={styles.infoCard}> <div className={styles.infoCard}>
<h1 style={{ fontSize: 28, marginBottom: 16, color: '#fff' }}>{activity.title}</h1> <h1 className={styles.activityTitle}>{activity.title}</h1>
<div style={{ display: 'flex', gap: 20, marginBottom: 16, color: 'rgba(255,255,255,0.7)' }}> <div className={styles.metaInfo}>
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}> <div className={styles.metaItem}>
<CalendarOutlined /> <CalendarOutlined />
<span>{activity.start_time ? new Date(activity.start_time).toLocaleDateString() : 'TBD'}</span> <span>{activity.start_time ? new Date(activity.start_time).toLocaleDateString() : 'TBD'}</span>
</div> </div>
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}> <div className={styles.metaItem}>
<ClockCircleOutlined /> <ClockCircleOutlined />
<span>{activity.start_time ? new Date(activity.start_time).toLocaleTimeString() : 'TBD'}</span> <span>{activity.start_time ? new Date(activity.start_time).toLocaleTimeString() : 'TBD'}</span>
</div> </div>
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}> <div className={styles.metaItem}>
<EnvironmentOutlined /> <EnvironmentOutlined />
<span>{activity.location || '线上活动'}</span> <span>{activity.location || '线上活动'}</span>
</div> </div>
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}> <div className={styles.metaItem}>
<UserOutlined /> <UserOutlined />
<span>{activity.current_signups || 0} / {activity.max_participants} 已报名</span> <span>{activity.current_signups || 0} / {activity.max_participants} 已报名</span>
</div> </div>
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}> <div className={styles.metaItem}>
<PayCircleOutlined /> <PayCircleOutlined />
<span>{activity.is_paid ? `¥${activity.price}` : '免费'}</span> <span>{activity.is_paid ? `¥${activity.price}` : '免费'}</span>
</div> </div>
</div> </div>
<div style={{ display: 'flex', gap: 10 }}> <div className={styles.statusWrapper}>
<span className={styles.statusTag}> <span className={styles.statusTag}>
{activity.status || (new Date() < new Date(activity.start_time) ? '报名中' : '已结束')} {activity.status || (new Date() < new Date(activity.start_time) ? '报名中' : '已结束')}
</span> </span>
@@ -365,7 +375,7 @@ const ActivityDetail = () => {
remarkPlugins={[remarkGfm]} remarkPlugins={[remarkGfm]}
rehypePlugins={[rehypeRaw]} rehypePlugins={[rehypeRaw]}
components={{ components={{
code({node, inline, className, children, ...props}) { code({inline, className, children, ...props}) {
const match = /language-(\w+)/.exec(className || '') const match = /language-(\w+)/.exec(className || '')
return !inline && match ? ( return !inline && match ? (
<SyntaxHighlighter <SyntaxHighlighter