好的,我们将按照您的兴趣,依次实现分页、搜索和用户详细资料这三个功能。让我们开始吧:

第七部分:分页、搜索和用户详细资料

  1. 首先,让我们实现分页功能。更新 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,
    };
  })
  // ... 其他路由保持不变
  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>
<% } %>
  1. 现在,让我们实现搜索功能。更新 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,
    };
  })
  // ... 其他路由保持不变
  1. 更新 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>
 
<!-- 其余部分保持不变 -->
  1. 最后,让我们实现用户详细资料页面。首先,创建一个新的路由文件 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 };
  });
  1. 创建一个新的视图 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>
<% } %>
  1. 更新 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}`);
  1. 更新 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>
 
<!-- 其余部分保持不变 -->
  1. 最后,更新 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;
}

这些更改实现了分页、搜索和用户详细资料功能:

  1. 分页:现在主页上的帖子列表支持分页,每页显示 10 篇帖子。
  2. 搜索:用户可以通过标题搜索帖子。
  3. 用户详细资料:每个用户都有一个个人资料页面,显示他们的基本信息和发布的帖子列表。

这些功能大大提升了博客的可用性和功能性。用户现在可以更轻松地浏览和查找内容,也可以了解其他用户的活动。

接下来,我们可以考虑添加评论系统、标签功能或改进的用户设置页面。您希望继续哪个方向?