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);