好的,我们将按照您的兴趣,依次实现分页、搜索和用户详细资料这三个功能。让我们开始吧:
第七部分:分页、搜索和用户详细资料
- 首先,让我们实现分页功能。更新
src/routes/posts.ts:
import { Elysia, t } from "elysia";
import { db } from "../db";
import { posts, users } from "../db/schema";
import { eq, like } from "drizzle-orm";
import { authMiddleware } from "../middleware/auth";
const POSTS_PER_PAGE = 10;
export const postRoutes = new Elysia()
.use(authMiddleware)
.get("/posts", async ({ query }) => {
const page = parseInt(query.page as string) || 1;
const offset = (page - 1) * POSTS_PER_PAGE;
const [allPosts, totalCount] = await Promise.all([
db.select({
id: posts.id,
title: posts.title,
content: posts.content,
createdAt: posts.createdAt,
author: users.username,
})
.from(posts)
.leftJoin(users, eq(posts.authorId, users.id))
.limit(POSTS_PER_PAGE)
.offset(offset)
.orderBy(posts.createdAt)
.all(),
db.select({ count: sql`count(*)` }).from(posts).get().then(result => result.count),
]);
const totalPages = Math.ceil(totalCount / POSTS_PER_PAGE);
return {
posts: allPosts,
currentPage: page,
totalPages,
hasNextPage: page < totalPages,
hasPrevPage: page > 1,
};
})
// ... 其他路由保持不变- 更新
src/views/home.eta以支持分页:
<% layout('./layouts/main.eta', { title: 'Home' }) %>
<h1>Latest Posts</h1>
<% if (it.posts.length > 0) { %>
<ul>
<% it.posts.forEach(function(post) { %>
<li>
<h2><a href="/posts/<%= post.id %>"><%= post.title %></a></h2>
<p>By <%= post.author %> on <%= new Date(post.createdAt).toLocaleDateString() %></p>
</li>
<% }) %>
</ul>
<div class="pagination">
<% if (it.hasPrevPage) { %>
<a href="?page=<%= it.currentPage - 1 %>">Previous</a>
<% } %>
<span>Page <%= it.currentPage %> of <%= it.totalPages %></span>
<% if (it.hasNextPage) { %>
<a href="?page=<%= it.currentPage + 1 %>">Next</a>
<% } %>
</div>
<% } else { %>
<p>No posts found.</p>
<% } %>
<% if (it.user) { %>
<a href="/create-post">Create New Post</a>
<% } %>- 现在,让我们实现搜索功能。更新
src/routes/posts.ts:
// ... 前面的代码保持不变
export const postRoutes = new Elysia()
.use(authMiddleware)
.get("/posts", async ({ query }) => {
const page = parseInt(query.page as string) || 1;
const search = (query.search as string) || '';
const offset = (page - 1) * POSTS_PER_PAGE;
const searchCondition = like(posts.title, `%${search}%`);
const [allPosts, totalCount] = await Promise.all([
db.select({
id: posts.id,
title: posts.title,
content: posts.content,
createdAt: posts.createdAt,
author: users.username,
})
.from(posts)
.leftJoin(users, eq(posts.authorId, users.id))
.where(searchCondition)
.limit(POSTS_PER_PAGE)
.offset(offset)
.orderBy(posts.createdAt)
.all(),
db.select({ count: sql`count(*)` }).from(posts).where(searchCondition).get().then(result => result.count),
]);
const totalPages = Math.ceil(totalCount / POSTS_PER_PAGE);
return {
posts: allPosts,
currentPage: page,
totalPages,
hasNextPage: page < totalPages,
hasPrevPage: page > 1,
search,
};
})
// ... 其他路由保持不变- 更新
src/views/home.eta以添加搜索表单:
<% layout('./layouts/main.eta', { title: 'Home' }) %>
<h1>Latest Posts</h1>
<form action="/" method="get">
<input type="text" name="search" value="<%= it.search %>" placeholder="Search posts...">
<button type="submit">Search</button>
</form>
<!-- 其余部分保持不变 -->
<div class="pagination">
<% if (it.hasPrevPage) { %>
<a href="?page=<%= it.currentPage - 1 %>&search=<%= it.search %>">Previous</a>
<% } %>
<span>Page <%= it.currentPage %> of <%= it.totalPages %></span>
<% if (it.hasNextPage) { %>
<a href="?page=<%= it.currentPage + 1 %>&search=<%= it.search %>">Next</a>
<% } %>
</div>
<!-- 其余部分保持不变 -->- 最后,让我们实现用户详细资料页面。首先,创建一个新的路由文件
src/routes/users.ts:
import { Elysia } from "elysia";
import { db } from "../db";
import { users, posts } from "../db/schema";
import { eq } from "drizzle-orm";
import { authMiddleware } from "../middleware/auth";
export const userRoutes = new Elysia()
.use(authMiddleware)
.get("/users/:id", async ({ params, set }) => {
const user = await db.select({
id: users.id,
username: users.username,
email: users.email,
createdAt: users.createdAt,
}).from(users).where(eq(users.id, parseInt(params.id))).get();
if (!user) {
set.status = 404;
return { error: "User not found" };
}
const userPosts = await db.select({
id: posts.id,
title: posts.title,
createdAt: posts.createdAt,
}).from(posts).where(eq(posts.authorId, user.id)).all();
return { user, posts: userPosts };
});- 创建一个新的视图
src/views/user-profile.eta:
<% layout('./layouts/main.eta', { title: 'User Profile' }) %>
<h1><%= it.user.username %>'s Profile</h1>
<p>Email: <%= it.user.email %></p>
<p>Joined: <%= new Date(it.user.createdAt).toLocaleDateString() %></p>
<h2>Posts by <%= it.user.username %></h2>
<% if (it.posts.length > 0) { %>
<ul>
<% it.posts.forEach(function(post) { %>
<li>
<a href="/posts/<%= post.id %>"><%= post.title %></a>
<span>(<%= new Date(post.createdAt).toLocaleDateString() %>)</span>
</li>
<% }) %>
</ul>
<% } else { %>
<p>No posts found.</p>
<% } %>- 更新
src/index.ts以包含新的用户路由:
import { Elysia } from "elysia";
import { html } from "@elysiajs/html";
import { staticPlugin } from "@elysiajs/static";
import { authRoutes, auth } from "./routes/auth";
import { postRoutes } from "./routes/posts";
import { userRoutes } from "./routes/users";
import { migrate } from "drizzle-orm/libsql/migrator";
import { db } from "./db";
import { renderFile } from "eta";
import path from "path";
import { posts, users } from "./db/schema";
import { eq } from "drizzle-orm";
// ... 前面的代码保持不变
const app = new Elysia()
.use(html())
.use(staticPlugin())
.use(auth)
.use(authRoutes)
.use(postRoutes)
.use(userRoutes)
// ... 其他路由保持不变
.get("/users/:id", async ({ params, set, getCurrentUser }) => {
const userData = await userRoutes.handle({ method: 'GET', path: `/users/${params.id}` });
if (userData.error) {
set.status = 404;
return "User not found";
}
const currentUser = await getCurrentUser();
set.headers['Content-Type'] = 'text/html';
return await renderFile(path.join(__dirname, "views", "user-profile.eta"), { ...userData, currentUser });
})
.listen(3000);
console.log(`Server is running at ${app.server?.hostname}:${app.server?.port}`);- 更新
src/views/post.eta以链接到作者的个人资料:
<% layout('./layouts/main.eta', { title: it.post.title }) %>
<article id="post-<%= it.post.id %>">
<h1><%= it.post.title %></h1>
<p>By <a href="/users/<%= it.post.authorId %>"><%= it.post.author %></a> on <%= new Date(it.post.createdAt).toLocaleDateString() %></p>
<div><%= it.post.content %></div>
</article>
<!-- 其余部分保持不变 -->- 最后,更新
public/styles/main.css以添加一些新的样式:
/* ... 前面的样式保持不变 */
.pagination {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 2rem;
}
.search-form {
margin-bottom: 2rem;
}
.search-form input {
width: 70%;
padding: 0.5rem;
}
.search-form button {
width: 25%;
padding: 0.5rem;
}
.user-profile {
background-color: #f4f4f4;
padding: 2rem;
border-radius: 8px;
margin-bottom: 2rem;
}
.user-posts {
list-style-type: none;
padding: 0;
}
.user-posts li {
margin-bottom: 1rem;
}这些更改实现了分页、搜索和用户详细资料功能:
- 分页:现在主页上的帖子列表支持分页,每页显示 10 篇帖子。
- 搜索:用户可以通过标题搜索帖子。
- 用户详细资料:每个用户都有一个个人资料页面,显示他们的基本信息和发布的帖子列表。
这些功能大大提升了博客的可用性和功能性。用户现在可以更轻松地浏览和查找内容,也可以了解其他用户的活动。
接下来,我们可以考虑添加评论系统、标签功能或改进的用户设置页面。您希望继续哪个方向?