update pay function
This commit is contained in:
532
src/views/ValueForum/PredictionTopicDetail.js
Normal file
532
src/views/ValueForum/PredictionTopicDetail.js
Normal file
@@ -0,0 +1,532 @@
|
||||
/**
|
||||
* 预测话题详情页
|
||||
* 展示预测市场的完整信息、交易、评论等
|
||||
*/
|
||||
|
||||
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 { getTopic } from '@services/predictionMarketService';
|
||||
import { getUserAccount } from '@services/creditSystemService';
|
||||
import { useAuth } from '@contexts/AuthContext';
|
||||
import TradeModal from './components/TradeModal';
|
||||
|
||||
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 {
|
||||
isOpen: isTradeModalOpen,
|
||||
onOpen: onTradeModalOpen,
|
||||
onClose: onTradeModalClose,
|
||||
} = useDisclosure();
|
||||
|
||||
// 加载话题数据
|
||||
useEffect(() => {
|
||||
const loadTopic = () => {
|
||||
const topicData = getTopic(topicId);
|
||||
if (topicData) {
|
||||
setTopic(topicData);
|
||||
} else {
|
||||
toast({
|
||||
title: '话题不存在',
|
||||
status: 'error',
|
||||
duration: 3000,
|
||||
});
|
||||
navigate('/value-forum');
|
||||
}
|
||||
};
|
||||
|
||||
loadTopic();
|
||||
|
||||
if (user) {
|
||||
setUserAccount(getUserAccount(user.id));
|
||||
}
|
||||
}, [topicId, user]);
|
||||
|
||||
// 打开交易弹窗
|
||||
const handleOpenTrade = (mode) => {
|
||||
if (!user) {
|
||||
toast({
|
||||
title: '请先登录',
|
||||
status: 'warning',
|
||||
duration: 3000,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setTradeMode(mode);
|
||||
onTradeModalOpen();
|
||||
};
|
||||
|
||||
// 交易成功回调
|
||||
const handleTradeSuccess = () => {
|
||||
// 刷新话题数据
|
||||
setTopic(getTopic(topicId));
|
||||
setUserAccount(getUserAccount(user.id));
|
||||
};
|
||||
|
||||
if (!topic) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 获取选项数据
|
||||
const yesData = topic.positions?.yes || { total_shares: 0, current_price: 500, lord_id: null };
|
||||
const noData = topic.positions?.no || { total_shares: 0, current_price: 500, 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 (
|
||||
<Box minH="100vh" bg={forumColors.background.main} pt="80px" pb="20">
|
||||
<Container maxW="container.xl">
|
||||
{/* 头部:返回按钮 */}
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => navigate('/value-forum')}
|
||||
mb="6"
|
||||
color={forumColors.text.secondary}
|
||||
_hover={{ bg: forumColors.background.hover }}
|
||||
>
|
||||
← 返回论坛
|
||||
</Button>
|
||||
|
||||
<SimpleGrid columns={{ base: 1, lg: 3 }} spacing="6">
|
||||
{/* 左侧:主要内容 */}
|
||||
<Box gridColumn={{ base: '1', lg: '1 / 3' }}>
|
||||
<VStack spacing="6" align="stretch">
|
||||
{/* 话题信息卡片 */}
|
||||
<MotionBox
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5 }}
|
||||
bg={forumColors.background.card}
|
||||
borderRadius="xl"
|
||||
border="1px solid"
|
||||
borderColor={forumColors.border.default}
|
||||
overflow="hidden"
|
||||
>
|
||||
{/* 头部 */}
|
||||
<Box
|
||||
bg={forumColors.gradients.goldSubtle}
|
||||
px="6"
|
||||
py="4"
|
||||
borderBottom="1px solid"
|
||||
borderColor={forumColors.border.default}
|
||||
>
|
||||
<HStack justify="space-between">
|
||||
<Badge
|
||||
bg={forumColors.primary[500]}
|
||||
color="white"
|
||||
px="3"
|
||||
py="1"
|
||||
borderRadius="full"
|
||||
fontSize="sm"
|
||||
>
|
||||
{topic.category}
|
||||
</Badge>
|
||||
|
||||
<HStack spacing="3">
|
||||
<Icon as={Clock} boxSize="16px" color={forumColors.text.secondary} />
|
||||
<Text fontSize="sm" color={forumColors.text.secondary}>
|
||||
{formatTime(topic.deadline)} 截止
|
||||
</Text>
|
||||
</HStack>
|
||||
</HStack>
|
||||
|
||||
<Heading
|
||||
as="h1"
|
||||
fontSize="2xl"
|
||||
fontWeight="bold"
|
||||
color={forumColors.text.primary}
|
||||
mt="4"
|
||||
>
|
||||
{topic.title}
|
||||
</Heading>
|
||||
|
||||
<Text fontSize="md" color={forumColors.text.secondary} mt="3">
|
||||
{topic.description}
|
||||
</Text>
|
||||
|
||||
{/* 作者信息 */}
|
||||
<HStack mt="4" spacing="3">
|
||||
<Avatar
|
||||
size="sm"
|
||||
name={topic.author_name}
|
||||
src={topic.author_avatar}
|
||||
bg={forumColors.gradients.goldPrimary}
|
||||
/>
|
||||
<VStack align="start" spacing="0">
|
||||
<Text fontSize="sm" fontWeight="600" color={forumColors.text.primary}>
|
||||
{topic.author_name}
|
||||
</Text>
|
||||
<Text fontSize="xs" color={forumColors.text.tertiary}>
|
||||
发起者
|
||||
</Text>
|
||||
</VStack>
|
||||
</HStack>
|
||||
</Box>
|
||||
|
||||
{/* 市场数据 */}
|
||||
<Box p="6">
|
||||
<SimpleGrid columns={{ base: 1, md: 2 }} spacing="6">
|
||||
{/* Yes 方 */}
|
||||
<Box
|
||||
bg="linear-gradient(135deg, rgba(72, 187, 120, 0.1) 0%, rgba(72, 187, 120, 0.05) 100%)"
|
||||
border="2px solid"
|
||||
borderColor="green.400"
|
||||
borderRadius="xl"
|
||||
p="6"
|
||||
position="relative"
|
||||
>
|
||||
{yesData.lord_id && (
|
||||
<HStack
|
||||
position="absolute"
|
||||
top="3"
|
||||
right="3"
|
||||
spacing="1"
|
||||
bg="yellow.400"
|
||||
px="2"
|
||||
py="1"
|
||||
borderRadius="full"
|
||||
>
|
||||
<Icon as={Crown} boxSize="12px" color="white" />
|
||||
<Text fontSize="xs" fontWeight="bold" color="white">
|
||||
领主
|
||||
</Text>
|
||||
</HStack>
|
||||
)}
|
||||
|
||||
<VStack align="start" spacing="4">
|
||||
<HStack spacing="2">
|
||||
<Icon as={TrendingUp} boxSize="20px" color="green.500" />
|
||||
<Text fontSize="lg" fontWeight="700" color="green.600">
|
||||
看涨 / Yes
|
||||
</Text>
|
||||
</HStack>
|
||||
|
||||
<VStack align="start" spacing="1">
|
||||
<Text fontSize="xs" color={forumColors.text.secondary}>
|
||||
当前价格
|
||||
</Text>
|
||||
<HStack spacing="1">
|
||||
<Text fontSize="3xl" fontWeight="bold" color="green.600">
|
||||
{Math.round(yesData.current_price)}
|
||||
</Text>
|
||||
<Text fontSize="sm" color={forumColors.text.secondary}>
|
||||
积分/份
|
||||
</Text>
|
||||
</HStack>
|
||||
</VStack>
|
||||
|
||||
<Divider />
|
||||
|
||||
<HStack justify="space-between" w="full">
|
||||
<Text fontSize="sm" color={forumColors.text.secondary}>
|
||||
总份额
|
||||
</Text>
|
||||
<Text fontSize="md" fontWeight="600" color="green.600">
|
||||
{yesData.total_shares}份
|
||||
</Text>
|
||||
</HStack>
|
||||
|
||||
<HStack justify="space-between" w="full">
|
||||
<Text fontSize="sm" color={forumColors.text.secondary}>
|
||||
市场占比
|
||||
</Text>
|
||||
<Text fontSize="md" fontWeight="600" color="green.600">
|
||||
{yesPercent.toFixed(1)}%
|
||||
</Text>
|
||||
</HStack>
|
||||
</VStack>
|
||||
</Box>
|
||||
|
||||
{/* No 方 */}
|
||||
<Box
|
||||
bg="linear-gradient(135deg, rgba(245, 101, 101, 0.1) 0%, rgba(245, 101, 101, 0.05) 100%)"
|
||||
border="2px solid"
|
||||
borderColor="red.400"
|
||||
borderRadius="xl"
|
||||
p="6"
|
||||
position="relative"
|
||||
>
|
||||
{noData.lord_id && (
|
||||
<HStack
|
||||
position="absolute"
|
||||
top="3"
|
||||
right="3"
|
||||
spacing="1"
|
||||
bg="yellow.400"
|
||||
px="2"
|
||||
py="1"
|
||||
borderRadius="full"
|
||||
>
|
||||
<Icon as={Crown} boxSize="12px" color="white" />
|
||||
<Text fontSize="xs" fontWeight="bold" color="white">
|
||||
领主
|
||||
</Text>
|
||||
</HStack>
|
||||
)}
|
||||
|
||||
<VStack align="start" spacing="4">
|
||||
<HStack spacing="2">
|
||||
<Icon as={TrendingDown} boxSize="20px" color="red.500" />
|
||||
<Text fontSize="lg" fontWeight="700" color="red.600">
|
||||
看跌 / No
|
||||
</Text>
|
||||
</HStack>
|
||||
|
||||
<VStack align="start" spacing="1">
|
||||
<Text fontSize="xs" color={forumColors.text.secondary}>
|
||||
当前价格
|
||||
</Text>
|
||||
<HStack spacing="1">
|
||||
<Text fontSize="3xl" fontWeight="bold" color="red.600">
|
||||
{Math.round(noData.current_price)}
|
||||
</Text>
|
||||
<Text fontSize="sm" color={forumColors.text.secondary}>
|
||||
积分/份
|
||||
</Text>
|
||||
</HStack>
|
||||
</VStack>
|
||||
|
||||
<Divider />
|
||||
|
||||
<HStack justify="space-between" w="full">
|
||||
<Text fontSize="sm" color={forumColors.text.secondary}>
|
||||
总份额
|
||||
</Text>
|
||||
<Text fontSize="md" fontWeight="600" color="red.600">
|
||||
{noData.total_shares}份
|
||||
</Text>
|
||||
</HStack>
|
||||
|
||||
<HStack justify="space-between" w="full">
|
||||
<Text fontSize="sm" color={forumColors.text.secondary}>
|
||||
市场占比
|
||||
</Text>
|
||||
<Text fontSize="md" fontWeight="600" color="red.600">
|
||||
{noPercent.toFixed(1)}%
|
||||
</Text>
|
||||
</HStack>
|
||||
</VStack>
|
||||
</Box>
|
||||
</SimpleGrid>
|
||||
|
||||
{/* 市场情绪进度条 */}
|
||||
<Box mt="6">
|
||||
<Flex justify="space-between" mb="2">
|
||||
<Text fontSize="sm" fontWeight="600" color={forumColors.text.primary}>
|
||||
市场情绪分布
|
||||
</Text>
|
||||
<Text fontSize="sm" color={forumColors.text.secondary}>
|
||||
{yesPercent.toFixed(1)}% vs {noPercent.toFixed(1)}%
|
||||
</Text>
|
||||
</Flex>
|
||||
<Progress
|
||||
value={yesPercent}
|
||||
size="lg"
|
||||
borderRadius="full"
|
||||
bg="red.200"
|
||||
sx={{
|
||||
'& > div': {
|
||||
bg: 'linear-gradient(90deg, #48BB78 0%, #38A169 100%)',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
</MotionBox>
|
||||
</VStack>
|
||||
</Box>
|
||||
|
||||
{/* 右侧:操作面板 */}
|
||||
<Box gridColumn={{ base: '1', lg: '3' }}>
|
||||
<VStack spacing="6" align="stretch" position="sticky" top="90px">
|
||||
{/* 奖池信息 */}
|
||||
<Box
|
||||
bg={forumColors.background.card}
|
||||
borderRadius="xl"
|
||||
border="1px solid"
|
||||
borderColor={forumColors.border.gold}
|
||||
p="6"
|
||||
>
|
||||
<VStack spacing="4" align="stretch">
|
||||
<HStack justify="center" spacing="2">
|
||||
<Icon as={DollarSign} boxSize="24px" color={forumColors.primary[500]} />
|
||||
<Text fontSize="sm" fontWeight="600" color={forumColors.text.secondary}>
|
||||
当前奖池
|
||||
</Text>
|
||||
</HStack>
|
||||
|
||||
<Text
|
||||
fontSize="4xl"
|
||||
fontWeight="bold"
|
||||
color={forumColors.primary[500]}
|
||||
textAlign="center"
|
||||
>
|
||||
{topic.total_pool}
|
||||
</Text>
|
||||
|
||||
<Text fontSize="sm" color={forumColors.text.secondary} textAlign="center">
|
||||
积分
|
||||
</Text>
|
||||
|
||||
<Divider />
|
||||
|
||||
<HStack justify="space-between" fontSize="sm">
|
||||
<Text color={forumColors.text.secondary}>参与人数</Text>
|
||||
<HStack spacing="1">
|
||||
<Icon as={Users} boxSize="14px" />
|
||||
<Text fontWeight="600">{topic.stats.unique_traders.size}</Text>
|
||||
</HStack>
|
||||
</HStack>
|
||||
|
||||
<HStack justify="space-between" fontSize="sm">
|
||||
<Text color={forumColors.text.secondary}>总交易量</Text>
|
||||
<Text fontWeight="600">{Math.round(topic.stats.total_volume)}</Text>
|
||||
</HStack>
|
||||
</VStack>
|
||||
</Box>
|
||||
|
||||
{/* 交易按钮 */}
|
||||
{topic.status === 'active' && (
|
||||
<VStack spacing="3">
|
||||
<Button
|
||||
leftIcon={<ShoppingCart size={18} />}
|
||||
bg={forumColors.gradients.goldPrimary}
|
||||
color={forumColors.background.main}
|
||||
size="lg"
|
||||
w="full"
|
||||
fontWeight="bold"
|
||||
onClick={() => handleOpenTrade('buy')}
|
||||
_hover={{
|
||||
transform: 'translateY(-2px)',
|
||||
boxShadow: forumColors.shadows.goldHover,
|
||||
}}
|
||||
_active={{ transform: 'translateY(0)' }}
|
||||
>
|
||||
购买席位
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
leftIcon={<ArrowLeftRight size={18} />}
|
||||
variant="outline"
|
||||
borderColor={forumColors.border.default}
|
||||
color={forumColors.text.primary}
|
||||
size="lg"
|
||||
w="full"
|
||||
fontWeight="bold"
|
||||
onClick={() => handleOpenTrade('sell')}
|
||||
_hover={{
|
||||
bg: forumColors.background.hover,
|
||||
borderColor: forumColors.border.gold,
|
||||
}}
|
||||
>
|
||||
卖出席位
|
||||
</Button>
|
||||
</VStack>
|
||||
)}
|
||||
|
||||
{/* 用户余额 */}
|
||||
{user && userAccount && (
|
||||
<Box
|
||||
bg={forumColors.background.hover}
|
||||
borderRadius="lg"
|
||||
p="4"
|
||||
border="1px solid"
|
||||
borderColor={forumColors.border.default}
|
||||
>
|
||||
<VStack spacing="2" align="stretch" fontSize="sm">
|
||||
<HStack justify="space-between">
|
||||
<Text color={forumColors.text.secondary}>可用余额</Text>
|
||||
<Text fontWeight="600" color={forumColors.text.primary}>
|
||||
{userAccount.balance} 积分
|
||||
</Text>
|
||||
</HStack>
|
||||
<HStack justify="space-between">
|
||||
<Text color={forumColors.text.secondary}>冻结积分</Text>
|
||||
<Text fontWeight="600" color={forumColors.text.primary}>
|
||||
{userAccount.frozen} 积分
|
||||
</Text>
|
||||
</HStack>
|
||||
</VStack>
|
||||
</Box>
|
||||
)}
|
||||
</VStack>
|
||||
</Box>
|
||||
</SimpleGrid>
|
||||
</Container>
|
||||
|
||||
{/* 交易模态框 */}
|
||||
<TradeModal
|
||||
isOpen={isTradeModalOpen}
|
||||
onClose={onTradeModalClose}
|
||||
topic={topic}
|
||||
mode={tradeMode}
|
||||
onTradeSuccess={handleTradeSuccess}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default PredictionTopicDetail;
|
||||
Reference in New Issue
Block a user