feat: 支持 webui 加载用户的订阅与收藏,一键点击订阅 (#357)

This commit is contained in:
ᴀᴍᴛᴏᴀᴇʀ
2025-06-09 11:16:33 +08:00
committed by GitHub
parent 586d5ec4ee
commit a98e49347b
23 changed files with 1513 additions and 38 deletions

View File

@@ -1,2 +1,2 @@
export const ssr = false;
export const prerender = true;
export const prerender = false;

View File

@@ -0,0 +1,108 @@
<script lang="ts">
import { onMount } from 'svelte';
import { toast } from 'svelte-sonner';
import { goto } from '$app/navigation';
import SubscriptionCard from '$lib/components/subscription-card.svelte';
import Pagination from '$lib/components/pagination.svelte';
import { setBreadcrumb } from '$lib/stores/breadcrumb';
import { appStateStore, ToQuery } from '$lib/stores/filter';
import api from '$lib/api';
import type { CollectionWithSubscriptionStatus, ApiError } from '$lib/types';
let collections: CollectionWithSubscriptionStatus[] = [];
let totalCount = 0;
let currentPage = 0;
let loading = false;
const pageSize = 50;
async function loadCollections(page: number = 0) {
loading = true;
try {
const response = await api.getFollowedCollections(page + 1, pageSize); // API使用1基索引
collections = response.data.collections;
totalCount = response.data.total;
} catch (error) {
console.error('加载合集失败:', error);
toast.error('加载合集失败', {
description: (error as ApiError).message
});
} finally {
loading = false;
}
}
function handleSubscriptionSuccess() {
// 重新加载数据以获取最新状态
loadCollections(currentPage);
}
async function handlePageChange(page: number) {
currentPage = page;
await loadCollections(page);
}
onMount(async () => {
setBreadcrumb([
{
label: '主页',
onClick: () => {
goto(`/${ToQuery($appStateStore)}`);
}
},
{
label: '关注的合集',
isActive: true
}
]);
await loadCollections();
});
$: totalPages = Math.ceil(totalCount / pageSize);
</script>
<svelte:head>
<title>关注的合集 - Bili Sync</title>
</svelte:head>
<div class="max-w-6xl">
<div class="mb-6 flex items-center justify-between">
<div>
<h1 class="text-2xl font-bold">关注的合集</h1>
<p class="text-muted-foreground mt-1">管理您在B站关注的合集订阅</p>
</div>
<div class="text-muted-foreground text-sm">
{#if !loading}
{totalCount} 个合集
{/if}
</div>
</div>
{#if loading}
<div class="flex items-center justify-center py-12">
<div class="text-muted-foreground">加载中...</div>
</div>
{:else if collections.length > 0}
<div class="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
{#each collections as collection (collection.id)}
<SubscriptionCard
item={collection}
type="collection"
onSubscriptionSuccess={handleSubscriptionSuccess}
/>
{/each}
</div>
<!-- 分页组件 -->
{#if totalPages > 1}
<Pagination {currentPage} {totalPages} onPageChange={handlePageChange} />
{/if}
{:else}
<div class="flex items-center justify-center py-12">
<div class="space-y-2 text-center">
<p class="text-muted-foreground">暂无合集数据</p>
<p class="text-muted-foreground text-sm">请先在B站关注一些合集或检查账号配置</p>
</div>
</div>
{/if}
</div>

View File

@@ -0,0 +1,88 @@
<script lang="ts">
import { onMount } from 'svelte';
import { toast } from 'svelte-sonner';
import { goto } from '$app/navigation';
import SubscriptionCard from '$lib/components/subscription-card.svelte';
import { setBreadcrumb } from '$lib/stores/breadcrumb';
import { appStateStore, ToQuery } from '$lib/stores/filter';
import api from '$lib/api';
import type { FavoriteWithSubscriptionStatus, ApiError } from '$lib/types';
let favorites: FavoriteWithSubscriptionStatus[] = [];
let loading = false;
async function loadFavorites() {
loading = true;
try {
const response = await api.getCreatedFavorites();
favorites = response.data.favorites;
} catch (error) {
console.error('加载收藏夹失败:', error);
toast.error('加载收藏夹失败', {
description: (error as ApiError).message
});
} finally {
loading = false;
}
}
function handleSubscriptionSuccess() {
// 重新加载数据以获取最新状态
loadFavorites();
}
onMount(async () => {
setBreadcrumb([
{
label: '主页',
onClick: () => {
goto(`/${ToQuery($appStateStore)}`);
}
},
{ label: '我的收藏夹', isActive: true }
]);
await loadFavorites();
});
</script>
<svelte:head>
<title>我的收藏夹 - Bili Sync</title>
</svelte:head>
<div class="max-w-6xl">
<div class="mb-6 flex items-center justify-between">
<div>
<h1 class="text-2xl font-bold">我的收藏夹</h1>
<p class="text-muted-foreground mt-1">管理您在B站创建的收藏夹订阅</p>
</div>
<div class="text-muted-foreground text-sm">
{#if !loading}
{favorites.length} 个收藏夹
{/if}
</div>
</div>
{#if loading}
<div class="flex items-center justify-center py-12">
<div class="text-muted-foreground">加载中...</div>
</div>
{:else if favorites.length > 0}
<div class="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
{#each favorites as favorite (favorite.fid)}
<SubscriptionCard
item={favorite}
type="favorite"
onSubscriptionSuccess={handleSubscriptionSuccess}
/>
{/each}
</div>
{:else}
<div class="flex items-center justify-center py-12">
<div class="space-y-2 text-center">
<p class="text-muted-foreground">暂无收藏夹数据</p>
<p class="text-muted-foreground text-sm">请先在B站创建收藏夹或检查账号配置</p>
</div>
</div>
{/if}
</div>

View File

@@ -0,0 +1,106 @@
<script lang="ts">
import { onMount } from 'svelte';
import { toast } from 'svelte-sonner';
import { goto } from '$app/navigation';
import SubscriptionCard from '$lib/components/subscription-card.svelte';
import Pagination from '$lib/components/pagination.svelte';
import { setBreadcrumb } from '$lib/stores/breadcrumb';
import { appStateStore, ToQuery } from '$lib/stores/filter';
import api from '$lib/api';
import type { UpperWithSubscriptionStatus, ApiError } from '$lib/types';
let uppers: UpperWithSubscriptionStatus[] = [];
let totalCount = 0;
let currentPage = 0;
let loading = false;
const pageSize = 50;
async function loadUppers(page: number = 0) {
loading = true;
try {
const response = await api.getFollowedUppers(page + 1, pageSize); // API使用1基索引
uppers = response.data.uppers;
totalCount = response.data.total;
} catch (error) {
console.error('加载UP主失败:', error);
toast.error('加载UP主失败', {
description: (error as ApiError).message
});
} finally {
loading = false;
}
}
function handleSubscriptionSuccess() {
// 重新加载数据以获取最新状态
loadUppers(currentPage);
}
async function handlePageChange(page: number) {
currentPage = page;
await loadUppers(page);
}
onMount(async () => {
setBreadcrumb([
{
label: '主页',
onClick: () => {
goto(`/${ToQuery($appStateStore)}`);
}
},
{ label: '关注的UP主', isActive: true }
]);
await loadUppers();
});
$: totalPages = Math.ceil(totalCount / pageSize);
</script>
<svelte:head>
<title>关注的UP主 - Bili Sync</title>
</svelte:head>
<div class="max-w-6xl">
<div class="mb-6 flex items-center justify-between">
<div>
<h1 class="text-2xl font-bold">关注的UP主</h1>
<p class="text-muted-foreground mt-1">管理您在B站关注的UP主投稿订阅</p>
</div>
<div class="text-muted-foreground text-sm">
{#if !loading}
{totalCount} 个UP主
{/if}
</div>
</div>
{#if loading}
<div class="flex items-center justify-center py-12">
<div class="text-muted-foreground">加载中...</div>
</div>
{:else if uppers.length > 0}
<div class="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
{#each uppers as upper (upper.mid)}
<SubscriptionCard
item={upper}
type="upper"
onSubscriptionSuccess={handleSubscriptionSuccess}
/>
{/each}
</div>
<!-- 分页组件 -->
{#if totalPages > 1}
<Pagination {currentPage} {totalPages} onPageChange={handlePageChange} />
{/if}
{:else}
<div class="flex items-center justify-center py-12">
<div class="space-y-2 text-center">
<p class="text-muted-foreground">暂无UP主数据</p>
<p class="text-muted-foreground text-sm">请先在B站关注一些UP主或检查账号配置</p>
</div>
</div>
{/if}
</div>

View File

@@ -156,7 +156,7 @@
bind:resetting
onReset={async () => {
try {
const result = await api.resetVideo((videoData as VideoResponse).video.id);
const result = await api.resetVideo(videoData!.video.id);
const data = result.data;
if (data.resetted) {
videoData = {

View File

@@ -1,2 +0,0 @@
export const ssr = false;
export const prerender = false;