报名表单
This commit is contained in:
@@ -6,7 +6,7 @@ import { motion, useScroll, useTransform } from 'framer-motion';
|
||||
import { ArrowLeftOutlined, ShareAltOutlined, CalendarOutlined, ClockCircleOutlined, EnvironmentOutlined, UserOutlined, UploadOutlined } from '@ant-design/icons';
|
||||
import confetti from 'canvas-confetti';
|
||||
import { message, Spin, Button, Result, Modal, Form, Input, Select, Radio, Checkbox, Upload } from 'antd';
|
||||
import { getActivityDetail, signUpActivity } from '../../api';
|
||||
import { getActivityDetail, signUpActivity, queryOrderStatus } from '../../api';
|
||||
import styles from '../../components/activity/activity.module.less';
|
||||
import { pageTransition, buttonTap } from '../../animation';
|
||||
import LoginModal from '../../components/LoginModal';
|
||||
@@ -16,6 +16,7 @@ import remarkGfm from 'remark-gfm';
|
||||
import rehypeRaw from 'rehype-raw';
|
||||
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
|
||||
import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism';
|
||||
import { QRCodeSVG } from 'qrcode.react';
|
||||
|
||||
const ActivityDetail = () => {
|
||||
const { id } = useParams();
|
||||
@@ -25,6 +26,8 @@ const ActivityDetail = () => {
|
||||
const { login } = useAuth();
|
||||
const [loginVisible, setLoginVisible] = useState(false);
|
||||
const [signupFormVisible, setSignupFormVisible] = useState(false);
|
||||
const [paymentModalVisible, setPaymentModalVisible] = useState(false);
|
||||
const [paymentInfo, setPaymentInfo] = useState(null);
|
||||
const [form] = Form.useForm();
|
||||
|
||||
// Header animation: transparent to white with shadow
|
||||
@@ -67,7 +70,15 @@ const ActivityDetail = () => {
|
||||
|
||||
const signUpMutation = useMutation({
|
||||
mutationFn: (values) => signUpActivity(id, { signup_info: values || {} }),
|
||||
onSuccess: () => {
|
||||
onSuccess: (data) => {
|
||||
if (data.payment_required) {
|
||||
setPaymentInfo(data);
|
||||
setPaymentModalVisible(true);
|
||||
setSignupFormVisible(false);
|
||||
message.info(data.message || '请扫码支付');
|
||||
return;
|
||||
}
|
||||
|
||||
message.success('报名成功!');
|
||||
setSignupFormVisible(false);
|
||||
confetti({
|
||||
@@ -84,6 +95,38 @@ const ActivityDetail = () => {
|
||||
}
|
||||
});
|
||||
|
||||
// Polling for payment status
|
||||
useEffect(() => {
|
||||
let timer;
|
||||
if (paymentModalVisible && paymentInfo?.order_id) {
|
||||
timer = setInterval(async () => {
|
||||
try {
|
||||
const response = await queryOrderStatus(paymentInfo.order_id);
|
||||
if (response.data.status === 'paid') {
|
||||
message.success('支付成功,报名已确认!');
|
||||
setPaymentModalVisible(false);
|
||||
setPaymentInfo(null);
|
||||
|
||||
// Trigger success effects
|
||||
confetti({
|
||||
particleCount: 150,
|
||||
spread: 70,
|
||||
origin: { y: 0.6 },
|
||||
colors: ['#00b96b', '#1890ff', '#ffffff']
|
||||
});
|
||||
queryClient.invalidateQueries(['activity', id]);
|
||||
queryClient.invalidateQueries(['activities']);
|
||||
|
||||
clearInterval(timer);
|
||||
}
|
||||
} catch (error) {
|
||||
// ignore error during polling
|
||||
}
|
||||
}, 3000);
|
||||
}
|
||||
return () => clearInterval(timer);
|
||||
}, [paymentModalVisible, paymentInfo, id, queryClient]);
|
||||
|
||||
const handleShare = async () => {
|
||||
const url = window.location.href;
|
||||
if (navigator.share) {
|
||||
@@ -399,6 +442,32 @@ const ActivityDetail = () => {
|
||||
})}
|
||||
</Form>
|
||||
</Modal>
|
||||
<Modal
|
||||
title="微信支付"
|
||||
open={paymentModalVisible}
|
||||
onCancel={() => setPaymentModalVisible(false)}
|
||||
footer={null}
|
||||
destroyOnHidden
|
||||
width={360}
|
||||
>
|
||||
<div style={{ textAlign: 'center', padding: '20px 0' }}>
|
||||
{paymentInfo?.code_url ? (
|
||||
<>
|
||||
<QRCodeSVG value={paymentInfo.code_url} size={200} />
|
||||
<p style={{ marginTop: 20, fontSize: 16, fontWeight: 'bold' }}>¥{paymentInfo.price}</p>
|
||||
<p style={{ color: '#666' }}>请使用微信扫一扫支付</p>
|
||||
</>
|
||||
) : (
|
||||
<Spin tip="正在生成二维码..." />
|
||||
)}
|
||||
<div style={{ marginTop: 20 }}>
|
||||
<Button type="primary" onClick={() => window.location.reload()}>
|
||||
我已支付
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
</motion.div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user