import React, { useState, useEffect, useRef } from 'react'; import { makeStyles, Button, Input, Text, tokens, Card, Tooltip, Divider } from '@fluentui/react-components'; import { Dismiss24Regular, ArrowReply24Regular } from '@fluentui/react-icons'; import { getComments, postComment } from '../api'; import type { Comment as CommentType } from '../api'; import { toast } from 'react-hot-toast'; const useStyles = makeStyles({ container: { padding: tokens.spacingVerticalM, width: '100%', }, commentInput: { marginBottom: tokens.spacingVerticalS, width: '100%', height: '40px', fontSize: '16px', }, commentButton: { marginBottom: tokens.spacingVerticalM, }, commentList: { marginTop: tokens.spacingVerticalM, }, commentCard: { marginBottom: tokens.spacingVerticalS, padding: tokens.spacingHorizontalM, width: '100%', }, childComment: { marginLeft: tokens.spacingHorizontalL, borderLeft: `2px solid ${tokens.colorNeutralStroke1}`, paddingLeft: tokens.spacingHorizontalM, }, commentHeader: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: tokens.spacingVerticalXS, }, nickname: { fontWeight: 'bold', }, commentFooter: { display: 'flex', justifyContent: 'flex-end', alignItems: 'center', marginTop: tokens.spacingVerticalXS, }, replyButton: { cursor: 'pointer', color: tokens.colorBrandForeground1, display: 'flex', alignItems: 'center', gap: tokens.spacingHorizontalXS, }, replyInfo: { display: 'flex', alignItems: 'center', marginBottom: tokens.spacingVerticalXS, }, cancelReply: { marginLeft: tokens.spacingHorizontalS, cursor: 'pointer', }, inputContainer: { display: 'flex', flexDirection: 'column', gap: tokens.spacingVerticalS, }, nicknameInput: { width: '100%', height: '40px', fontSize: '16px', }, }); interface CommentSectionProps { postId: number; } const CommentSection: React.FC = ({ postId }) => { const styles = useStyles(); const [comments, setComments] = useState([]); const [content, setContent] = useState(''); const [nickname, setNickname] = useState(''); const [replyTo, setReplyTo] = useState(null); const [loading, setLoading] = useState(false); const commentCardRefs = useRef>(new Map()); useEffect(() => { fetchComments(); }, [postId]); const fetchComments = async () => { try { setLoading(true); const data = await getComments(postId); setComments(data as CommentType[]); } catch (error) { toast.error('获取评论失败'); console.error('Error fetching comments:', error); } finally { setLoading(false); } }; const handleSubmitComment = async () => { if (!content.trim() || !nickname.trim()) { toast.error('评论内容和昵称不能为空'); return; } try { setLoading(true); await postComment({ submission_id: postId, nickname, content, parent_comment_id: replyTo ? replyTo.id : 0, }); toast.success('评论成功'); setContent(''); if (replyTo) setReplyTo(null); fetchComments(); } catch (error: any) { toast.error('评论失败'); console.error('Error posting comment:', error); } finally { setLoading(false); } }; const handleReply = (comment: CommentType) => { setReplyTo(comment); }; const cancelReply = () => { setReplyTo(null); }; const renderComments = (parentId: number = 0, level: number = 0) => { return comments .filter(comment => comment.parent_comment_id === parentId) .map(comment => (
0 ? styles.childComment : ''} ref={el => { if (el) commentCardRefs.current.set(comment.id, el); }} >
{comment.nickname}
{comment.content}
handleReply(comment)} > 回复
{renderComments(comment.id, level + 1)}
)); }; return (
setNickname(e.target.value)} /> {replyTo && (
回复:{replyTo.nickname}
)} setContent(e.target.value)} />
{loading && 加载评论中...} {!loading && comments.length === 0 && 暂无评论} {renderComments()}
); }; export default CommentSection;