/** * 预测话题详情页 * 展示预测市场的完整信息、交易、评论等 */ import React, { useState, useEffect } from 'react'; import { Box, Container, Heading, Text, Button, HStack, VStack, Flex, Badge, Avatar, Icon, Progress, Divider, useDisclosure, useToast, SimpleGrid, } from '@chakra-ui/react'; import { TrendingUp, TrendingDown, Crown, Users, Clock, DollarSign, ShoppingCart, ArrowLeftRight, CheckCircle2, } from 'lucide-react'; import { useParams, useNavigate } from 'react-router-dom'; import { motion } from 'framer-motion'; import { forumColors } from '@theme/forumTheme'; import { getTopicDetail, getUserAccount } from '@services/predictionMarketService.api'; import { useAuth } from '@contexts/AuthContext'; import TradeModal from './components/TradeModal'; import PredictionCommentSection from './components/PredictionCommentSection'; import CommentInvestModal from './components/CommentInvestModal'; const MotionBox = motion(Box); const PredictionTopicDetail = () => { const { topicId } = useParams(); const navigate = useNavigate(); const toast = useToast(); const { user } = useAuth(); // 状态 const [topic, setTopic] = useState(null); const [userAccount, setUserAccount] = useState(null); const [tradeMode, setTradeMode] = useState('buy'); const [selectedComment, setSelectedComment] = useState(null); const [commentSectionKey, setCommentSectionKey] = useState(0); // 模态框 const { isOpen: isTradeModalOpen, onOpen: onTradeModalOpen, onClose: onTradeModalClose, } = useDisclosure(); const { isOpen: isInvestModalOpen, onOpen: onInvestModalOpen, onClose: onInvestModalClose, } = useDisclosure(); // 加载话题数据 useEffect(() => { const loadTopic = async () => { try { const response = await getTopicDetail(topicId); if (response.success) { setTopic(response.data); } else { toast({ title: '话题不存在', status: 'error', duration: 3000, }); navigate('/value-forum'); } } catch (error) { console.error('获取话题详情失败:', error); toast({ title: '加载失败', description: error.message, status: 'error', duration: 3000, }); navigate('/value-forum'); } }; const loadAccount = async () => { if (!user) return; try { const response = await getUserAccount(); if (response.success) { setUserAccount(response.data); } } catch (error) { console.error('获取账户失败:', error); } }; loadTopic(); loadAccount(); }, [topicId, user, toast, navigate]); // 打开交易弹窗 const handleOpenTrade = (mode) => { if (!user) { toast({ title: '请先登录', status: 'warning', duration: 3000, }); return; } setTradeMode(mode); onTradeModalOpen(); }; // 交易成功回调 const handleTradeSuccess = async () => { // 刷新话题数据 try { const topicResponse = await getTopicDetail(topicId); if (topicResponse.success) { setTopic(topicResponse.data); } const accountResponse = await getUserAccount(); if (accountResponse.success) { setUserAccount(accountResponse.data); } } catch (error) { console.error('刷新数据失败:', error); } }; // 打开投资弹窗 const handleOpenInvest = (comment) => { if (!user) { toast({ title: '请先登录', status: 'warning', duration: 3000, }); return; } setSelectedComment(comment); onInvestModalOpen(); }; // 投资成功回调 const handleInvestSuccess = async () => { // 刷新账户数据 try { const accountResponse = await getUserAccount(); if (accountResponse.success) { setUserAccount(accountResponse.data); } // 刷新评论区(通过更新key触发重新加载) setCommentSectionKey((prev) => prev + 1); toast({ title: '投资成功', description: '评论列表已刷新', status: 'success', duration: 2000, }); } catch (error) { console.error('刷新数据失败:', error); } }; if (!topic) { return null; } // 获取选项数据(从后端扁平结构映射到前端使用的嵌套结构) const yesData = { total_shares: topic.yes_total_shares || 0, current_price: topic.yes_price || 500, lord_id: topic.yes_lord_id || null, }; const noData = { total_shares: topic.no_total_shares || 0, current_price: topic.no_price || 500, lord_id: topic.no_lord_id || null, }; // 计算总份额 const totalShares = yesData.total_shares + noData.total_shares; // 计算百分比 const yesPercent = totalShares > 0 ? (yesData.total_shares / totalShares) * 100 : 50; const noPercent = totalShares > 0 ? (noData.total_shares / totalShares) * 100 : 50; // 格式化时间 const formatTime = (dateString) => { const date = new Date(dateString); const now = new Date(); const diff = date - now; const days = Math.floor(diff / 86400000); const hours = Math.floor(diff / 3600000); if (days > 0) return `${days}天后`; if (hours > 0) return `${hours}小时后`; return '即将截止'; }; return ( {/* 头部:返回按钮 */} {/* 左侧:主要内容 */} {/* 话题信息卡片 */} {/* 头部 */} {topic.category} {formatTime(topic.deadline)} 截止 {topic.title} {topic.description} {/* 作者信息 */} {topic.author_name} 发起者 {/* 市场数据 */} {/* Yes 方 */} {yesData.lord_id && ( 领主 )} 看涨 / Yes 当前价格 {Math.round(yesData.current_price)} 积分/份 总份额 {yesData.total_shares}份 市场占比 {yesPercent.toFixed(1)}% {/* No 方 */} {noData.lord_id && ( 领主 )} 看跌 / No 当前价格 {Math.round(noData.current_price)} 积分/份 总份额 {noData.total_shares}份 市场占比 {noPercent.toFixed(1)}% {/* 市场情绪进度条 */} 市场情绪分布 {yesPercent.toFixed(1)}% vs {noPercent.toFixed(1)}% div': { bg: 'linear-gradient(90deg, #48BB78 0%, #38A169 100%)', }, }} /> {/* 右侧:操作面板 */} {/* 奖池信息 */} 当前奖池 {topic.total_pool} 积分 参与人数 {topic.participants_count || 0} 总交易量 {Math.round((topic.yes_total_shares || 0) + (topic.no_total_shares || 0))} {/* 交易按钮 */} {topic.status === 'active' && ( )} {/* 用户余额 */} {user && userAccount && ( 可用余额 {userAccount.balance} 积分 冻结积分 {userAccount.frozen} 积分 )} {/* 评论区 - 占据全宽 */} {/* 交易模态框 */} {/* 观点投资模态框 */} ); }; export default PredictionTopicDetail;