Skip to content

Commit 3925028

Browse files
committed
feat: add iframe coscon event page
1 parent 39e0309 commit 3925028

File tree

2 files changed

+374
-309
lines changed

2 files changed

+374
-309
lines changed

src/pages/events/coscon/index.tsx

Lines changed: 31 additions & 309 deletions
Original file line numberDiff line numberDiff line change
@@ -1,316 +1,38 @@
1-
/* eslint-disable @typescript-eslint/no-explicit-any */
2-
import { useState, useEffect, useCallback, useMemo } from 'react';
3-
import {
4-
Card,
5-
Image,
6-
Button,
7-
Tag,
8-
Popconfirm,
9-
App as AntdApp,
10-
} from 'antd';
11-
import dayjs from 'dayjs';
12-
import {
13-
Calendar,
14-
Users,
15-
MapPin,
16-
Plus,
17-
Edit,
18-
Trash2,
19-
Share2,
20-
Globe,
21-
} from 'lucide-react';
22-
import { SiX } from 'react-icons/si';
23-
import Link from 'next/link';
24-
import styles from '../index.module.css';
25-
import { getEvents, deleteEvent } from '../../api/event';
26-
import { useRouter } from 'next/router';
27-
import { useAuth } from '@/contexts/AuthContext';
1+
import { useEffect } from 'react';
282

29-
30-
export function formatTime(isoTime: string): string {
31-
return dayjs(isoTime).format('YYYY-MM-DD');
32-
}
33-
34-
export default function CosconEventsPage() {
35-
const { message } = AntdApp.useApp();
36-
const [events, setEvents] = useState<any[]>([]);
37-
const [loading, setLoading] = useState(false);
38-
const [publishStatus, setPublishStatus] = useState(2);
39-
40-
const router = useRouter();
41-
const { session, status } = useAuth();
42-
const permissions = useMemo(() => session?.user?.permissions || [], [session?.user?.permissions]);
43-
44-
// 加载事件列表 - 固定参数,只加载 coscon 类型的活动
45-
const loadEvents = useCallback(async () => {
46-
try {
47-
setLoading(true);
48-
49-
const queryParams = {
50-
keyword: '',
51-
tag: '',
52-
order: 'desc' as const,
53-
page: 1,
54-
page_size: 9999,
55-
status: '3',
56-
location: '',
57-
event_mode: '',
58-
event_type: 'coscon', // 写死为 coscon
59-
publish_status: publishStatus,
60-
};
61-
62-
const result = await getEvents(queryParams);
63-
64-
if (result.success && result.data) {
65-
if (result.data.events && Array.isArray(result.data.events)) {
66-
setEvents(result.data.events);
67-
} else if (Array.isArray(result.data)) {
68-
setEvents(result.data);
69-
} else {
70-
console.warn('API 返回的数据格式不符合预期:', result.data);
71-
setEvents([]);
72-
}
73-
} else {
74-
console.error('获取事件列表失败:', result.message);
75-
setEvents([]);
76-
}
77-
} catch (error: unknown) {
78-
console.error('加载事件列表异常:', error);
79-
setEvents([]);
80-
} finally {
81-
setLoading(false);
82-
}
83-
}, [publishStatus]);
84-
85-
// 根据登录状态更新 publishStatus
3+
export default function CosconEvent() {
864
useEffect(() => {
87-
if (status === 'authenticated' && permissions.includes('event:review')) {
88-
setPublishStatus(0);
89-
} else if (status === 'unauthenticated') {
90-
setPublishStatus(2);
91-
}
92-
}, [status, permissions]);
93-
94-
// 主要的数据加载效果
95-
useEffect(() => {
96-
if (!router.isReady) return;
97-
loadEvents();
98-
}, [publishStatus, loadEvents, router.isReady]);
99-
100-
101-
// 获取事件状态显示文本
102-
const getStatusText = (event: any) => {
103-
if (event.status === 0) {
104-
return '未开始';
105-
} else if (event.status === 1) {
106-
return '进行中';
107-
} else {
108-
return '已结束';
109-
}
110-
};
111-
112-
// 获取事件状态类名
113-
const getStatusClass = (event: any) => {
114-
if (event.status === 0) {
115-
return styles.upcoming;
116-
} else if (event.status === 1) {
117-
return styles.ongoing;
118-
} else {
119-
return styles.ended;
120-
}
121-
};
122-
123-
const handleDeleteEvent = async (id: number) => {
124-
try {
125-
const result = await deleteEvent(id);
126-
if (result.success) {
127-
message.success(result.message);
128-
loadEvents();
129-
} else {
130-
message.error(result.message || '删除活动失败');
131-
}
132-
} catch {
133-
message.error('删除失败,请重试');
134-
}
135-
};
5+
const script = document.createElement('script');
6+
script.innerHTML = `
7+
(function (i, s, o, g, r, a, m) {
8+
i['BagEventIFrameResize'] = r;
9+
i[r] = i[r] || function () {
10+
(i[r].q = i[r].q || []).push(arguments)
11+
};
12+
a = s.createElement(o),
13+
m = s.getElementsByTagName(o)[0];
14+
a.async = 1;
15+
a.src = g;
16+
m.parentNode.insertBefore(a, m)
17+
})(window, document, 'script', 'https://www.bagevent.com/resources/js/iframeResizer/iframeResizer.min.js', 'bfr');
18+
bfr('iFrameResize', {checkOrigin: false, heightCalculationMethod: 'taggedElement'}, "#promote_ticket_iframe");
19+
`;
20+
document.body.appendChild(script);
21+
22+
return () => {
23+
document.body.removeChild(script);
24+
};
25+
}, []);
13626

13727
return (
138-
<div className={`${styles.container} nav-t-top`}>
139-
{/* Title Section */}
140-
<div className={styles.header}>
141-
<div className={styles.headerContent}>
142-
<div className={styles.titleSection}>
143-
<h1 className={styles.title}>中国开源年会</h1>
144-
<p className={styles.subtitle}>中国最大的开源技术年度盛会</p>
145-
</div>
146-
<Link href="/events/new?event_type=coscon" className={styles.createButton}>
147-
<Plus size={20} />
148-
发布开源年会
149-
</Link>
150-
</div>
151-
</div>
152-
153-
154-
{/* Events Display */}
155-
{loading ? (
156-
<div className={styles.loading}>
157-
<div className={styles.loadingSpinner}></div>
158-
</div>
159-
) : events.length === 0 ? (
160-
<div className={styles.emptyContainer}>
161-
<div className={styles.emptyIcon}>📅</div>
162-
<div className={styles.emptyTitle}>暂无中国开源年会活动</div>
163-
<div className={styles.emptyDescription}>
164-
还没有创建任何中国开源年会活动
165-
</div>
166-
<Link href="/events/new?event_type=coscon" className={styles.createButton}>
167-
<Plus className={styles.buttonIcon} />
168-
创建第一个活动
169-
</Link>
170-
</div>
171-
) : (
172-
<div className={styles.eventsGrid}>
173-
{events.map((event) => (
174-
<Link
175-
href={`/events/${event.ID}`}
176-
key={event.ID}
177-
className={styles.cardLink}
178-
>
179-
<Card
180-
className={styles.eventCard}
181-
cover={
182-
<div className={styles.cardCover}>
183-
<Image
184-
alt={event.title}
185-
src={
186-
event.cover_img ||
187-
'/placeholder.svg?height=240&width=400&text=活动封面'
188-
}
189-
className={styles.coverImage}
190-
preview={false}
191-
/>
192-
<div className={styles.coverOverlay}>
193-
<Tag
194-
className={`${styles.statusTag} ${getStatusClass(event)}`}
195-
>
196-
{getStatusText(event)}
197-
</Tag>
198-
{event.publish_status === 1 && (
199-
<Tag className={styles.noPublishStatus}>未发布</Tag>
200-
)}
201-
<div className={styles.cardActions}>
202-
{status === 'authenticated' &&
203-
permissions.includes('event:write') ? (
204-
<Button
205-
className={styles.actionIconButton}
206-
onClick={(e) => {
207-
e.preventDefault();
208-
router.push(`/events/${event.ID}/edit?event_type=coscon`);
209-
}}
210-
icon={<Edit className={styles.actionIcon} />}
211-
title="编辑活动"
212-
/>
213-
) : null}
214-
<Button
215-
className={styles.actionIconButton}
216-
onClick={(e) => {
217-
e.preventDefault();
218-
navigator.clipboard.writeText(
219-
`${window.location.href.replace('/coscon', '')}/${event.ID}`
220-
);
221-
message.success('链接已复制到剪贴板');
222-
}}
223-
icon={<Share2 className={styles.actionIcon} />}
224-
title="分享活动"
225-
/>
226-
<Button
227-
className={styles.actionIconButton}
228-
onClick={(e) => {
229-
e.preventDefault();
230-
if (event.twitter) {
231-
window.open(event.twitter, '_blank');
232-
}
233-
}}
234-
icon={<SiX className={styles.actionIcon} />}
235-
title="查看推文"
236-
/>
237-
{status === 'authenticated' &&
238-
permissions.includes('event:delete') ? (
239-
<Popconfirm
240-
title="删除活动"
241-
description="你确定删除这个活动吗?"
242-
okText="是"
243-
cancelText="否"
244-
onConfirm={() => handleDeleteEvent(event.ID)}
245-
>
246-
<Button
247-
className={styles.actionIconButton}
248-
onClick={(e) => {
249-
e.preventDefault();
250-
}}
251-
icon={<Trash2 className={styles.actionIcon} />}
252-
title="删除活动"
253-
danger
254-
/>
255-
</Popconfirm>
256-
) : null}
257-
</div>
258-
</div>
259-
</div>
260-
}
261-
>
262-
<div className={styles.cardBody}>
263-
<h3 className={styles.eventTitle}>{event.title}</h3>
264-
265-
<div className={styles.cardMeta}>
266-
<div className={styles.metaItem}>
267-
<Calendar className={styles.metaIcon} />
268-
<span>{formatTime(event.start_time)}</span>
269-
</div>
270-
<div className={styles.metaItem}>
271-
{event.event_mode === '线上活动' ? (
272-
<>
273-
<Globe className={styles.metaIcon} />
274-
<span className={styles.locationText}>线上活动</span>
275-
</>
276-
) : (
277-
<>
278-
<MapPin className={styles.metaIcon} />
279-
<span className={styles.locationText}>
280-
{event.location || '未指定地点'}
281-
</span>
282-
</>
283-
)}
284-
</div>
285-
{event.participants !== 0 && (
286-
<div className={styles.metaItem}>
287-
<Users className={styles.metaIcon} />
288-
<span>{event.participants || ''}</span>
289-
</div>
290-
)}
291-
</div>
292-
{event.tags && event.tags.length > 0 && (
293-
<div className={styles.cardTags}>
294-
{event.tags
295-
.slice(0, 3)
296-
.map((tag: string, index: number) => (
297-
<Tag key={index} className={styles.eventTag}>
298-
{tag}
299-
</Tag>
300-
))}
301-
{event.tags.length > 3 && (
302-
<Tag className={styles.moreTag}>
303-
+{event.tags.length - 3}
304-
</Tag>
305-
)}
306-
</div>
307-
)}
308-
</div>
309-
</Card>
310-
</Link>
311-
))}
312-
</div>
313-
)}
28+
<div>
29+
<iframe
30+
id="promote_ticket_iframe"
31+
width="100%"
32+
src="https://www.bagevent.com/widget/ticket/8199016?widget=2&iframe=1"
33+
frameBorder="0"
34+
scrolling="no"
35+
/>
31436
</div>
31537
);
31638
}

0 commit comments

Comments
 (0)