From 3b9ed8af71bf965ec2be519bfb22408c36e737ce Mon Sep 17 00:00:00 2001 From: seaHi Date: Sat, 8 Aug 2026 15:25:14 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=88=86=E7=B1=BB?= =?UTF-8?q?=E6=9F=A5=E7=9C=8B=E5=8A=9F=E8=83=BD=E5=8F=8A=E5=AF=B9=E5=BA=94?= =?UTF-8?q?=E7=9A=84=E8=B7=AF=E7=94=B1=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/routes/+layout.svelte | 5 + src/routes/categories/+page.server.ts | 31 ++++++ src/routes/categories/+page.svelte | 69 +++++++++++++ src/routes/categories/[slug]/+page.server.ts | 100 +++++++++++++++++++ src/routes/categories/[slug]/+page.svelte | 99 ++++++++++++++++++ 5 files changed, 304 insertions(+) create mode 100644 src/routes/categories/+page.server.ts create mode 100644 src/routes/categories/+page.svelte create mode 100644 src/routes/categories/[slug]/+page.server.ts create mode 100644 src/routes/categories/[slug]/+page.svelte diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index b0955dd..a3f784e 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -52,6 +52,11 @@ class={$page.url.pathname.startsWith('/series') ? 'active' : ''} >{$page.data.aliases?.series || '系列'} + {$page.data.aliases?.category || '分类'} 关于 { + const db = platform?.env?.DB; + if (!db) throw error(500, '数据库未连接'); + try { + const { results } = await db + .prepare( + `SELECT c.id, c.name, c.slug, COUNT(p.id) AS post_count + FROM categories c + LEFT JOIN posts p ON p.category_id = c.id + GROUP BY c.id, c.name, c.slug + ORDER BY c.name COLLATE NOCASE ASC` + ) + .all(); + return { + categories: results as unknown as Array<{ + id: number; + name: string; + slug: string; + post_count: number; + }> + }; + } catch (err: any) { + console.error('Error fetching public categories list:', err); + throw error(500, '读取分类列表失败'); + } +}; diff --git a/src/routes/categories/+page.svelte b/src/routes/categories/+page.svelte new file mode 100644 index 0000000..6437d4c --- /dev/null +++ b/src/routes/categories/+page.svelte @@ -0,0 +1,69 @@ + + + + 全部{$page.data.aliases?.category || '分类'} - {$page.data.site?.title || 'BlogFlare'} + + +
+
+ +
+
+ + diff --git a/src/routes/categories/[slug]/+page.server.ts b/src/routes/categories/[slug]/+page.server.ts new file mode 100644 index 0000000..97a4b78 --- /dev/null +++ b/src/routes/categories/[slug]/+page.server.ts @@ -0,0 +1,100 @@ +import { error, isHttpError } from '@sveltejs/kit'; +import type { PageServerLoad } from './$types'; + +const PAGE_SIZE = 20; + +type CategoryPostRow = { + id: number; + slug: string; + title: string; + cover: string | null; + date: string; + excerpt_source: string; +}; + +function createExcerpt(source: string): string { + const plainText = source + .replace(/\{\{<[\s\S]*?>\}\}/g, '') + .replace(/<[^>]+>/g, '') + .replace(/[#*`>~[\]()-]/g, '') + .replace(/\s+/g, ' ') + .trim() + .slice(0, 150) + .trim(); + + return plainText ? `${plainText}...` : ''; +} + +export const load: PageServerLoad = async ({ platform, params, url }) => { + const db = platform?.env.DB; + if (!db) throw error(500, '数据库未连接'); + + try { + const category = await db + .prepare('SELECT id, name, slug FROM categories WHERE slug = ? COLLATE NOCASE') + .bind(params.slug) + .first<{ id: number; name: string; slug: string }>(); + if (!category) throw error(404, '该分类不存在'); + + const countResult = await db + .prepare('SELECT COUNT(*) AS total FROM posts WHERE category_id = ?') + .bind(category.id) + .first<{ total: number }>(); + const total = countResult?.total ?? 0; + const totalPages = Math.max(1, Math.ceil(total / PAGE_SIZE)); + const requestedPage = Number(url.searchParams.get('page') ?? '1'); + const page = Number.isInteger(requestedPage) + ? Math.min(Math.max(requestedPage, 1), totalPages) + : 1; + + const { results } = await db + .prepare( + `SELECT + p.id, + p.slug, + p.title, + p.cover, + p.published_at AS date, + CASE + WHEN instr(p.content, '') > 0 + THEN substr(p.content, 1, instr(p.content, '') - 1) + ELSE substr(p.content, 1, 500) + END AS excerpt_source + FROM posts p + WHERE p.category_id = ? + ORDER BY p.published_at DESC, p.id DESC + LIMIT ? OFFSET ?` + ) + .bind(category.id, PAGE_SIZE, (page - 1) * PAGE_SIZE) + .all(); + + const posts = results.map((post) => { + const date = new Date(post.date); + return { + id: post.id, + title: post.title, + slug: post.slug, + cover: post.cover, + date: Number.isNaN(date.getTime()) + ? post.date + : date.toLocaleDateString('en-US', { + month: 'short', + day: 'numeric', + year: 'numeric', + timeZone: 'UTC' + }), + excerpt: createExcerpt(post.excerpt_source || '') + }; + }); + + return { + category, + posts, + pagination: { page, pageSize: PAGE_SIZE, total, totalPages } + }; + } catch (cause) { + if (isHttpError(cause, 404)) throw cause; + console.error('Error loading category posts:', cause); + throw error(500, '读取分类内容列表失败'); + } +}; diff --git a/src/routes/categories/[slug]/+page.svelte b/src/routes/categories/[slug]/+page.svelte new file mode 100644 index 0000000..d032b38 --- /dev/null +++ b/src/routes/categories/[slug]/+page.svelte @@ -0,0 +1,99 @@ + + + + {$page.data.aliases?.category || '分类'}:{data.category.name} - {$page.data.site?.title || + 'BlogFlare'} + + +
+
+
+ +
+
+ +
+
+ {#each data.posts as post (post.id)} + +
+ {#if post.cover} +
+ {post.title} +
+ {/if} +
+ +

{post.title}

+

{post.excerpt}

+
+
+
+ {:else} +

现阶段尚未在此{$page.data.aliases?.category || '分类'}下发布任何{$page.data.aliases?.post || '文章'}。

+ {/each} +
+ + {#if data.pagination.totalPages > 1} + + {/if} +
+
+ +