refactor: replace writable stores with reactive variables, update media category labels to Chinese, and streamline event handling

This commit is contained in:
ethan.chen
2025-05-23 17:43:30 +08:00
parent 27ceef4bd2
commit 96becb0363

View File

@@ -1,7 +1,5 @@
<script lang="ts"> <script lang="ts">
import { AppBar, AppRail, AppRailTile } from '@skeletonlabs/skeleton';
import { fade } from 'svelte/transition'; import { fade } from 'svelte/transition';
import { writable } from 'svelte/store';
import request from './lib/request'; import request from './lib/request';
// 类型定义 // 类型定义
@@ -32,65 +30,71 @@
} }
// 状态管理 // 状态管理
const isAuthenticated = writable(false); let isAuthenticated = $state(false);
const username = writable(''); let username = $state('');
const password = writable(''); let password = $state('');
const error = writable(''); let error = $state('');
// 媒体列表状态 // 媒体列表状态
const currentCategory = writable('game'); let currentCategory = $state('game');
const mediaList = writable<Media[]>([]); let mediaList = $state<Media[]>([]);
const currentPage = writable(1); let currentPage = $state(1);
const pageSize = writable(10); let pageSize = $state(10);
const totalItems = writable(0); let totalItems = $state(0);
const loading = writable(false); let loading = $state(false);
// 类别选项 // 类别选项
const categories = [ const categories = [
{ id: 'game', label: 'Games' }, { id: 'game', label: '游戏' },
{ id: 'book', label: 'Books' }, { id: 'book', label: '书籍' },
{ id: 'movie', label: 'Movies' }, { id: 'movie', label: '电影' },
{ id: 'anime', label: 'Anime' } { id: 'anime', label: '番剧' },
{ id: 'other', label: '其他' }
]; ];
// 获取媒体列表 // 获取媒体列表
async function fetchMediaList() { async function fetchMediaList() {
loading.set(true); loading = true;
try { try {
const response = await request.get<ApiResponse<PageResponse>>('/media/page', { const response = await request.get<ApiResponse<PageResponse>>('/media/page', {
params: { params: {
type: $currentCategory, type: currentCategory,
currentPage: $currentPage, currentPage: currentPage,
pageSize: $pageSize pageSize: pageSize
} }
}); });
if (response.data.code === 0) { if (response.data.code === 0) {
mediaList.set(response.data.data.list); mediaList = response.data.data.list;
totalItems.set(response.data.data.total); totalItems = response.data.data.total;
} }
} catch (e: any) { } catch (e: any) {
error.set(e.message || 'Failed to fetch media list'); error = e.message || 'Failed to fetch media list';
} finally { } finally {
loading.set(false); loading = false;
} }
} }
// 监听类别变化 // 监听类别变化
$: if ($isAuthenticated) { $effect(() => {
currentPage.set(1); if (isAuthenticated && currentCategory) {
currentPage = 1;
fetchMediaList(); fetchMediaList();
} }
});
// 监听分页变化 // 监听分页变化
$: if ($isAuthenticated && $currentPage) { $effect(() => {
if (isAuthenticated && currentPage) {
fetchMediaList(); fetchMediaList();
} }
});
// 登录处理 // 登录处理
async function handleLogin() { async function handleLogin(e: Event) {
e.preventDefault();
try { try {
// 保存认证信息 // 保存认证信息
const auth = btoa(`${$username}:${$password}`); const auth = btoa(`${username}:${password}`);
localStorage.setItem('auth', auth); localStorage.setItem('auth', auth);
// 使用 page 接口验证登录 // 使用 page 接口验证登录
@@ -103,17 +107,17 @@
}); });
if (response.data.code === 0) { if (response.data.code === 0) {
isAuthenticated.set(true); isAuthenticated = true;
error.set(''); error = '';
// 设置初始数据 // 设置初始数据
mediaList.set(response.data.data.list); mediaList = response.data.data.list;
totalItems.set(response.data.data.total); totalItems = response.data.data.total;
} else { } else {
error.set(response.data.message || 'Invalid username or password'); error = response.data.message || 'Invalid username or password';
localStorage.removeItem('auth'); localStorage.removeItem('auth');
} }
} catch (e: any) { } catch (e: any) {
error.set(e.message || 'Connection error'); error = e.message || 'Connection error';
localStorage.removeItem('auth'); localStorage.removeItem('auth');
} }
} }
@@ -123,7 +127,7 @@
<title>My Score</title> <title>My Score</title>
</svelte:head> </svelte:head>
{#if !$isAuthenticated} {#if !isAuthenticated}
<div class="min-h-screen flex items-center justify-center bg-gray-100" transition:fade> <div class="min-h-screen flex items-center justify-center bg-gray-100" transition:fade>
<div class="w-[480px] space-y-8 p-8 bg-white rounded-lg shadow-lg"> <div class="w-[480px] space-y-8 p-8 bg-white rounded-lg shadow-lg">
<div> <div>
@@ -134,7 +138,7 @@
Sign in to your account Sign in to your account
</p> </p>
</div> </div>
<form class="mt-8 space-y-6" on:submit|preventDefault={handleLogin}> <form class="mt-8 space-y-6" onsubmit={handleLogin}>
<div class="space-y-4"> <div class="space-y-4">
<div> <div>
<label for="username" class="block text-sm font-medium text-gray-700">Username</label> <label for="username" class="block text-sm font-medium text-gray-700">Username</label>
@@ -145,7 +149,7 @@
required required
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm" class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
placeholder="Enter your username" placeholder="Enter your username"
bind:value={$username} bind:value={username}
/> />
</div> </div>
<div> <div>
@@ -157,14 +161,14 @@
required required
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm" class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
placeholder="Enter your password" placeholder="Enter your password"
bind:value={$password} bind:value={password}
/> />
</div> </div>
</div> </div>
{#if $error} {#if error}
<div class="text-red-500 text-sm text-center"> <div class="text-red-500 text-sm text-center">
{$error} {error}
</div> </div>
{/if} {/if}
@@ -188,8 +192,8 @@
<div class="flex space-x-6"> <div class="flex space-x-6">
{#each categories as category} {#each categories as category}
<button <button
class="px-4 py-2 rounded-md text-sm font-medium {$currentCategory === category.id ? 'bg-indigo-600 text-white' : 'bg-gray-100 text-gray-700 hover:bg-gray-200'}" class="px-4 py-2 rounded-md text-sm font-medium {currentCategory === category.id ? 'bg-indigo-600 text-white' : 'bg-gray-100 text-gray-700 hover:bg-gray-200'}"
on:click={() => currentCategory.set(category.id)} onclick={() => currentCategory = category.id}
> >
{category.label} {category.label}
</button> </button>
@@ -201,14 +205,14 @@
<!-- 媒体列表 --> <!-- 媒体列表 -->
<div class="bg-white shadow rounded-lg"> <div class="bg-white shadow rounded-lg">
<div class="px-6 py-4"> <div class="px-6 py-4">
{#if $loading} {#if loading}
<div class="text-center py-8"> <div class="text-center py-8">
<div class="animate-spin rounded-full h-8 w-8 border-b-2 border-indigo-600 mx-auto"></div> <div class="animate-spin rounded-full h-8 w-8 border-b-2 border-indigo-600 mx-auto"></div>
</div> </div>
{:else} {:else}
<div class="grid grid-cols-2 gap-6"> <div class="grid grid-cols-2 gap-6">
{#each $mediaList as media} {#each mediaList as media}
<div class="border rounded-lg p-6 hover:bg-gray-50"> <div class="border rounded-lg p-3 hover:bg-gray-50">
<div class="flex justify-between items-start"> <div class="flex justify-between items-start">
<div class="space-y-2"> <div class="space-y-2">
<h3 class="text-lg font-medium text-gray-900">{media.title}</h3> <h3 class="text-lg font-medium text-gray-900">{media.title}</h3>
@@ -233,20 +237,20 @@
<!-- 分页控制 --> <!-- 分页控制 -->
<div class="mt-6 flex justify-between items-center"> <div class="mt-6 flex justify-between items-center">
<div class="text-sm text-gray-700"> <div class="text-sm text-gray-700">
Showing {($currentPage - 1) * $pageSize + 1} to {Math.min($currentPage * $pageSize, $totalItems)} of {$totalItems} items Showing {(currentPage - 1) * pageSize + 1} to {Math.min(currentPage * pageSize, totalItems)} of {totalItems} items
</div> </div>
<div class="flex space-x-2"> <div class="flex space-x-2">
<button <button
class="px-4 py-2 rounded-md text-sm font-medium {$currentPage === 1 ? 'bg-gray-100 text-gray-400 cursor-not-allowed' : 'bg-white text-gray-700 hover:bg-gray-50 border'}" class="px-4 py-2 rounded-md text-sm font-medium {currentPage === 1 ? 'bg-gray-100 text-gray-400 cursor-not-allowed' : 'bg-white text-gray-700 hover:bg-gray-50 border'}"
disabled={$currentPage === 1} disabled={currentPage === 1}
on:click={() => currentPage.set($currentPage - 1)} onclick={() => currentPage = currentPage - 1}
> >
Previous Previous
</button> </button>
<button <button
class="px-4 py-2 rounded-md text-sm font-medium {$currentPage * $pageSize >= $totalItems ? 'bg-gray-100 text-gray-400 cursor-not-allowed' : 'bg-white text-gray-700 hover:bg-gray-50 border'}" class="px-4 py-2 rounded-md text-sm font-medium {currentPage * pageSize >= totalItems ? 'bg-gray-100 text-gray-400 cursor-not-allowed' : 'bg-white text-gray-700 hover:bg-gray-50 border'}"
disabled={$currentPage * $pageSize >= $totalItems} disabled={currentPage * pageSize >= totalItems}
on:click={() => currentPage.set($currentPage + 1)} onclick={() => currentPage = currentPage + 1}
> >
Next Next
</button> </button>