feat: 新增帖子最后回复展示、文章目录与三栏布局,优化点赞按钮与导航栏
这是一次包含多个功能升级的提交: 1. 后端新增批量查询帖子最后回复的逻辑,在前端帖子列表项中展示最后回复信息 2. 重构点赞按钮,新增文章样式变体并统一样式逻辑 3. 重写工作台导航栏,精简默认展示的菜单选项 4. 新增文章目录组件,为帖子详情页添加文章目录导航 5. 重构帖子详情页,实现三栏响应式布局,新增板块卡片、作者信息栏与同板块热议推荐 6. 重构帖子列表行组件,优化排版与信息展示逻辑 7. 重构帖子管理操作组件,使用下拉菜单整合所有管理功能 8. 新增全局通用的滚动容器与点赞按钮样式
This commit is contained in:
@@ -43,23 +43,100 @@ func toPostListItems(posts []model.Post) []PostListItem {
|
||||
return items
|
||||
}
|
||||
|
||||
// LastReplyUser 最后回复人(仅列表展示所需字段)
|
||||
type LastReplyUser struct {
|
||||
ID uint `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Nickname string `json:"nickname"`
|
||||
Avatar string `json:"avatar"`
|
||||
}
|
||||
|
||||
// LastReplyInfo 帖子最后一条已发布评论的摘要
|
||||
type LastReplyInfo struct {
|
||||
User LastReplyUser `json:"user"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
// PostListItem 帖子列表项(不含正文)
|
||||
type PostListItem struct {
|
||||
ID uint `json:"id"`
|
||||
BoardID uint `json:"board_id"`
|
||||
UserID uint `json:"user_id"`
|
||||
Title string `json:"title"`
|
||||
Tags string `json:"tags"`
|
||||
PostType string `json:"post_type"`
|
||||
Pinned int `json:"pinned"`
|
||||
Recommended bool `json:"recommended"`
|
||||
LikeCount int `json:"like_count"`
|
||||
ViewCount int `json:"view_count"`
|
||||
CommentCount int `json:"comment_count"`
|
||||
Liked bool `json:"liked"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
Board model.Board `json:"board"`
|
||||
User model.User `json:"user"`
|
||||
ID uint `json:"id"`
|
||||
BoardID uint `json:"board_id"`
|
||||
UserID uint `json:"user_id"`
|
||||
Title string `json:"title"`
|
||||
Tags string `json:"tags"`
|
||||
PostType string `json:"post_type"`
|
||||
Pinned int `json:"pinned"`
|
||||
Recommended bool `json:"recommended"`
|
||||
LikeCount int `json:"like_count"`
|
||||
ViewCount int `json:"view_count"`
|
||||
CommentCount int `json:"comment_count"`
|
||||
Liked bool `json:"liked"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
LastReply *LastReplyInfo `json:"last_reply,omitempty"`
|
||||
Board model.Board `json:"board"`
|
||||
User model.User `json:"user"`
|
||||
}
|
||||
|
||||
// fillLastReply 批量填充每帖最后一条已发布评论(发帖人+时间),
|
||||
// 单条 SQL 取每帖最新评论,再批量取用户,避免 N+1
|
||||
func (s *PostService) fillLastReply(items []PostListItem) {
|
||||
if len(items) == 0 {
|
||||
return
|
||||
}
|
||||
postIDs := make([]uint, 0, len(items))
|
||||
for _, it := range items {
|
||||
postIDs = append(postIDs, it.ID)
|
||||
}
|
||||
|
||||
type latestRow struct {
|
||||
PostID uint
|
||||
UserID uint
|
||||
CreatedAt time.Time
|
||||
}
|
||||
var rows []latestRow
|
||||
if err := s.db.Raw(`
|
||||
SELECT DISTINCT ON (post_id) post_id, user_id, created_at
|
||||
FROM comments
|
||||
WHERE post_id IN ? AND status = 'published' AND deleted_at IS NULL
|
||||
ORDER BY post_id, created_at DESC`, postIDs).Scan(&rows).Error; err != nil || len(rows) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
userIDs := make([]uint, 0, len(rows))
|
||||
seen := map[uint]bool{}
|
||||
for _, r := range rows {
|
||||
if !seen[r.UserID] {
|
||||
seen[r.UserID] = true
|
||||
userIDs = append(userIDs, r.UserID)
|
||||
}
|
||||
}
|
||||
var users []model.User
|
||||
if err := s.db.Select("id, username, nickname, avatar").Where("id IN ?", userIDs).Find(&users).Error; err != nil {
|
||||
return
|
||||
}
|
||||
userMap := make(map[uint]model.User, len(users))
|
||||
for _, u := range users {
|
||||
userMap[u.ID] = u
|
||||
}
|
||||
|
||||
replyMap := make(map[uint]latestRow, len(rows))
|
||||
for _, r := range rows {
|
||||
replyMap[r.PostID] = r
|
||||
}
|
||||
for i := range items {
|
||||
r, ok := replyMap[items[i].ID]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
u, ok := userMap[r.UserID]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
items[i].LastReply = &LastReplyInfo{
|
||||
User: LastReplyUser{ID: u.ID, Username: u.Username, Nickname: u.Nickname, Avatar: u.Avatar},
|
||||
CreatedAt: r.CreatedAt,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// List 获取帖子列表
|
||||
@@ -104,7 +181,9 @@ func (s *PostService) List(q PostListQuery) ([]PostListItem, int64, error) {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return toPostListItems(posts), total, nil
|
||||
items := toPostListItems(posts)
|
||||
s.fillLastReply(items)
|
||||
return items, total, nil
|
||||
}
|
||||
|
||||
// ListByUser 获取指定用户发布的帖子(公开已发布)
|
||||
@@ -126,7 +205,9 @@ func (s *PostService) ListByUser(userID uint, page, size int) ([]PostListItem, i
|
||||
Preload("Board").Preload("User").Find(&posts).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return toPostListItems(posts), total, nil
|
||||
items := toPostListItems(posts)
|
||||
s.fillLastReply(items)
|
||||
return items, total, nil
|
||||
}
|
||||
|
||||
// GetAuthorID 获取帖子作者 ID(不增加浏览量)
|
||||
|
||||
Reference in New Issue
Block a user