- 新增可复用的后台分页组件 `Pagination.svelte` - 文件管理 (`/admin/files`) 服务端查询增加分页逻辑(默认 24 条/页),前端接入分页导航 - 标签管理 (`/admin/tags`) 服务端查询增加分页逻辑(默认 20 条/页),前端接入分页导航
This commit is contained in:
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>
|
||||
@@ -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,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;
|
||||
|
||||
@@ -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,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>
|
||||
|
||||
|
||||
Reference in new issue
Block a user