好的,让我们来实现标签云和热门标签功能。这将帮助用户快速浏览热门话题,并提供一种视觉上吸引人的方式来展示博客的内容分布。

第十九部分:实现标签云和热门标签功能

  1. 首先,我们需要在数据库中添加标签相关的表。更新 src/db/schema.ts 文件:
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
 
// ... 保留现有的表定义
 
export const tags = sqliteTable("tags", {
  id: integer("id").primaryKey(),
  name: text("name").notNull().unique(),
});
 
export const postTags = sqliteTable("post_tags", {
  id: integer("id").primaryKey(),
  postId: integer("post_id").notNull().references(() => posts.id),
  tagId: integer("tag_id").notNull().references(() => tags.id),
});
  1. 创建一个新的迁移文件来添加这些表。在 src/db/migrations 目录下创建一个新文件,例如 0004_add_tags.ts
import { sqliteTable, integer, text } from "drizzle-orm/sqlite-core";
 
export async function up(db) {
  await db.run(sql`
    CREATE TABLE tags (
      id INTEGER PRIMARY KEY,
      name TEXT NOT NULL UNIQUE
    );
 
    CREATE TABLE post_tags (
      id INTEGER PRIMARY KEY,
      post_id INTEGER NOT NULL,
      tag_id INTEGER NOT NULL,
      FOREIGN KEY (post_id) REFERENCES posts(id),
      FOREIGN KEY (tag_id) REFERENCES tags(id)
    );
  `);
}
 
export async function down(db) {
  await db.run(sql`
    DROP TABLE post_tags;
    DROP TABLE tags;
  `);
}
  1. 运行迁移:
bun run migrate
  1. 更新 src/routes/posts.ts 文件,添加标签相关的逻辑:
import { Elysia } from "elysia";
import { db } from "../db";
import { posts, users, categories, postCategories, tags, postTags } from "../db/schema";
import { eq, sql } from "drizzle-orm";
 
export const postRoutes = new Elysia()
  // ... 保留现有的路由
 
  .post("/posts", async ({ body, set }) => {
    const { title, content, categoryIds, tags: tagNames } = body;
    
    const post = await db.transaction(async (tx) => {
      const [newPost] = await tx.insert(posts).values({
        title,
        content,
        authorId: 1, // 假设用户已登录,这里应该使用实际的用户ID
        createdAt: new Date(),
        updatedAt: new Date(),
      }).returning();
 
      if (categoryIds && categoryIds.length > 0) {
        await tx.insert(postCategories).values(
          categoryIds.map(categoryId => ({
            postId: newPost.id,
            categoryId: parseInt(categoryId),
          }))
        );
      }
 
      if (tagNames && tagNames.length > 0) {
        for (const tagName of tagNames) {
          let [tag] = await tx.select().from(tags).where(eq(tags.name, tagName)).limit(1);
          if (!tag) {
            [tag] = await tx.insert(tags).values({ name: tagName }).returning();
          }
          await tx.insert(postTags).values({ postId: newPost.id, tagId: tag.id });
        }
      }
 
      return newPost;
    });
 
    set.redirect = `/posts/${post.id}`;
  })
 
  // ... 保留其他路由
  1. 创建一个新的路由文件 src/routes/tags.ts 来处理标签相关的请求:
import { Elysia } from "elysia";
import { db } from "../db";
import { tags, postTags } from "../db/schema";
import { sql } from "drizzle-orm";
 
export const tagRoutes = new Elysia()
  .get("/tags", async ({ render }) => {
    const tagCounts = await db
      .select({
        id: tags.id,
        name: tags.name,
        count: sql<number>`count(${postTags.id})`.as('count'),
      })
      .from(tags)
      .leftJoin(postTags, sql`${tags.id} = ${postTags.tagId}`)
      .groupBy(tags.id)
      .orderBy(sql`count DESC`)
      .limit(20)
      .all();
 
    return render("tags/cloud", { tags: tagCounts });
  });
  1. 创建一个新的视图文件 src/views/tags/cloud.eta 来显示标签云:
<% layout('../layouts/main.eta', { title: 'Tag Cloud' }) %>
 
<h1>Tag Cloud</h1>
 
<div class="tag-cloud">
  <% it.tags.forEach(tag => { %>
    <a href="/search?tag=<%= tag.name %>" class="tag" style="font-size: <%= 100 + tag.count * 20 %>%;">
      <%= tag.name %>
    </a>
  <% }) %>
</div>
  1. 更新 src/index.ts 文件,添加新的标签路由:
import { tagRoutes } from "./routes/tags";
 
// ... 其他导入保持不变
 
app.use(tagRoutes);
 
// ... 其他代码保持不变
  1. 更新 public/styles/main.css 文件,添加标签云的样式:
/* ... 前面的样式保持不变 */
 
.tag-cloud {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  padding: 20px;
}
 
.tag-cloud .tag {
  display: inline-block;
  margin: 5px;
  padding: 5px 10px;
  background-color: #f0f0f0;
  border-radius: 15px;
  color: #333;
  text-decoration: none;
  transition: background-color 0.3s ease;
}
 
.tag-cloud .tag:hover {
  background-color: #e0e0e0;
}
  1. 更新主布局文件 src/views/layouts/main.eta,添加标签云链接:
<header>
  <nav>
    <a href="/">Home</a>
    <a href="/posts/new">New Post</a>
    <% if (it.user) { %>
      <a href="/logout">Logout</a>
    <% } else { %>
      <a href="/login">Login</a>
      <a href="/register">Register</a>
    <% } %>
    <a href="/search">Advanced Search</a>
    <a href="/tags">Tag Cloud</a>
  </nav>
</header>
  1. 更新 src/views/posts/new.etasrc/views/posts/edit.eta 文件,添加标签输入字段:
<!-- ... 其他表单字段保持不变 -->
<div>
  <label for="tags">Tags (comma separated):</label>
  <input type="text" id="tags" name="tags" value="<%= it.post ? it.post.tags.join(', ') : '' %>">
</div>
<!-- ... -->

这些更改实现了标签云和热门标签功能:

  1. 创建了新的数据库表来存储标签信息。
  2. 更新了文章创建和编辑功能,支持添加标签。
  3. 实现了一个标签云页面,显示最常用的标签。
  4. 添加了相应的样式,使标签云看起来更加美观。

主要改进包括:

  • 用户可以为文章添加标签,提高内容的可发现性。
  • 标签云提供了一种视觉上吸引人的方式来浏览博客的主题。
  • 热门标签功能帮助用户快速了解博客的主要内容方向。

这个功能不仅提高了博客的可用性,还为用户提供了一种新的内容导航方式。

接下来,我们可以考虑添加以下功能:

  1. 用户头像上传
  2. 文章归档功能
  3. 评论系统
  4. 相关文章推荐

你希望继续哪个方向,或者有其他想法吗?