报名表单

This commit is contained in:
jeremygan2021
2026-02-23 15:07:55 +08:00
parent 6a391c5eab
commit db7a3bd000
11 changed files with 289 additions and 22 deletions

View File

@@ -15,6 +15,8 @@ const MyOrders = () => {
const [activities, setActivities] = useState([]);
const [modalVisible, setModalVisible] = useState(false);
const [currentOrder, setCurrentOrder] = useState(null);
const [signupInfoModalVisible, setSignupInfoModalVisible] = useState(false);
const [currentSignupInfo, setCurrentSignupInfo] = useState(null);
const [loginVisible, setLoginVisible] = useState(false);
const navigate = useNavigate();
@@ -31,6 +33,11 @@ const MyOrders = () => {
setModalVisible(true);
};
const showSignupInfo = (info) => {
setCurrentSignupInfo(info);
setSignupInfoModalVisible(true);
};
const handleQueryData = async () => {
setLoading(true);
try {
@@ -251,7 +258,14 @@ const MyOrders = () => {
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginTop: 16 }}>
<Tag color="blue">{activity.status || '已报名'}</Tag>
<Button type="primary" size="small" ghost>查看详情</Button>
<Space>
{item.signup_info && Object.keys(item.signup_info).length > 0 && (
<Button size="small" onClick={(e) => { e.stopPropagation(); showSignupInfo(item.signup_info); }}>
查看报名信息
</Button>
)}
<Button type="primary" size="small" ghost>查看详情</Button>
</Space>
</div>
</div>
</Card>
@@ -334,6 +348,27 @@ const MyOrders = () => {
}
}}
/>
<Modal
title="报名信息详情"
open={signupInfoModalVisible}
onCancel={() => setSignupInfoModalVisible(false)}
footer={[
<Button key="close" onClick={() => setSignupInfoModalVisible(false)}>
关闭
</Button>
]}
>
{currentSignupInfo && (
<Descriptions column={1} bordered>
{Object.entries(currentSignupInfo).map(([key, value]) => (
<Descriptions.Item label={key} key={key}>
{typeof value === 'object' ? JSON.stringify(value) : String(value)}
</Descriptions.Item>
))}
</Descriptions>
)}
</Modal>
</div>
</div>
);

View File

@@ -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>
);
};