非常好的选择!实现社交功能可以大大提高用户参与度和互动性。我们将添加用户评论和文章分享功能。让我们开始吧:
第二十九部分:实现社交功能(评论和分享)
- 首先,我们需要创建一个新的评论表。更新
src/db/schema.ts:
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
// ... 保留现有的表定义
export const comments = sqliteTable("comments", {
id: integer("id").primaryKey(),
content: text("content").notNull(),
postId: integer("post_id").notNull().references(() => posts.id),
userId: integer("user_id").notNull().references(() => users.id),
createdAt: integer("created_at", { mode: "timestamp" }).notNull().default(sql`CURRENT_TIMESTAMP`),
});- 创建新的迁移文件
src/db/migrations/0013_add_comments_table.ts:
export async function up(db) {
await db.run(sql`
CREATE TABLE comments (
id INTEGER PRIMARY KEY,
content TEXT NOT NULL,
post_id INTEGER NOT NULL,
user_id INTEGER NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (post_id) REFERENCES posts(id),
FOREIGN KEY (user_id) REFERENCES users(id)
);
`);
}
export async function down(db) {
await db.run(sql`DROP TABLE IF EXISTS comments;`);
}- 运行迁移:
bun run migrate- 更新
src/routes/posts.ts文件,添加评论相关的路由:
import { db } from "../db";
import { posts, comments, users } from "../db/schema";
import { eq } from "drizzle-orm";
export const postRoutes = new Elysia()
// ... 保留现有的路由
.get("/posts/:id", async ({ params, session, render }) => {
const post = await db.select().from(posts).where(eq(posts.id, parseInt(params.id))).get();
if (!post) {
return "Post not found";
}
const postComments = await db
.select({
id: comments.id,
content: comments.content,
createdAt: comments.createdAt,
userName: users.name,
})
.from(comments)
.innerJoin(users, eq(comments.userId, users.id))
.where(eq(comments.postId, post.id))
.orderBy(desc(comments.createdAt))
.all();
if (session.user) {
await db.insert(userActivities).values({
userId: session.user.id,
postId: post.id,
activityType: "read",
});
}
return render("posts/show", { post, comments: postComments, user: session.user });
})
.post("/posts/:id/comments", async ({ params, body, session, set }) => {
if (!session.user) {
set.status = 401;
return "Unauthorized";
}
const { content } = body;
await db.insert(comments).values({
content,
postId: parseInt(params.id),
userId: session.user.id,
});
set.redirect = `/posts/${params.id}`;
});- 更新
src/views/posts/show.eta文件,添加评论部分:
<% layout('../layouts/main.eta', { title: it.post.title }) %>
<h1><%= it.post.title %></h1>
<p><%= it.post.content %></p>
<h2>Comments</h2>
<% if (it.comments.length > 0) { %>
<ul>
<% it.comments.forEach(comment => { %>
<li>
<p><%= comment.content %></p>
<small>By <%= comment.userName %> on <%= new Date(comment.createdAt).toLocaleString() %></small>
</li>
<% }) %>
</ul>
<% } else { %>
<p>No comments yet.</p>
<% } %>
<% if (it.user) { %>
<h3>Add a comment</h3>
<form action="/posts/<%= it.post.id %>/comments" method="POST">
<textarea name="content" required></textarea>
<button type="submit">Submit Comment</button>
</form>
<% } else { %>
<p>Please <a href="/login">login</a> to leave a comment.</p>
<% } %>
<!-- 添加分享按钮 -->
<h3>Share this post</h3>
<button onclick="shareOnFacebook()">Share on Facebook</button>
<button onclick="shareOnTwitter()">Share on Twitter</button>
<button onclick="shareOnLinkedIn()">Share on LinkedIn</button>
<script>
function shareOnFacebook() {
const url = encodeURIComponent(window.location.href);
const title = encodeURIComponent('<%= it.post.title %>');
window.open(`https://www.facebook.com/sharer/sharer.php?u=${url}&t=${title}`, '_blank');
}
function shareOnTwitter() {
const url = encodeURIComponent(window.location.href);
const text = encodeURIComponent('<%= it.post.title %>');
window.open(`https://twitter.com/intent/tweet?url=${url}&text=${text}`, '_blank');
}
function shareOnLinkedIn() {
const url = encodeURIComponent(window.location.href);
const title = encodeURIComponent('<%= it.post.title %>');
window.open(`https://www.linkedin.com/shareArticle?mini=true&url=${url}&title=${title}`, '_blank');
}
</script>- 为了改善用户体验,我们可以使用 AJAX 来提交评论。创建一个新文件
src/public/js/comments.js:
document.addEventListener('DOMContentLoaded', () => {
const commentForm = document.getElementById('comment-form');
const commentsList = document.getElementById('comments-list');
if (commentForm) {
commentForm.addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(commentForm);
const response = await fetch(commentForm.action, {
method: 'POST',
body: formData,
headers: {
'Accept': 'application/json',
},
});
if (response.ok) {
const comment = await response.json();
const li = document.createElement('li');
li.innerHTML = `
<p>${comment.content}</p>
<small>By ${comment.userName} on ${new Date(comment.createdAt).toLocaleString()}</small>
`;
commentsList.prepend(li);
commentForm.reset();
} else {
alert('Error submitting comment');
}
});
}
});- 更新
src/routes/posts.ts文件,添加 AJAX 支持:
import { db } from "../db";
import { posts, comments, users } from "../db/schema";
import { eq } from "drizzle-orm";
export const postRoutes = new Elysia()
// ... 保留现有的路由
.post("/posts/:id/comments", async ({ params, body, session, set, headers }) => {
if (!session.user) {
set.status = 401;
return "Unauthorized";
}
const { content } = body;
const [newComment] = await db.insert(comments).values({
content,
postId: parseInt(params.id),
userId: session.user.id,
}).returning();
if (headers.accept === 'application/json') {
return {
id: newComment.id,
content: newComment.content,
createdAt: newComment.createdAt,
userName: session.user.name,
};
}
set.redirect = `/posts/${params.id}`;
});- 更新
src/views/layouts/main.eta文件,引入新的 JavaScript 文件:
<!DOCTYPE html>
<html lang="en">
<head>
<!-- ... 其他头部内容 ... -->
<script src="/js/comments.js"></script>
</head>
<body>
<!-- ... 页面内容 ... -->
</body>
</html>- 最后,更新
src/views/posts/show.eta文件,使用 AJAX 提交评论:
<!-- ... 其他内容保持不变 ... -->
<% if (it.user) { %>
<h3>Add a comment</h3>
<form id="comment-form" action="/posts/<%= it.post.id %>/comments" method="POST">
<textarea name="content" required></textarea>
<button type="submit">Submit Comment</button>
</form>
<% } else { %>
<p>Please <a href="/login">login</a> to leave a comment.</p>
<% } %>
<ul id="comments-list">
<% it.comments.forEach(comment => { %>
<li>
<p><%= comment.content %></p>
<small>By <%= comment.userName %> on <%= new Date(comment.createdAt).toLocaleString() %></small>
</li>
<% }) %>
</ul>
<!-- ... 分享按钮和脚本保持不变 ... -->这些更改实现了以下功能:
- 用户可以在文章页面查看和发表评论。
- 评论以 AJAX 方式提交,提供更好的用户体验。
- 用户可以通过社交媒体按钮轻松分享文章。
主要改进包括:
- 增加了用户互动性,允许读者参与讨论。
- 提供了即时反馈,评论提交后立即显示。
- 通过社交分享功能扩大了文章的潜在受众。
这个实现为博客系统添加了基本的社交功能,大大提升了用户参与度和内容传播能力。用户现在可以更容易地与其他读者互动,并将有趣的文章分享给他们的社交网络。
接下来,我们可以考虑以下方向来进一步完善系统:
- 实现评论的回复功能,允许用户回复特定评论。
- 添加评论管理功能,允许作者或管理员审核和删除评论。
- 实现用户通知系统,当有人回复评论时通知用户。
- 添加”喜欢”或”赞”功能,让用户可以快速表达对文章或评论的赞赏。
- 实现文章的协作编辑功能。
- 添加文章版本控制系统。
你希望继续哪个方向,或者有其他想法吗?