- 新增可复用的后台分页组件 `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;
|
created_at: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const load: PageServerLoad = async ({ platform }) => {
|
export const load: PageServerLoad = async ({ url, platform }) => {
|
||||||
const db = platform?.env.DB;
|
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 {
|
try {
|
||||||
const domainResult = await db
|
const domainResult = await db
|
||||||
.prepare('SELECT value FROM settings WHERE key = ?')
|
.prepare('SELECT value FROM settings WHERE key = ?')
|
||||||
.bind('r2.public_domain')
|
.bind('r2.public_domain')
|
||||||
.first<{ value: string }>();
|
.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
|
const { results } = await db
|
||||||
.prepare(
|
.prepare(
|
||||||
`SELECT id, filename, key, mime_type, size, created_at
|
`SELECT id, filename, key, mime_type, size, created_at
|
||||||
FROM attachments
|
FROM attachments
|
||||||
ORDER BY created_at DESC`
|
ORDER BY created_at DESC
|
||||||
|
LIMIT ? OFFSET ?`
|
||||||
)
|
)
|
||||||
|
.bind(limit, offset)
|
||||||
.all<Attachment>();
|
.all<Attachment>();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
files: results,
|
files: results,
|
||||||
|
pagination: { page, totalPages, totalItems },
|
||||||
r2Domain: domainResult?.value || 'https://assets.yourdomain.com'
|
r2Domain: domainResult?.value || 'https://assets.yourdomain.com'
|
||||||
};
|
};
|
||||||
} catch (cause) {
|
} catch (cause) {
|
||||||
console.error('Error fetching attachments:', 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 { addToast } from '$lib/stores/toast.svelte';
|
||||||
import { confirmState } from '$lib/stores/confirm.svelte';
|
import { confirmState } from '$lib/stores/confirm.svelte';
|
||||||
import { prepareUpload } from '$lib/uploads';
|
import { prepareUpload } from '$lib/uploads';
|
||||||
|
import Pagination from '$lib/components/admin/Pagination.svelte';
|
||||||
|
|
||||||
let { data, form } = $props<{ data: PageData; form: ActionData }>();
|
let { data, form } = $props<{ data: PageData; form: ActionData }>();
|
||||||
|
|
||||||
@@ -213,6 +214,12 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<Pagination
|
||||||
|
page={data.pagination.page}
|
||||||
|
totalPages={data.pagination.totalPages}
|
||||||
|
path="/admin/files"
|
||||||
|
/>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.admin-page-header {
|
.admin-page-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -3,14 +3,24 @@
|
|||||||
import { fail } from '@sveltejs/kit';
|
import { fail } from '@sveltejs/kit';
|
||||||
import type { Actions, PageServerLoad } from './$types';
|
import type { Actions, PageServerLoad } from './$types';
|
||||||
|
|
||||||
export const load: PageServerLoad = async ({ platform }) => {
|
export const load: PageServerLoad = async ({ url, platform }) => {
|
||||||
const db = platform?.env?.DB;
|
const db = platform?.env?.DB;
|
||||||
|
|
||||||
if (!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 {
|
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
|
const { results } = await db
|
||||||
.prepare(
|
.prepare(
|
||||||
@@ -18,16 +28,19 @@ export const load: PageServerLoad = async ({ platform }) => {
|
|||||||
FROM tags t
|
FROM tags t
|
||||||
LEFT JOIN post_tags pt ON t.id = pt.tag_id
|
LEFT JOIN post_tags pt ON t.id = pt.tag_id
|
||||||
GROUP BY t.id, t.name
|
GROUP BY t.id, t.name
|
||||||
ORDER BY t.name ASC`
|
ORDER BY t.name ASC
|
||||||
|
LIMIT ? OFFSET ?`
|
||||||
)
|
)
|
||||||
|
.bind(limit, offset)
|
||||||
.all();
|
.all();
|
||||||
|
|
||||||
return {
|
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) {
|
} catch (err: any) {
|
||||||
console.error('Error fetching tags:', err);
|
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 Button from '$lib/components/admin/Button.svelte';
|
||||||
import DataTable from '$lib/components/admin/DataTable.svelte';
|
import DataTable from '$lib/components/admin/DataTable.svelte';
|
||||||
import Alert from '$lib/components/admin/Alert.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();
|
let { data, form }: { data: PageData; form: ActionData } = $props();
|
||||||
|
|
||||||
@@ -125,6 +126,11 @@
|
|||||||
{/each}
|
{/each}
|
||||||
</tbody>
|
</tbody>
|
||||||
</DataTable>
|
</DataTable>
|
||||||
|
<Pagination
|
||||||
|
page={data.pagination.page}
|
||||||
|
totalPages={data.pagination.totalPages}
|
||||||
|
path="/admin/tags"
|
||||||
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user