mirror of
https://github.com/amtoaer/bili-sync.git
synced 2026-09-05 23:49:53 +08:00
feat: 支持清除重置,方便分页视频刷新 (#596)
This commit is contained in:
@@ -33,6 +33,12 @@ pub struct ResetVideoResponse {
|
|||||||
pub pages: Vec<PageInfo>,
|
pub pages: Vec<PageInfo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct ClearAndResetVideoStatusResponse {
|
||||||
|
pub warning: Option<String>,
|
||||||
|
pub video: VideoInfo,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
pub struct ResetFilteredVideosResponse {
|
pub struct ResetFilteredVideosResponse {
|
||||||
pub resetted: bool,
|
pub resetted: bool,
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::{Context, Result};
|
||||||
use axum::extract::{Extension, Path, Query};
|
use axum::extract::{Extension, Path, Query};
|
||||||
use axum::routing::{get, post};
|
use axum::routing::{get, post};
|
||||||
use axum::{Json, Router};
|
use axum::{Json, Router};
|
||||||
use bili_sync_entity::*;
|
use bili_sync_entity::*;
|
||||||
|
use sea_orm::ActiveValue::Set;
|
||||||
use sea_orm::{
|
use sea_orm::{
|
||||||
ColumnTrait, DatabaseConnection, EntityTrait, PaginatorTrait, QueryFilter, QueryOrder, TransactionTrait,
|
ActiveModelTrait, ColumnTrait, DatabaseConnection, EntityTrait, IntoActiveModel, PaginatorTrait, QueryFilter,
|
||||||
|
QueryOrder, TransactionTrait, TryIntoModel,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::api::error::InnerApiError;
|
use crate::api::error::InnerApiError;
|
||||||
@@ -16,8 +18,9 @@ use crate::api::request::{
|
|||||||
UpdateVideoStatusRequest, VideosRequest,
|
UpdateVideoStatusRequest, VideosRequest,
|
||||||
};
|
};
|
||||||
use crate::api::response::{
|
use crate::api::response::{
|
||||||
PageInfo, ResetFilteredVideosResponse, ResetVideoResponse, SimplePageInfo, SimpleVideoInfo,
|
ClearAndResetVideoStatusResponse, PageInfo, ResetFilteredVideosResponse, ResetVideoResponse, SimplePageInfo,
|
||||||
UpdateFilteredVideoStatusResponse, UpdateVideoStatusResponse, VideoInfo, VideoResponse, VideosResponse,
|
SimpleVideoInfo, UpdateFilteredVideoStatusResponse, UpdateVideoStatusResponse, VideoInfo, VideoResponse,
|
||||||
|
VideosResponse,
|
||||||
};
|
};
|
||||||
use crate::api::wrapper::{ApiError, ApiResponse, ValidatedJson};
|
use crate::api::wrapper::{ApiError, ApiResponse, ValidatedJson};
|
||||||
use crate::utils::status::{PageStatus, VideoStatus};
|
use crate::utils::status::{PageStatus, VideoStatus};
|
||||||
@@ -26,6 +29,10 @@ pub(super) fn router() -> Router {
|
|||||||
Router::new()
|
Router::new()
|
||||||
.route("/videos", get(get_videos))
|
.route("/videos", get(get_videos))
|
||||||
.route("/videos/{id}", get(get_video))
|
.route("/videos/{id}", get(get_video))
|
||||||
|
.route(
|
||||||
|
"/videos/{id}/clear-and-reset-status",
|
||||||
|
post(clear_and_reset_video_status),
|
||||||
|
)
|
||||||
.route("/videos/{id}/reset-status", post(reset_video_status))
|
.route("/videos/{id}/reset-status", post(reset_video_status))
|
||||||
.route("/videos/{id}/update-status", post(update_video_status))
|
.route("/videos/{id}/update-status", post(update_video_status))
|
||||||
.route("/videos/reset-status", post(reset_filtered_video_status))
|
.route("/videos/reset-status", post(reset_filtered_video_status))
|
||||||
@@ -152,6 +159,43 @@ pub async fn reset_video_status(
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn clear_and_reset_video_status(
|
||||||
|
Path(id): Path<i32>,
|
||||||
|
Extension(db): Extension<DatabaseConnection>,
|
||||||
|
) -> Result<ApiResponse<ClearAndResetVideoStatusResponse>, ApiError> {
|
||||||
|
let video_info = video::Entity::find_by_id(id).one(&db).await?;
|
||||||
|
let Some(video_info) = video_info else {
|
||||||
|
return Err(InnerApiError::NotFound(id).into());
|
||||||
|
};
|
||||||
|
let txn = db.begin().await?;
|
||||||
|
let mut video_info = video_info.into_active_model();
|
||||||
|
video_info.single_page = Set(None);
|
||||||
|
video_info.download_status = Set(0);
|
||||||
|
let video_info = video_info.update(&txn).await?;
|
||||||
|
page::Entity::delete_many()
|
||||||
|
.filter(page::Column::VideoId.eq(id))
|
||||||
|
.exec(&txn)
|
||||||
|
.await?;
|
||||||
|
txn.commit().await?;
|
||||||
|
let video_info = video_info.try_into_model()?;
|
||||||
|
let warning = tokio::fs::remove_dir_all(&video_info.path)
|
||||||
|
.await
|
||||||
|
.context(format!("删除本地路径「{}」失败", video_info.path))
|
||||||
|
.err()
|
||||||
|
.map(|e| format!("{:#}", e));
|
||||||
|
Ok(ApiResponse::ok(ClearAndResetVideoStatusResponse {
|
||||||
|
warning,
|
||||||
|
video: VideoInfo {
|
||||||
|
id: video_info.id,
|
||||||
|
bvid: video_info.bvid,
|
||||||
|
name: video_info.name,
|
||||||
|
upper_name: video_info.upper_name,
|
||||||
|
should_download: video_info.should_download,
|
||||||
|
download_status: video_info.download_status,
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn reset_filtered_video_status(
|
pub async fn reset_filtered_video_status(
|
||||||
Extension(db): Extension<DatabaseConnection>,
|
Extension(db): Extension<DatabaseConnection>,
|
||||||
Json(request): Json<ResetFilteredVideoStatusRequest>,
|
Json(request): Json<ResetFilteredVideoStatusRequest>,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import type {
|
|||||||
VideosResponse,
|
VideosResponse,
|
||||||
VideoResponse,
|
VideoResponse,
|
||||||
ResetVideoResponse,
|
ResetVideoResponse,
|
||||||
|
ClearAndResetVideoResponse,
|
||||||
ResetFilteredVideosResponse,
|
ResetFilteredVideosResponse,
|
||||||
UpdateVideoStatusRequest,
|
UpdateVideoStatusRequest,
|
||||||
UpdateVideoStatusResponse,
|
UpdateVideoStatusResponse,
|
||||||
@@ -165,6 +166,10 @@ class ApiClient {
|
|||||||
return this.post<ResetVideoResponse>(`/videos/${id}/reset-status`, request);
|
return this.post<ResetVideoResponse>(`/videos/${id}/reset-status`, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async clearAndResetVideoStatus(id: number): Promise<ApiResponse<ClearAndResetVideoResponse>> {
|
||||||
|
return this.post<ClearAndResetVideoResponse>(`/videos/${id}/clear-and-reset-status`);
|
||||||
|
}
|
||||||
|
|
||||||
async resetFilteredVideoStatus(
|
async resetFilteredVideoStatus(
|
||||||
request: ResetFilteredVideoStatusRequest
|
request: ResetFilteredVideoStatusRequest
|
||||||
): Promise<ApiResponse<ResetFilteredVideosResponse>> {
|
): Promise<ApiResponse<ResetFilteredVideosResponse>> {
|
||||||
@@ -297,6 +302,7 @@ const api = {
|
|||||||
getVideo: (id: number) => apiClient.getVideo(id),
|
getVideo: (id: number) => apiClient.getVideo(id),
|
||||||
resetVideoStatus: (id: number, request: ResetVideoStatusRequest) =>
|
resetVideoStatus: (id: number, request: ResetVideoStatusRequest) =>
|
||||||
apiClient.resetVideoStatus(id, request),
|
apiClient.resetVideoStatus(id, request),
|
||||||
|
clearAndResetVideoStatus: (id: number) => apiClient.clearAndResetVideoStatus(id),
|
||||||
resetFilteredVideoStatus: (request: ResetFilteredVideoStatusRequest) =>
|
resetFilteredVideoStatus: (request: ResetFilteredVideoStatusRequest) =>
|
||||||
apiClient.resetFilteredVideoStatus(request),
|
apiClient.resetFilteredVideoStatus(request),
|
||||||
updateVideoStatus: (id: number, request: UpdateVideoStatusRequest) =>
|
updateVideoStatus: (id: number, request: UpdateVideoStatusRequest) =>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
import type { VideoInfo } from '$lib/types';
|
import type { VideoInfo } from '$lib/types';
|
||||||
import RotateCcwIcon from '@lucide/svelte/icons/rotate-ccw';
|
import RotateCcwIcon from '@lucide/svelte/icons/rotate-ccw';
|
||||||
import InfoIcon from '@lucide/svelte/icons/info';
|
import InfoIcon from '@lucide/svelte/icons/info';
|
||||||
|
import BrushCleaningIcon from '@lucide/svelte/icons/brush-cleaning';
|
||||||
import UserIcon from '@lucide/svelte/icons/user';
|
import UserIcon from '@lucide/svelte/icons/user';
|
||||||
import SquareArrowOutUpRightIcon from '@lucide/svelte/icons/square-arrow-out-up-right';
|
import SquareArrowOutUpRightIcon from '@lucide/svelte/icons/square-arrow-out-up-right';
|
||||||
import MoreHorizontalIcon from '@lucide/svelte/icons/more-horizontal';
|
import MoreHorizontalIcon from '@lucide/svelte/icons/more-horizontal';
|
||||||
@@ -24,8 +25,11 @@
|
|||||||
export let taskNames: string[] = []; // 自定义任务名称
|
export let taskNames: string[] = []; // 自定义任务名称
|
||||||
export let showProgress: boolean = true; // 是否显示进度信息
|
export let showProgress: boolean = true; // 是否显示进度信息
|
||||||
export let onReset: ((forceReset: boolean) => Promise<void>) | null = null; // 自定义重置函数
|
export let onReset: ((forceReset: boolean) => Promise<void>) | null = null; // 自定义重置函数
|
||||||
|
export let onClearAndReset: (() => Promise<void>) | null = null; // 自定义清空重置函数
|
||||||
export let resetDialogOpen = false; // 导出对话框状态,让父组件可以控制
|
export let resetDialogOpen = false; // 导出对话框状态,让父组件可以控制
|
||||||
|
export let clearAndResetDialogOpen = false; // 导出清空重置对话框状态
|
||||||
export let resetting = false;
|
export let resetting = false;
|
||||||
|
export let clearAndResetting = false;
|
||||||
|
|
||||||
let forceReset = false;
|
let forceReset = false;
|
||||||
|
|
||||||
@@ -98,6 +102,15 @@
|
|||||||
forceReset = false;
|
forceReset = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleClearAndReset() {
|
||||||
|
clearAndResetting = true;
|
||||||
|
if (onClearAndReset) {
|
||||||
|
await onClearAndReset();
|
||||||
|
}
|
||||||
|
clearAndResetting = false;
|
||||||
|
clearAndResetDialogOpen = false;
|
||||||
|
}
|
||||||
|
|
||||||
function handleViewDetail() {
|
function handleViewDetail() {
|
||||||
goto(`/video/${video.id}`);
|
goto(`/video/${video.id}`);
|
||||||
}
|
}
|
||||||
@@ -112,7 +125,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Card class={cardClasses}>
|
<Card class={cardClasses}>
|
||||||
<CardHeader class="flex-shrink-0 pb-3">
|
<CardHeader class="shrink-0 pb-3">
|
||||||
<div class="flex min-w-0 items-start justify-between gap-3">
|
<div class="flex min-w-0 items-start justify-between gap-3">
|
||||||
<CardTitle
|
<CardTitle
|
||||||
class="line-clamp-2 min-w-0 flex-1 cursor-default {mode === 'default'
|
class="line-clamp-2 min-w-0 flex-1 cursor-default {mode === 'default'
|
||||||
@@ -196,6 +209,17 @@
|
|||||||
{/snippet}
|
{/snippet}
|
||||||
</DropdownMenu.Trigger>
|
</DropdownMenu.Trigger>
|
||||||
<DropdownMenu.Content align="start" class="w-48">
|
<DropdownMenu.Content align="start" class="w-48">
|
||||||
|
<DropdownMenu.Item class="cursor-pointer" onclick={() => (resetDialogOpen = true)}>
|
||||||
|
<RotateCcwIcon class="mr-2 h-4 w-4" />
|
||||||
|
重置
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
<DropdownMenu.Item
|
||||||
|
class="cursor-pointer"
|
||||||
|
onclick={() => (clearAndResetDialogOpen = true)}
|
||||||
|
>
|
||||||
|
<BrushCleaningIcon class="mr-2 h-4 w-4" />
|
||||||
|
清空重置
|
||||||
|
</DropdownMenu.Item>
|
||||||
<DropdownMenu.Item
|
<DropdownMenu.Item
|
||||||
class="cursor-pointer"
|
class="cursor-pointer"
|
||||||
onclick={() =>
|
onclick={() =>
|
||||||
@@ -204,10 +228,6 @@
|
|||||||
<SquareArrowOutUpRightIcon class="mr-2 h-4 w-4" />
|
<SquareArrowOutUpRightIcon class="mr-2 h-4 w-4" />
|
||||||
在 B 站打开
|
在 B 站打开
|
||||||
</DropdownMenu.Item>
|
</DropdownMenu.Item>
|
||||||
<DropdownMenu.Item class="cursor-pointer" onclick={() => (resetDialogOpen = true)}>
|
|
||||||
<RotateCcwIcon class="mr-2 h-4 w-4" />
|
|
||||||
重置下载状态
|
|
||||||
</DropdownMenu.Item>
|
|
||||||
</DropdownMenu.Content>
|
</DropdownMenu.Content>
|
||||||
</DropdownMenu.Root>
|
</DropdownMenu.Root>
|
||||||
</div>
|
</div>
|
||||||
@@ -261,3 +281,38 @@
|
|||||||
</AlertDialog.Footer>
|
</AlertDialog.Footer>
|
||||||
</AlertDialog.Content>
|
</AlertDialog.Content>
|
||||||
</AlertDialog.Root>
|
</AlertDialog.Root>
|
||||||
|
|
||||||
|
<!-- 清空重置确认对话框 -->
|
||||||
|
<AlertDialog.Root bind:open={clearAndResetDialogOpen}>
|
||||||
|
<AlertDialog.Content>
|
||||||
|
<AlertDialog.Header>
|
||||||
|
<AlertDialog.Title>清空重置视频</AlertDialog.Title>
|
||||||
|
<AlertDialog.Description>
|
||||||
|
确定要清空重置视频 <strong>"{displayTitle}"</strong> 吗?
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
此操作会:
|
||||||
|
<ul class="mt-2 ml-4 list-disc space-y-1">
|
||||||
|
<li>将视频状态重置为未开始</li>
|
||||||
|
<li>删除所有分页信息</li>
|
||||||
|
<li class="text-destructive font-medium">删除视频对应的文件夹</li>
|
||||||
|
</ul>
|
||||||
|
<br />
|
||||||
|
该功能可在多页视频变更后手动触发全量更新,执行后<span class="text-destructive font-medium"
|
||||||
|
>无法撤销</span
|
||||||
|
>。
|
||||||
|
</AlertDialog.Description>
|
||||||
|
</AlertDialog.Header>
|
||||||
|
|
||||||
|
<AlertDialog.Footer>
|
||||||
|
<AlertDialog.Cancel>取消</AlertDialog.Cancel>
|
||||||
|
<AlertDialog.Action
|
||||||
|
onclick={handleClearAndReset}
|
||||||
|
disabled={clearAndResetting}
|
||||||
|
class="bg-destructive hover:bg-destructive/90"
|
||||||
|
>
|
||||||
|
{clearAndResetting ? '清空重置中...' : '确认清空重置'}
|
||||||
|
</AlertDialog.Action>
|
||||||
|
</AlertDialog.Footer>
|
||||||
|
</AlertDialog.Content>
|
||||||
|
</AlertDialog.Root>
|
||||||
|
|||||||
@@ -57,6 +57,11 @@ export interface ResetVideoResponse {
|
|||||||
pages: PageInfo[];
|
pages: PageInfo[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ClearAndResetVideoResponse {
|
||||||
|
warning?: string;
|
||||||
|
video: VideoInfo;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ResetFilteredVideosResponse {
|
export interface ResetFilteredVideosResponse {
|
||||||
resetted: boolean;
|
resetted: boolean;
|
||||||
resetted_videos_count: number;
|
resetted_videos_count: number;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
import type { ApiError, VideoResponse, UpdateVideoStatusRequest } from '$lib/types';
|
import type { ApiError, VideoResponse, UpdateVideoStatusRequest } from '$lib/types';
|
||||||
import RotateCcwIcon from '@lucide/svelte/icons/rotate-ccw';
|
import RotateCcwIcon from '@lucide/svelte/icons/rotate-ccw';
|
||||||
import EditIcon from '@lucide/svelte/icons/edit';
|
import EditIcon from '@lucide/svelte/icons/edit';
|
||||||
|
import BrushCleaningIcon from '@lucide/svelte/icons/brush-cleaning';
|
||||||
import { setBreadcrumb } from '$lib/stores/breadcrumb';
|
import { setBreadcrumb } from '$lib/stores/breadcrumb';
|
||||||
import { appStateStore, ToQuery } from '$lib/stores/filter';
|
import { appStateStore, ToQuery } from '$lib/stores/filter';
|
||||||
import VideoCard from '$lib/components/video-card.svelte';
|
import VideoCard from '$lib/components/video-card.svelte';
|
||||||
@@ -19,6 +20,8 @@
|
|||||||
let error: string | null = null;
|
let error: string | null = null;
|
||||||
let resetDialogOpen = false;
|
let resetDialogOpen = false;
|
||||||
let resetting = false;
|
let resetting = false;
|
||||||
|
let clearAndResetDialogOpen = false;
|
||||||
|
let clearAndResetting = false;
|
||||||
let statusEditorOpen = false;
|
let statusEditorOpen = false;
|
||||||
let statusEditorLoading = false;
|
let statusEditorLoading = false;
|
||||||
|
|
||||||
@@ -87,6 +90,56 @@
|
|||||||
statusEditorLoading = false;
|
statusEditorLoading = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleReset(forceReset: boolean) {
|
||||||
|
if (!videoData) return;
|
||||||
|
try {
|
||||||
|
const result = await api.resetVideoStatus(videoData.video.id, { force: forceReset });
|
||||||
|
const data = result.data;
|
||||||
|
if (data.resetted) {
|
||||||
|
videoData = {
|
||||||
|
video: data.video,
|
||||||
|
pages: data.pages
|
||||||
|
};
|
||||||
|
toast.success('重置成功');
|
||||||
|
} else {
|
||||||
|
toast.info('重置无效', {
|
||||||
|
description: `视频「${data.video.name}」没有失败的状态,无需重置`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('重置失败:', error);
|
||||||
|
toast.error('重置失败', {
|
||||||
|
description: (error as ApiError).message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleClearAndReset() {
|
||||||
|
if (!videoData) return;
|
||||||
|
try {
|
||||||
|
const result = await api.clearAndResetVideoStatus(videoData.video.id);
|
||||||
|
const data = result.data;
|
||||||
|
videoData = {
|
||||||
|
video: data.video,
|
||||||
|
pages: []
|
||||||
|
};
|
||||||
|
if (data.warning) {
|
||||||
|
toast.warning('清空重置成功', {
|
||||||
|
description: data.warning
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
toast.success('清空重置成功', {
|
||||||
|
description: `视频「${data.video.name}」已清空重置`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('清空重置失败:', error);
|
||||||
|
toast.error('清空重置失败', {
|
||||||
|
description: (error as ApiError).message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
@@ -130,11 +183,21 @@
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
class="shrink-0 cursor-pointer "
|
class="shrink-0 cursor-pointer "
|
||||||
onclick={() => (resetDialogOpen = true)}
|
onclick={() => (resetDialogOpen = true)}
|
||||||
disabled={resetting}
|
disabled={resetting || clearAndResetting}
|
||||||
>
|
>
|
||||||
<RotateCcwIcon class="mr-2 h-4 w-4 {resetting ? 'animate-spin' : ''}" />
|
<RotateCcwIcon class="mr-2 h-4 w-4 {resetting ? 'animate-spin' : ''}" />
|
||||||
重置
|
重置
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="outline"
|
||||||
|
class="shrink-0 cursor-pointer "
|
||||||
|
onclick={() => (clearAndResetDialogOpen = true)}
|
||||||
|
disabled={resetting || clearAndResetting}
|
||||||
|
>
|
||||||
|
<BrushCleaningIcon class="mr-2 h-4 w-4 {clearAndResetting ? 'animate-spin' : ''}" />
|
||||||
|
清空重置
|
||||||
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
@@ -164,28 +227,10 @@
|
|||||||
taskNames={['视频封面', '视频信息', 'UP 主头像', 'UP 主信息', '分页下载']}
|
taskNames={['视频封面', '视频信息', 'UP 主头像', 'UP 主信息', '分页下载']}
|
||||||
bind:resetDialogOpen
|
bind:resetDialogOpen
|
||||||
bind:resetting
|
bind:resetting
|
||||||
onReset={async (forceReset: boolean) => {
|
bind:clearAndResetDialogOpen
|
||||||
try {
|
bind:clearAndResetting
|
||||||
const result = await api.resetVideoStatus(videoData!.video.id, { force: forceReset });
|
onReset={handleReset}
|
||||||
const data = result.data;
|
onClearAndReset={handleClearAndReset}
|
||||||
if (data.resetted) {
|
|
||||||
videoData = {
|
|
||||||
video: data.video,
|
|
||||||
pages: data.pages
|
|
||||||
};
|
|
||||||
toast.success('重置成功');
|
|
||||||
} else {
|
|
||||||
toast.info('重置无效', {
|
|
||||||
description: `视频「${data.video.name}」没有失败的状态,无需重置`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('重置失败:', error);
|
|
||||||
toast.error('重置失败', {
|
|
||||||
description: (error as ApiError).message
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -226,7 +271,6 @@
|
|||||||
<div class="py-12 text-center">
|
<div class="py-12 text-center">
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<p class="text-muted-foreground">暂无分 P 数据</p>
|
<p class="text-muted-foreground">暂无分 P 数据</p>
|
||||||
<p class="text-muted-foreground text-sm">该视频可能为单 P 视频</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -131,6 +131,29 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleClearAndResetVideo(id: number) {
|
||||||
|
try {
|
||||||
|
const result = await api.clearAndResetVideoStatus(id);
|
||||||
|
const data = result.data;
|
||||||
|
if (data.warning) {
|
||||||
|
toast.warning('清空重置成功', {
|
||||||
|
description: data.warning
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
toast.success('清空重置成功', {
|
||||||
|
description: `视频「${data.video.name}」已清空重置`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const { query, currentPage, videoSource } = $appStateStore;
|
||||||
|
await loadVideos(query, currentPage, videoSource);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('清空重置失败:', error);
|
||||||
|
toast.error('清空重置失败', {
|
||||||
|
description: (error as ApiError).message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function handleResetAllVideos() {
|
async function handleResetAllVideos() {
|
||||||
resettingAll = true;
|
resettingAll = true;
|
||||||
try {
|
try {
|
||||||
@@ -332,6 +355,9 @@
|
|||||||
onReset={async (forceReset: boolean) => {
|
onReset={async (forceReset: boolean) => {
|
||||||
await handleResetVideo(video.id, forceReset);
|
await handleResetVideo(video.id, forceReset);
|
||||||
}}
|
}}
|
||||||
|
onClearAndReset={async () => {
|
||||||
|
await handleClearAndResetVideo(video.id);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user