/**
* 预测话题评论区组件
* 支持发布评论、嵌套回复、点赞、庄主标识、观点IPO投资
*/
import React, { useState, useEffect } from 'react';
import {
Box,
VStack,
HStack,
Text,
Avatar,
Textarea,
Button,
Flex,
IconButton,
Divider,
useToast,
Badge,
Tooltip,
} from '@chakra-ui/react';
import { motion, AnimatePresence } from 'framer-motion';
import { Heart, MessageCircle, Send, TrendingUp, Crown, Pin } from 'lucide-react';
import { forumColors } from '@theme/forumTheme';
import {
createComment,
getComments,
likeComment,
} from '@services/predictionMarketService.api';
import { useAuth } from '@contexts/AuthContext';
const MotionBox = motion(Box);
const CommentItem = ({ comment, topicId, topic, onReply, onInvest }) => {
const [isLiked, setIsLiked] = useState(false);
const [likes, setLikes] = useState(comment.likes_count || 0);
const [showReply, setShowReply] = useState(false);
// 处理点赞
const handleLike = async () => {
try {
const response = await likeComment(comment.id);
if (response.success) {
setLikes(response.likes_count);
setIsLiked(response.action === 'like');
}
} catch (error) {
console.error('点赞失败:', error);
}
};
// 格式化时间
const formatTime = (dateString) => {
const date = new Date(dateString);
const now = new Date();
const diff = now - date;
const minutes = Math.floor(diff / 60000);
const hours = Math.floor(diff / 3600000);
const days = Math.floor(diff / 86400000);
if (minutes < 1) return '刚刚';
if (minutes < 60) return `${minutes}分钟前`;
if (hours < 24) return `${hours}小时前`;
if (days < 7) return `${days}天前`;
return date.toLocaleDateString('zh-CN', {
month: '2-digit',
day: '2-digit',
});
};
// 判断是否是庄主
const isYesLord = comment.user?.id === topic?.yes_lord_id;
const isNoLord = comment.user?.id === topic?.no_lord_id;
const isLord = isYesLord || isNoLord;
return (
{/* 头像 */}
{/* 评论内容 */}
{/* 用户名和时间 */}
{comment.user?.nickname || comment.user?.username || '匿名用户'}
{/* 庄主标识 */}
{isLord && (
{isYesLord ? 'YES庄' : 'NO庄'}
)}
{/* 置顶标识 */}
{comment.is_pinned && (
置顶
)}
{formatTime(comment.created_at)}
{/* 评论正文 */}
{comment.content}
{/* 观点IPO投资统计 */}
{comment.total_investment > 0 && (
{comment.investor_count || 0}人投资
总投资:
{comment.total_investment}
积分
{comment.is_verified && (
{comment.verification_result === 'correct' ? '✓ 预测正确' : '✗ 预测错误'}
)}
)}
{/* 操作按钮 */}
{likes > 0 ? likes : '点赞'}
setShowReply(!showReply)}
_hover={{ color: forumColors.primary[500] }}
>
回复
{/* 投资观点按钮 */}
{!comment.is_verified && onInvest && (
}
color={forumColors.primary[500]}
_hover={{ bg: forumColors.gradients.goldSubtle }}
onClick={() => onInvest(comment)}
>
投资观点
)}
{/* 回复输入框 */}
{showReply && (
{
setShowReply(false);
if (onReply) onReply();
}}
/>
)}
{/* 回复列表 */}
{comment.replies && comment.replies.length > 0 && (
{comment.replies.map((reply) => (
{reply.user?.nickname || reply.user?.username || '匿名用户'}
{formatTime(reply.created_at)}
{reply.content}
))}
)}
);
};
const ReplyInput = ({ topicId, parentId = null, placeholder, onSubmit }) => {
const toast = useToast();
const { user } = useAuth();
const [content, setContent] = useState('');
const [isSubmitting, setIsSubmitting] = useState(false);
const handleSubmit = async () => {
if (!content.trim()) {
toast({
title: '请输入评论内容',
status: 'warning',
duration: 2000,
});
return;
}
setIsSubmitting(true);
try {
const response = await createComment(topicId, {
content: content.trim(),
parent_id: parentId,
});
if (response.success) {
toast({
title: '评论成功',
status: 'success',
duration: 2000,
});
setContent('');
if (onSubmit) onSubmit();
}
} catch (error) {
console.error('评论失败:', error);
toast({
title: '评论失败',
description: error.response?.data?.error || error.message,
status: 'error',
duration: 3000,
});
} finally {
setIsSubmitting(false);
}
};
return (
);
};
const PredictionCommentSection = ({ topicId, topic, onInvest }) => {
const [comments, setComments] = useState([]);
const [loading, setLoading] = useState(true);
const [total, setTotal] = useState(0);
const [page, setPage] = useState(1);
const [hasMore, setHasMore] = useState(false);
// 加载评论
const loadComments = async (pageNum = 1) => {
try {
setLoading(true);
const response = await getComments(topicId, { page: pageNum, per_page: 20 });
if (response.success) {
if (pageNum === 1) {
setComments(response.data);
} else {
setComments((prev) => [...prev, ...response.data]);
}
setTotal(response.pagination?.total || response.data.length);
setHasMore(response.pagination?.has_next || false);
setPage(pageNum);
}
} catch (error) {
console.error('加载评论失败:', error);
} finally {
setLoading(false);
}
};
useEffect(() => {
loadComments();
}, [topicId]);
return (
{/* 标题 */}
评论
共 {total} 条
{/* 发表评论 */}
loadComments(1)} />
{/* 评论列表 */}
{loading && page === 1 ? (
加载中...
) : comments.length === 0 ? (
暂无评论,快来抢沙发吧!
) : (
<>
}>
{comments.map((comment) => (
loadComments(1)}
onInvest={onInvest}
/>
))}
{/* 加载更多 */}
{hasMore && (
)}
>
)}
);
};
export default PredictionCommentSection;