feat: 后台文件管理与标签管理增加分页功能
continuous-integration/drone/push Build is passing

- 新增可复用的后台分页组件 `Pagination.svelte`
- 文件管理 (`/admin/files`) 服务端查询增加分页逻辑(默认 24 条/页),前端接入分页导航
- 标签管理 (`/admin/tags`) 服务端查询增加分页逻辑(默认 20 条/页),前端接入分页导航
This commit is contained in:
seahi committed 2026-09-26 15:01:17 +08:00
1 parent f92abc126d
commit 19606301de
5 files changed
+114 -9

No files matched your search

@@ -0,0 +1,60 @@
<script lang="ts">
import Button from './Button.svelte';
let {
page,
totalPages,
path = ''
}: {
page: number;
totalPages: number;
path?: string;
} = $props();
</script>
{#if totalPages > 1}
<div class="pagination">
<Button
variant="secondary"
href={page > 1 ? `${path}?page=${page - 1}` : undefined}
class={page <= 1 ? 'disabled' : ''}
aria-disabled={page <= 1}
>
上一页
</Button>
<span class="page-info">{page} / {totalPages}</span>
<Button
variant="secondary"
href={page < totalPages ? `${path}?page=${page + 1}` : undefined}
class={page >= totalPages ? 'disabled' : ''}
aria-disabled={page >= totalPages}
>
下一页
</Button>
</div>
{/if}
<style>
.pagination {
display: flex;
align-items: center;
justify-content: center;
gap: 15px;
margin-top: 30px;
}
.page-info {
font-weight: 600;
font-size: 0.95rem;
min-width: 60px;
text-align: center;
}
:global(.pagination .btn.disabled) {
opacity: 0.5;
cursor: not-allowed;
pointer-events: none;
}
</style>
+23 -4
View File
@@ -11,30 +11,49 @@ type Attachment = {
created_at: string;
};
export const load: PageServerLoad = async ({ platform }) => {
export const load: PageServerLoad = async ({ url, platform }) => {
const db = platform?.env.DB;
if (!db) return { files: [], r2Domain: '' };
if (!db)
return { files: [], pagination: { page: 1, totalPages: 0, totalItems: 0 }, r2Domain: '' };
const page = Number(url.searchParams.get('page')) || 1;
const limit = 24;
const offset = (page - 1) * limit;
try {
const domainResult = await db
.prepare('SELECT value FROM settings WHERE key = ?')
.bind('r2.public_domain')
.first<{ value: string }>();
const countResult = await db
.prepare('SELECT COUNT(*) as count FROM attachments')
.first<{ count: number }>();
const totalItems = countResult?.count || 0;
const totalPages = Math.ceil(totalItems / limit);
const { results } = await db
.prepare(
`SELECT id, filename, key, mime_type, size, created_at
FROM attachments
ORDER BY created_at DESC`
ORDER BY created_at DESC
LIMIT ? OFFSET ?`
)
.bind(limit, offset)
.all<Attachment>();
return {
files: results,
pagination: { page, totalPages, totalItems },
r2Domain: domainResult?.value || 'https://assets.yourdomain.com'
};
} catch (cause) {
console.error('Error fetching attachments:', cause);
return { files: [], r2Domain: 'https://assets.yourdomain.com' };
return {
files: [],
pagination: { page: 1, totalPages: 0, totalItems: 0 },
r2Domain: 'https://assets.yourdomain.com'
};
}
};
+7
View File
@@ -7,6 +7,7 @@
import { addToast } from '$lib/stores/toast.svelte';
import { confirmState } from '$lib/stores/confirm.svelte';
import { prepareUpload } from '$lib/uploads';
import Pagination from '$lib/components/admin/Pagination.svelte';
let { data, form } = $props<{ data: PageData; form: ActionData }>();
@@ -213,6 +214,12 @@
{/if}
</div>
<Pagination
page={data.pagination.page}
totalPages={data.pagination.totalPages}
path="/admin/files"
/>
<style>
.admin-page-header {
display: flex;
+18 -5
View File
@@ -3,14 +3,24 @@
import { fail } from '@sveltejs/kit';
import type { Actions, PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ platform }) => {
export const load: PageServerLoad = async ({ url, platform }) => {
const db = platform?.env?.DB;
if (!db) {
return { tags: [] };
return { tags: [], pagination: { page: 1, totalPages: 0, totalItems: 0 } };
}
const page = Number(url.searchParams.get('page')) || 1;
const limit = 20;
const offset = (page - 1) * limit;
try {
const countResult = await db
.prepare('SELECT COUNT(*) as count FROM tags')
.first<{ count: number }>();
const totalItems = countResult?.count || 0;
const totalPages = Math.ceil(totalItems / limit);
// 查询所有标签以及对应的文章数量
const { results } = await db
.prepare(
@@ -18,16 +28,19 @@ export const load: PageServerLoad = async ({ platform }) => {
FROM tags t
LEFT JOIN post_tags pt ON t.id = pt.tag_id
GROUP BY t.id, t.name
ORDER BY t.name ASC`
ORDER BY t.name ASC
LIMIT ? OFFSET ?`
)
.bind(limit, offset)
.all();
return {
tags: results as unknown as { id: number; name: string; post_count: number }[]
tags: results as unknown as { id: number; name: string; post_count: number }[],
pagination: { page, totalPages, totalItems }
};
} catch (err: any) {
console.error('Error fetching tags:', err);
return { tags: [] };
return { tags: [], pagination: { page: 1, totalPages: 0, totalItems: 0 } };
}
};
+6
View File
@@ -6,6 +6,7 @@
import Button from '$lib/components/admin/Button.svelte';
import DataTable from '$lib/components/admin/DataTable.svelte';
import Alert from '$lib/components/admin/Alert.svelte';
import Pagination from '$lib/components/admin/Pagination.svelte';
let { data, form }: { data: PageData; form: ActionData } = $props();
@@ -125,6 +126,11 @@
{/each}
</tbody>
</DataTable>
<Pagination
page={data.pagination.page}
totalPages={data.pagination.totalPages}
path="/admin/tags"
/>
{/if}
</div>