Files
vf_react/src/views/ValueForum/components/CreatePredictionModal.js
2025-11-23 18:11:48 +08:00

387 lines
13 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 创建预测话题模态框
* 用户可以发起新的预测市场话题
*/
import React, { useState } from 'react';
import {
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalBody,
ModalFooter,
ModalCloseButton,
Button,
VStack,
FormControl,
FormLabel,
Input,
Textarea,
Select,
HStack,
Text,
Box,
Icon,
Alert,
AlertIcon,
useToast,
} from '@chakra-ui/react';
import { Zap, Calendar, DollarSign } from 'lucide-react';
import { forumColors } from '@theme/forumTheme';
import { createTopic } from '@services/predictionMarketService';
import { getUserAccount, CREDIT_CONFIG } from '@services/creditSystemService';
import { useAuth } from '@contexts/AuthContext';
const CreatePredictionModal = ({ isOpen, onClose, onTopicCreated }) => {
const toast = useToast();
const { user } = useAuth();
// 表单状态
const [formData, setFormData] = useState({
title: '',
description: '',
category: 'stock',
deadline_days: 7,
});
const [isSubmitting, setIsSubmitting] = useState(false);
// 获取用户余额
const userAccount = user ? getUserAccount(user.id) : null;
// 处理表单变化
const handleChange = (field, value) => {
setFormData((prev) => ({ ...prev, [field]: value }));
};
// 提交表单
const handleSubmit = async () => {
try {
setIsSubmitting(true);
// 验证
if (!formData.title.trim()) {
toast({
title: '请填写话题标题',
status: 'warning',
duration: 3000,
});
return;
}
if (!formData.description.trim()) {
toast({
title: '请填写话题描述',
status: 'warning',
duration: 3000,
});
return;
}
// 检查余额
if (userAccount.balance < CREDIT_CONFIG.CREATE_TOPIC_COST) {
toast({
title: '积分不足',
description: `创建话题需要${CREDIT_CONFIG.CREATE_TOPIC_COST}积分`,
status: 'error',
duration: 3000,
});
return;
}
// 计算截止时间
const deadline = new Date();
deadline.setDate(deadline.getDate() + parseInt(formData.deadline_days));
const settlement_date = new Date(deadline);
settlement_date.setDate(settlement_date.getDate() + 1);
// 创建话题
const newTopic = createTopic({
author_id: user.id,
author_name: user.name || user.username,
author_avatar: user.avatar,
title: formData.title,
description: formData.description,
category: formData.category,
deadline: deadline.toISOString(),
settlement_date: settlement_date.toISOString(),
});
toast({
title: '创建成功!',
description: `话题已发布,扣除${CREDIT_CONFIG.CREATE_TOPIC_COST}积分`,
status: 'success',
duration: 3000,
});
// 重置表单
setFormData({
title: '',
description: '',
category: 'stock',
deadline_days: 7,
});
// 通知父组件
if (onTopicCreated) {
onTopicCreated(newTopic);
}
onClose();
} catch (error) {
console.error('创建话题失败:', error);
toast({
title: '创建失败',
description: error.message,
status: 'error',
duration: 3000,
});
} finally {
setIsSubmitting(false);
}
};
return (
<Modal isOpen={isOpen} onClose={onClose} size="xl" isCentered>
<ModalOverlay backdropFilter="blur(4px)" />
<ModalContent
bg={forumColors.background.card}
borderRadius="xl"
border="1px solid"
borderColor={forumColors.border.default}
>
<ModalHeader
bg={forumColors.gradients.goldSubtle}
borderTopRadius="xl"
borderBottom="1px solid"
borderColor={forumColors.border.default}
>
<HStack spacing="2">
<Icon as={Zap} boxSize="20px" color={forumColors.primary[500]} />
<Text color={forumColors.text.primary}>发起预测话题</Text>
</HStack>
</ModalHeader>
<ModalCloseButton color={forumColors.text.primary} />
<ModalBody py="6">
<VStack spacing="5" align="stretch">
{/* 提示信息 */}
<Alert
status="info"
bg={forumColors.background.hover}
borderRadius="lg"
border="1px solid"
borderColor={forumColors.border.default}
>
<AlertIcon color={forumColors.primary[500]} />
<VStack align="start" spacing="1" flex="1">
<Text fontSize="sm" color={forumColors.text.primary} fontWeight="600">
创建预测话题
</Text>
<Text fontSize="xs" color={forumColors.text.secondary}>
创建费用{CREDIT_CONFIG.CREATE_TOPIC_COST}积分进入奖池
</Text>
<Text fontSize="xs" color={forumColors.text.secondary}>
作者不能参与自己发起的话题
</Text>
<Text fontSize="xs" color={forumColors.text.secondary}>
截止后由作者提交结果进行结算
</Text>
</VStack>
</Alert>
{/* 话题标题 */}
<FormControl isRequired>
<FormLabel fontSize="sm" color={forumColors.text.primary}>
话题标题
</FormLabel>
<Input
placeholder="例如:贵州茅台下周会涨吗?"
value={formData.title}
onChange={(e) => handleChange('title', e.target.value)}
bg={forumColors.background.main}
border="1px solid"
borderColor={forumColors.border.default}
color={forumColors.text.primary}
_placeholder={{ color: forumColors.text.tertiary }}
_hover={{ borderColor: forumColors.border.light }}
_focus={{
borderColor: forumColors.border.gold,
boxShadow: `0 0 0 1px ${forumColors.border.goldGlow}`,
}}
/>
</FormControl>
{/* 话题描述 */}
<FormControl isRequired>
<FormLabel fontSize="sm" color={forumColors.text.primary}>
话题描述
</FormLabel>
<Textarea
placeholder="详细描述预测的内容、判断标准、数据来源等..."
value={formData.description}
onChange={(e) => handleChange('description', e.target.value)}
rows={4}
bg={forumColors.background.main}
border="1px solid"
borderColor={forumColors.border.default}
color={forumColors.text.primary}
_placeholder={{ color: forumColors.text.tertiary }}
_hover={{ borderColor: forumColors.border.light }}
_focus={{
borderColor: forumColors.border.gold,
boxShadow: `0 0 0 1px ${forumColors.border.goldGlow}`,
}}
/>
</FormControl>
{/* 分类 */}
<FormControl>
<FormLabel fontSize="sm" color={forumColors.text.primary}>
分类
</FormLabel>
<Select
value={formData.category}
onChange={(e) => handleChange('category', e.target.value)}
bg={forumColors.background.main}
border="1px solid"
borderColor={forumColors.border.default}
color={forumColors.text.primary}
_hover={{ borderColor: forumColors.border.light }}
_focus={{
borderColor: forumColors.border.gold,
boxShadow: `0 0 0 1px ${forumColors.border.goldGlow}`,
}}
>
<option value="stock">股票行情</option>
<option value="index">指数走势</option>
<option value="concept">概念板块</option>
<option value="policy">政策影响</option>
<option value="event">事件预测</option>
<option value="other">其他</option>
</Select>
</FormControl>
{/* 截止时间 */}
<FormControl>
<FormLabel fontSize="sm" color={forumColors.text.primary}>
<HStack spacing="2">
<Icon as={Calendar} boxSize="16px" />
<Text>交易截止时间</Text>
</HStack>
</FormLabel>
<Select
value={formData.deadline_days}
onChange={(e) => handleChange('deadline_days', e.target.value)}
bg={forumColors.background.main}
border="1px solid"
borderColor={forumColors.border.default}
color={forumColors.text.primary}
_hover={{ borderColor: forumColors.border.light }}
_focus={{
borderColor: forumColors.border.gold,
boxShadow: `0 0 0 1px ${forumColors.border.goldGlow}`,
}}
>
<option value="1">1天后</option>
<option value="3">3天后</option>
<option value="7">7天后推荐</option>
<option value="14">14天后</option>
<option value="30">30天后</option>
</Select>
<Text fontSize="xs" color={forumColors.text.tertiary} mt="2">
截止后次日可提交结果进行结算
</Text>
</FormControl>
{/* 费用说明 */}
<Box
bg={forumColors.gradients.goldSubtle}
border="1px solid"
borderColor={forumColors.border.gold}
borderRadius="lg"
p="4"
>
<HStack justify="space-between">
<VStack align="start" spacing="1">
<Text fontSize="sm" fontWeight="600" color={forumColors.text.primary}>
创建费用
</Text>
<Text fontSize="xs" color={forumColors.text.secondary}>
将进入奖池奖励给获胜者
</Text>
</VStack>
<HStack spacing="1">
<Icon as={DollarSign} boxSize="20px" color={forumColors.primary[500]} />
<Text fontSize="2xl" fontWeight="bold" color={forumColors.primary[500]}>
{CREDIT_CONFIG.CREATE_TOPIC_COST}
</Text>
<Text fontSize="sm" color={forumColors.text.secondary}>
积分
</Text>
</HStack>
</HStack>
<Box mt="3" pt="3" borderTop="1px solid" borderColor={forumColors.border.default}>
<HStack justify="space-between" fontSize="sm">
<Text color={forumColors.text.secondary}>你的余额</Text>
<Text fontWeight="600" color={forumColors.text.primary}>
{userAccount?.balance || 0} 积分
</Text>
</HStack>
<HStack justify="space-between" fontSize="sm" mt="1">
<Text color={forumColors.text.secondary}>创建后</Text>
<Text
fontWeight="600"
color={
(userAccount?.balance || 0) >= CREDIT_CONFIG.CREATE_TOPIC_COST
? forumColors.success[500]
: forumColors.error[500]
}
>
{(userAccount?.balance || 0) - CREDIT_CONFIG.CREATE_TOPIC_COST} 积分
</Text>
</HStack>
</Box>
</Box>
</VStack>
</ModalBody>
<ModalFooter borderTop="1px solid" borderColor={forumColors.border.default}>
<HStack spacing="3">
<Button
variant="ghost"
onClick={onClose}
color={forumColors.text.secondary}
_hover={{ bg: forumColors.background.hover }}
>
取消
</Button>
<Button
bg={forumColors.gradients.goldPrimary}
color={forumColors.background.main}
fontWeight="bold"
onClick={handleSubmit}
isLoading={isSubmitting}
loadingText="创建中..."
isDisabled={(userAccount?.balance || 0) < CREDIT_CONFIG.CREATE_TOPIC_COST}
_hover={{
opacity: 0.9,
transform: 'translateY(-2px)',
}}
_active={{ transform: 'translateY(0)' }}
>
发布话题
</Button>
</HStack>
</ModalFooter>
</ModalContent>
</Modal>
);
};
export default CreatePredictionModal;