From 6226fa7c4ded0bc1e2dbe4190170a6402f6c0c9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=B4=80=E1=B4=8D=E1=B4=9B=E1=B4=8F=E1=B4=80=E1=B4=87?= =?UTF-8?q?=CA=80?= Date: Wed, 4 Jun 2025 21:15:19 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E5=B0=8F=E9=97=AE=E9=A2=98=EF=BC=8C=E4=BC=98=E5=8C=96=E7=BB=86?= =?UTF-8?q?=E8=8A=82=E4=BD=93=E9=AA=8C=20(#352)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/lib/components/app-sidebar.svelte | 16 ++- web/src/lib/components/video-card.svelte | 19 +--- web/src/lib/stores/filter.ts | 58 ++++++---- web/src/routes/+layout.svelte | 3 +- web/src/routes/+page.svelte | 132 +++++++++++++--------- web/src/routes/video/[id]/+page.svelte | 13 ++- 6 files changed, 144 insertions(+), 97 deletions(-) diff --git a/web/src/lib/components/app-sidebar.svelte b/web/src/lib/components/app-sidebar.svelte index c57f473..f4fedc7 100644 --- a/web/src/lib/components/app-sidebar.svelte +++ b/web/src/lib/components/app-sidebar.svelte @@ -3,7 +3,13 @@ import SettingsIcon from '@lucide/svelte/icons/settings'; import * as Sidebar from '$lib/components/ui/sidebar/index.js'; import { useSidebar } from '$lib/components/ui/sidebar/context.svelte.js'; - import { appStateStore, setVideoSourceFilter, clearAll, ToQuery } from '$lib/stores/filter'; + import { + appStateStore, + setVideoSourceFilter, + clearAll, + ToQuery, + resetCurrentPage + } from '$lib/stores/filter'; import { type VideoSourcesResponse } from '$lib/types'; import { VIDEO_SOURCES } from '$lib/consts'; @@ -15,7 +21,11 @@ const items = Object.values(VIDEO_SOURCES); function handleSourceClick(sourceType: string, sourceId: number) { - setVideoSourceFilter(sourceType, sourceId.toString()); + setVideoSourceFilter({ + type: sourceType, + id: sourceId.toString() + }); + resetCurrentPage(); goto(`/${ToQuery($appStateStore)}`); if (sidebar.isMobile) { sidebar.setOpenMobile(false); @@ -52,7 +62,7 @@
视频来源 diff --git a/web/src/lib/components/video-card.svelte b/web/src/lib/components/video-card.svelte index 96a6996..6c45ded 100644 --- a/web/src/lib/components/video-card.svelte +++ b/web/src/lib/components/video-card.svelte @@ -76,22 +76,11 @@ async function handleReset() { resetting = true; - try { - if (onReset) { - await onReset(); - } else { - await api.resetVideo(video.id); - window.location.reload(); - } - } catch (error) { - console.error('重置失败:', error); - toast.error('重置失败', { - description: (error as ApiError).message - }); - } finally { - resetting = false; - resetDialogOpen = false; + if (onReset) { + await onReset(); } + resetting = false; + resetDialogOpen = false; } function handleViewDetail() { diff --git a/web/src/lib/stores/filter.ts b/web/src/lib/stores/filter.ts index 864b724..1e90edd 100644 --- a/web/src/lib/stores/filter.ts +++ b/web/src/lib/stores/filter.ts @@ -2,35 +2,35 @@ import { writable } from 'svelte/store'; export interface AppState { query: string; + currentPage: number; videoSource: { - key: string; - value: string; - }; + type: string; + id: string; + } | null; } -// 创建应用状态store export const appStateStore = writable({ query: '', - videoSource: { - key: '', - value: '' - } + currentPage: 0, + videoSource: null, }); export const ToQuery = (state: AppState): string => { const { query, videoSource } = state; const params = new URLSearchParams(); + if (state.currentPage > 0) { + params.set('page', String(state.currentPage)); + } if (query.trim()) { params.set('query', query); } - if (videoSource.key && videoSource.value) { - params.set(videoSource.key, videoSource.value); + if (videoSource && videoSource.type && videoSource.id) { + params.set(videoSource.type, videoSource.id); } const queryString = params.toString(); return queryString ? `?${queryString}` : ''; }; -// 便捷的设置方法 export const setQuery = (query: string) => { appStateStore.update((state) => ({ ...state, @@ -38,28 +38,46 @@ export const setQuery = (query: string) => { })); }; -export const setVideoSourceFilter = (key: string, value: string) => { +export const setVideoSourceFilter = (filter: { type: string; id: string }) => { appStateStore.update((state) => ({ ...state, - videoSource: { key, value } + videoSource: filter, })); }; export const clearVideoSourceFilter = () => { appStateStore.update((state) => ({ ...state, - videoSource: { key: '', value: '' } + videoSource: null, })); }; +export const setCurrentPage = (page: number) => { + appStateStore.update((state) => ({ + ...state, + currentPage: page, + })); +}; + +export const resetCurrentPage = () => { + appStateStore.update((state) => ({ + ...state, + currentPage: 0, + })); +}; + +export const setAll = (query: string, currentPage: number, videoSource: { type: string; id: string } | null) => { + appStateStore.set({ + query, + currentPage, + videoSource, + }); +}; + export const clearAll = () => { appStateStore.set({ query: '', - videoSource: { key: '', value: '' } + currentPage: 0, + videoSource: null, }); }; - -// 保留旧的接口以兼容现有代码 -export const filterStore = writable({ key: '', value: '' }); -export const setFilter = setVideoSourceFilter; -export const clearFilter = clearVideoSourceFilter; diff --git a/web/src/routes/+layout.svelte b/web/src/routes/+layout.svelte index 8a91243..bdc4014 100644 --- a/web/src/routes/+layout.svelte +++ b/web/src/routes/+layout.svelte @@ -4,7 +4,7 @@ import SearchBar from '$lib/components/search-bar.svelte'; import * as Sidebar from '$lib/components/ui/sidebar/index.js'; import { goto } from '$app/navigation'; - import { appStateStore, setQuery, ToQuery } from '$lib/stores/filter'; + import { appStateStore, resetCurrentPage, setQuery, ToQuery } from '$lib/stores/filter'; import { Toaster } from '$lib/components/ui/sonner/index.js'; import { breadcrumbStore } from '$lib/stores/breadcrumb'; import BreadCrumb from '$lib/components/bread-crumb.svelte'; @@ -18,6 +18,7 @@ async function handleSearch(query: string) { setQuery(query); + resetCurrentPage(); goto(`/${ToQuery($appStateStore)}`); } diff --git a/web/src/routes/+page.svelte b/web/src/routes/+page.svelte index 87ba244..4234c0c 100644 --- a/web/src/routes/+page.svelte +++ b/web/src/routes/+page.svelte @@ -21,52 +21,57 @@ import { appStateStore, clearVideoSourceFilter, + resetCurrentPage, + setAll, + setCurrentPage, setQuery, setVideoSourceFilter, ToQuery } from '$lib/stores/filter'; import { toast } from 'svelte-sonner'; + import { Title } from '$lib/components/ui/card'; + + const pageSize = 20; let videosData: VideosResponse | null = null; let loading = false; - let currentPage = 0; - const pageSize = 20; - let currentFilter: { type: string; id: string } | null = null; + let lastSearch: string | null = null; - // 重置所有视频相关状态 let resetAllDialogOpen = false; let resettingAll = false; - // 从URL参数获取筛选条件 - function getFilterFromURL(searchParams: URLSearchParams) { + function getApiParams(searchParams: URLSearchParams) { + let videoSource = null; for (const source of Object.values(VIDEO_SOURCES)) { const value = searchParams.get(source.type); if (value) { - return { type: source.type, id: value }; + videoSource = { type: source.type, id: value }; } } - return null; + return { + query: searchParams.get('query') || '', + videoSource, + pageNum: parseInt(searchParams.get('page') || '0') + }; } - // 获取筛选项名称 - function getFilterName(type: string, id: string): string { + function getFilterContent(type: string, id: string) { + const filterTitle = Object.values(VIDEO_SOURCES).find((s) => s.type === type)?.title || ''; + let filterName = ''; const videoSources = $videoSourceStore; - if (!videoSources || !type || !id) return ''; - - const sources = videoSources[type as keyof VideoSourcesResponse]; - const source = sources?.find((s) => s.id.toString() === id); - return source?.name || ''; - } - - // 获取筛选项标题 - function getFilterTitle(type: string): string { - const sourceConfig = Object.values(VIDEO_SOURCES).find((s) => s.type === type); - return sourceConfig?.title || ''; + if (videoSources && type && id) { + const sources = videoSources[type as keyof VideoSourcesResponse]; + filterName = sources?.find((s) => s.id.toString() === id)?.name || ''; + } + return { + title: filterTitle, + name: filterName + }; } async function loadVideos( - query?: string, + query: string, pageNum: number = 0, filter?: { type: string; id: string } | null ) { @@ -76,19 +81,14 @@ page: pageNum, page_size: pageSize }; - if (query) { params.query = query; } - - // 添加筛选参数 if (filter) { params[filter.type] = parseInt(filter.id); } - const result = await api.getVideos(params); videosData = result.data; - currentPage = pageNum; } catch (error) { console.error('加载视频失败:', error); toast.error('加载视频失败', { @@ -100,44 +100,56 @@ } async function handlePageChange(pageNum: number) { - const query = ToQuery($appStateStore); - if (query) { - goto(`/${query}&page=${pageNum}`); - } else { - goto(`/?page=${pageNum}`); - } + setCurrentPage(pageNum); + goto(`/${ToQuery($appStateStore)}`); } - async function handleSearchParamsChange() { - const query = $page.url.searchParams.get('query'); - currentFilter = getFilterFromURL($page.url.searchParams); - setQuery(query || ''); - if (currentFilter) { - setVideoSourceFilter(currentFilter.type, currentFilter.id); - } else { - clearVideoSourceFilter(); - } - loadVideos(query || '', parseInt($page.url.searchParams.get('page') || '0'), currentFilter); + async function handleSearchParamsChange(searchParams: URLSearchParams) { + const { query, videoSource, pageNum } = getApiParams(searchParams); + setAll(query, pageNum, videoSource); + loadVideos(query, pageNum, videoSource); } function handleFilterRemove() { clearVideoSourceFilter(); + resetCurrentPage(); goto(`/${ToQuery($appStateStore)}`); } + async function handleResetVideo(id: number) { + try { + const result = await api.resetVideo(id); + const data = result.data; + if (data.resetted) { + toast.success('重置成功', { + description: `视频「${data.video.name}」已重置` + }); + const { query, currentPage, videoSource } = $appStateStore; + await loadVideos(query, currentPage, videoSource); + } else { + toast.info('重置无效', { + description: `视频「${data.video.name}」没有失败的状态,无需重置` + }); + } + } catch (error) { + console.error('重置失败:', error); + toast.error('重置失败', { + description: (error as ApiError).message + }); + } + } + async function handleResetAllVideos() { resettingAll = true; try { const result = await api.resetAllVideos(); const data = result.data; - if (data.resetted) { toast.success('重置成功', { description: `已重置 ${data.resetted_videos_count} 个视频和 ${data.resetted_pages_count} 个分页` }); - // 重新加载当前页面的视频数据 - const query = $page.url.searchParams.get('query'); - loadVideos(query || '', currentPage, currentFilter); + const { query, currentPage, videoSource } = $appStateStore; + await loadVideos(query, currentPage, videoSource); } else { toast.info('没有需要重置的视频'); } @@ -154,7 +166,7 @@ $: if ($page.url.search !== lastSearch) { lastSearch = $page.url.search; - handleSearchParamsChange(); + handleSearchParamsChange($page.url.searchParams); } onMount(async () => { @@ -167,15 +179,20 @@ }); $: totalPages = videosData ? Math.ceil(videosData.total_count / pageSize) : 0; - $: filterTitle = currentFilter ? getFilterTitle(currentFilter.type) : ''; - $: filterName = currentFilter ? getFilterName(currentFilter.type, currentFilter.id) : ''; + $: filterContent = $appStateStore.videoSource + ? getFilterContent($appStateStore.videoSource.type, $appStateStore.videoSource.id) + : { title: '', name: '' }; 主页 - Bili Sync - + {#if videosData} @@ -214,13 +231,22 @@ > {#each videosData.videos as video (video.id)}
- + { + await handleResetVideo(video.id); + }} + />
{/each}
- + {:else}
diff --git a/web/src/routes/video/[id]/+page.svelte b/web/src/routes/video/[id]/+page.svelte index 0a1af15..346908f 100644 --- a/web/src/routes/video/[id]/+page.svelte +++ b/web/src/routes/video/[id]/+page.svelte @@ -24,10 +24,8 @@ toast.error('无效的视频ID'); return; } - loading = true; error = null; - try { const result = await api.getVideo(videoId); videoData = result.data; @@ -114,12 +112,17 @@ onReset={async () => { try { const result = await api.resetVideo((videoData as VideoResponse).video.id); - if (result.data.resetted) { + const data = result.data; + if (data.resetted) { videoData = { - video: result.data.video, - pages: result.data.pages + video: data.video, + pages: data.pages }; toast.success('重置成功'); + } else { + toast.info('重置无效', { + description: `视频「${data.video.name}」没有失败的状态,无需重置` + }); } } catch (error) { console.error('重置失败:', error);