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