//! 评论列表组件 //! //! 将已审核评论与待审核评论合并成一棵树并按时间排序渲染。 use dioxus::prelude::*; use crate::components::comments::item::CommentItem; use crate::components::comments::pending_item::PendingCommentItem; use crate::utils::comment_storage::PendingComment; use crate::models::comment::PublicComment; /// 合并后的评论节点,可能是已审核或待审核评论。 #[derive(Clone)] enum MergedComment { Approved(PublicComment), Pending(PendingComment), } /// 合并两类评论并构建成树形结构。 /// /// 处理逻辑: /// - 将已审核与待审核评论统一为 `MergedComment` /// - 若某条评论的 parent_id 不存在于当前集合中,则视为顶层评论 /// - 同一父节点下的子评论按时间排序 /// - 使用 DFS 前序遍历生成最终展示顺序 fn merge_and_treeify( approved: Vec, pending: Vec, ) -> Vec { use std::collections::{HashMap, HashSet}; let all: Vec = approved .into_iter() .map(MergedComment::Approved) .chain(pending.into_iter().map(MergedComment::Pending)) .collect(); let all_ids: HashSet = all .iter() .map(|c| match c { MergedComment::Approved(c) => c.id, MergedComment::Pending(c) => c.id, }) .collect(); // 按 parent_id 分组,处理指向不存在父节点的 parent_id let mut children_map: HashMap, Vec> = HashMap::new(); for comment in all { let parent_id = match &comment { MergedComment::Approved(c) => c.parent_id, MergedComment::Pending(c) => c.parent_id, }; let effective_parent = match parent_id { Some(pid) if !all_ids.contains(&pid) => None, _ => parent_id, }; children_map .entry(effective_parent) .or_default() .push(comment); } // 每个父节点下的子评论按创建时间排序 for children in children_map.values_mut() { children.sort_by(|a, b| { let time_a = match a { MergedComment::Approved(c) => c.created_at_iso.as_str(), MergedComment::Pending(c) => c.created_at.as_str(), }; let time_b = match b { MergedComment::Approved(c) => c.created_at_iso.as_str(), MergedComment::Pending(c) => c.created_at.as_str(), }; time_a.cmp(time_b) }); } // 深度优先遍历生成树形顺序 fn dfs( parent_id: Option, children_map: &HashMap, Vec>, result: &mut Vec, ) { if let Some(children) = children_map.get(&parent_id) { for child in children { result.push(child.clone()); let child_id = match child { MergedComment::Approved(c) => Some(c.id), MergedComment::Pending(c) => Some(c.id), }; dfs(child_id, children_map, result); } } } let mut result = Vec::new(); dfs(None, &children_map, &mut result); result } /// 评论列表组件。 /// /// Props: /// - `comments`:已审核评论列表 /// - `pending`:待审核评论列表 /// - `post_id`:所属文章 ID /// /// 根据两类评论构建合并树,依次渲染为 `CommentItem` 或 `PendingCommentItem`。 #[component] pub fn CommentList( comments: Vec, pending: Vec, post_id: i32, ) -> Element { let merged = merge_and_treeify(comments, pending); rsx! { div { class: "space-y-0 divide-y divide-gray-100 dark:divide-gray-700", for item in merged { match item { MergedComment::Approved(comment) => rsx! { CommentItem { key: "{comment.id}", comment, post_id } }, MergedComment::Pending(comment) => rsx! { PendingCommentItem { key: "{comment.id}", comment, post_id } }, } } } } }