From 7af32201ab7978844a100fb1b38ddc641424e212 Mon Sep 17 00:00:00 2001 From: shinya Date: Tue, 8 Jul 2025 23:50:20 +0800 Subject: [PATCH] feat: loading screen --- src/app/play/page.tsx | 232 +++++++++++++++++++++++++++++++++--------- 1 file changed, 185 insertions(+), 47 deletions(-) diff --git a/src/app/play/page.tsx b/src/app/play/page.tsx index aeaf9b5..1b6ea80 100644 --- a/src/app/play/page.tsx +++ b/src/app/play/page.tsx @@ -39,6 +39,10 @@ function PlayPageClient() { // 状态变量(State) // ----------------------------------------------------------------------------- const [loading, setLoading] = useState(true); + const [loadingStage, setLoadingStage] = useState< + 'searching' | 'fetching' | 'ready' + >('searching'); + const [loadingMessage, setLoadingMessage] = useState('正在搜索播放源...'); const [error, setError] = useState(null); const [detail, setDetail] = useState(null); @@ -55,9 +59,6 @@ function PlayPageClient() { }); // 视频基本信息 - const [videoDoubanId, setVideoDoubanId] = useState( - searchParams.get('douban_id') || '' - ); const [videoTitle, setVideoTitle] = useState(searchParams.get('title') || ''); const [videoYear, setVideoYear] = useState(searchParams.get('year') || ''); const [videoCover, setVideoCover] = useState(''); @@ -245,8 +246,10 @@ function PlayPageClient() { if (!currentSource && !currentId) { // 只包含视频标题,搜索视频 setLoading(true); + setLoadingStage('searching'); + setLoadingMessage('🔍 正在搜索播放源...'); + const searchResults = await handleSearchSources(videoTitle); - console.log('searchResults', searchResults); if (searchResults.length == 0) { setError('未找到匹配结果'); setLoading(false); @@ -255,7 +258,6 @@ function PlayPageClient() { setCurrentSource(searchResults[0].source); setCurrentId(searchResults[0].id); setVideoYear(searchResults[0].year); - setVideoDoubanId(''); // 清空豆瓣ID // 替换URL参数 const newUrl = new URL(window.location.href); newUrl.searchParams.set('source', searchResults[0].source); @@ -268,6 +270,9 @@ function PlayPageClient() { const fetchDetail = async () => { try { + setLoadingStage('fetching'); + setLoadingMessage('🎬 正在获取视频详情...'); + const detailData = await fetchVideoDetail({ source: currentSource, id: currentId, @@ -282,7 +287,6 @@ function PlayPageClient() { // 确保集数索引在有效范围内 if (currentEpisodeIndex >= detailData.episodes.length) { - console.log('currentEpisodeIndex', currentEpisodeIndex); setCurrentEpisodeIndex(0); } @@ -293,9 +297,17 @@ function PlayPageClient() { newUrl.searchParams.delete('position'); window.history.replaceState({}, '', newUrl.toString()); } + + setLoadingStage('ready'); + setLoadingMessage('✨ 准备就绪,即将开始播放...'); + + // 短暂延迟让用户看到完成状态 + setTimeout(() => { + setLoading(false); + }, 1000); } catch (err) { console.error('获取视频详情失败:', err); - } finally { + setError(err instanceof Error ? err.message : '获取视频详情失败'); setLoading(false); } }; @@ -417,16 +429,12 @@ function PlayPageClient() { ? (detail.episodes.length === 1 && result.episodes.length === 1) || (detail.episodes.length > 1 && result.episodes.length > 1) - : true) && - (videoDoubanId && result.douban_id - ? result.douban_id.toString() === videoDoubanId : true) ); if (exactMatchs.length > 0) { processedResults.push(...exactMatchs); } }); - console.log('processedResults', processedResults); setAvailableSources(processedResults); return processedResults; @@ -739,17 +747,27 @@ function PlayPageClient() { // 切换收藏 const handleToggleFavorite = async () => { - if (!videoTitleRef.current || !detailRef.current || !currentSourceRef.current || !currentIdRef.current) return; + if ( + !videoTitleRef.current || + !detailRef.current || + !currentSourceRef.current || + !currentIdRef.current + ) + return; try { - const newState = await toggleFavorite(currentSourceRef.current, currentIdRef.current, { - title: videoTitleRef.current, - source_name: detailRef.current?.source_name || '', - year: detailRef.current?.year || '', - cover: detailRef.current?.poster || '', - total_episodes: detailRef.current?.episodes.length || 1, - save_time: Date.now(), - }); + const newState = await toggleFavorite( + currentSourceRef.current, + currentIdRef.current, + { + title: videoTitleRef.current, + source_name: detailRef.current?.source_name || '', + year: detailRef.current?.year || '', + cover: detailRef.current?.poster || '', + total_episodes: detailRef.current?.episodes.length || 1, + save_time: Date.now(), + } + ); setFavorited(newState); } catch (err) { console.error('切换收藏失败:', err); @@ -798,7 +816,6 @@ function PlayPageClient() { }集`; artPlayerRef.current.poster = videoCover; if (artPlayerRef.current?.video) { - console.log('attachVideoEventListeners'); attachVideoEventListeners( artPlayerRef.current.video as HTMLVideoElement ); @@ -1024,7 +1041,6 @@ function PlayPageClient() { }); if (artPlayerRef.current?.video) { - console.log('attachVideoEventListeners'); attachVideoEventListeners( artPlayerRef.current.video as HTMLVideoElement ); @@ -1037,7 +1053,7 @@ function PlayPageClient() { console.error('创建播放器失败:', err); setError('播放器初始化失败'); } - }, [Artplayer, Hls, videoUrl]); + }, [Artplayer, Hls, videoUrl, loading]); // --------------------------------------------------------------------------- // 视频元素事件监听 @@ -1164,12 +1180,91 @@ function PlayPageClient() { if (loading) { return ( -
-
-
-

- 加载中... -

+
+
+ {/* 动画影院图标 */} +
+
+
+ {loadingStage === 'searching' && '🔍'} + {loadingStage === 'fetching' && '🎬'} + {loadingStage === 'ready' && '✨'} +
+ {/* 旋转光环 */} +
+
+ + {/* 浮动粒子效果 */} +
+
+
+
+
+
+ + {/* 进度指示器 */} +
+
+
+
+
+
+ + {/* 进度条 */} +
+
+
+
+ + {/* 加载消息 */} +
+

+ {loadingMessage} +

+

+ {loadingStage === 'searching' && '正在为您寻找可用播放源...'} + {loadingStage === 'fetching' && '正在解析视频信息和播放列表...'} + {loadingStage === 'ready' && '一切准备就绪,马上就能观看啦!'} +

+
@@ -1179,24 +1274,67 @@ function PlayPageClient() { if (error) { return ( -
-
-
⚠️
-

- {error} -

- +
+
+ {/* 错误图标 */} +
+
+
😵
+ {/* 脉冲效果 */} +
+
+ + {/* 浮动错误粒子 */} +
+
+
+
+
+
+ + {/* 错误信息 */} +
+

+ 哎呀,出现了一些问题 +

+
+

+ {error} +

+
+

+ 请检查网络连接或尝试刷新页面 +

+
+ + {/* 操作按钮 */} +
+ + + +