blob: 13de6adfcaff1d099bb1ea59e9806f5a9aa5096a [file] [log] [blame]
meisiyu1d4aade2025-06-02 20:10:36 +08001import React, { useState, useEffect } from 'react';
2import {
3 Card,
4 Tabs,
5 Button,
6 Table,
7 Modal,
8 Form,
9 Input,
10 Select,
11 Upload,
12 message,
13 Tag,
14 Space,
15 Popconfirm,
16 Row,
17 Col,
18 Radio,
19 InputNumber,
20 Image
21} from 'antd';
22import {
23 PlusOutlined,
24 EditOutlined,
25 DeleteOutlined,
26 EyeOutlined,
27 UploadOutlined,
28 HeartOutlined,
29 LoadingOutlined
30} from '@ant-design/icons';
31import { useNavigate } from 'react-router-dom';
32import {
33 getMyPosts,
34 getMyFavorites,
35 publishPost,
36 updatePost,
37 deletePost,
38 getAvailableTags,
39 uploadImage,
40 deleteImage,
41 getPromotionPlans,
42 createPayment,
43 getPromotionStatus,
44 confirmPayment,
45 cancelPayment
46} from '@/services/post';
47import PostCard from '../PostCenter/PostCard';
48import styles from './index.module.css';
49
50const { TabPane } = Tabs;
51const { TextArea } = Input;
52const { Option } = Select;
53
54interface PostFormData {
55 title: string;
56 content: string;
57 summary: string;
58 tags: string[] | string;
59 promotionPlan?: number;
60 coverImage?: string;
61}
62
63interface PromotionPlan {
64 id: number;
65 name: string;
66 description: string;
67 price: number;
68 duration: number;
69}
70
71interface PaymentRecord {
72 paymentId: number;
73 postId: number;
74 planId: number;
75 userId: number;
76 amount: number;
77 paymentStatus: string;
78 paymentTime: string;
79}
80
81const UserCenter: React.FC = () => {
82 const navigate = useNavigate();
83 const [activeTab, setActiveTab] = useState('myPosts');
84 const [publishModalVisible, setPublishModalVisible] = useState(false);
85 const [editModalVisible, setEditModalVisible] = useState(false);
86 const [paymentModalVisible, setPaymentModalVisible] = useState(false);
87 const [myPosts, setMyPosts] = useState<API.Post.PostInfo[]>([]);
88 const [favorites, setFavorites] = useState<API.Post.PostInfo[]>([]);
89 const [loading, setLoading] = useState(false);
90 const [form] = Form.useForm();
91 const [editForm] = Form.useForm();
92 const [selectedPromotion, setSelectedPromotion] = useState<PromotionPlan | null>(null);
93 const [currentEditPost, setCurrentEditPost] = useState<API.Post.PostInfo | null>(null);
94 const [availableTags, setAvailableTags] = useState<API.Post.PostTag[]>([]);
95 const [promotionPlans, setPromotionPlans] = useState<PromotionPlan[]>([]);
96 const [uploadLoading, setUploadLoading] = useState(false);
97 const [editUploadLoading, setEditUploadLoading] = useState(false);
98 const [coverImageUrl, setCoverImageUrl] = useState<string>('');
99 const [editCoverImageUrl, setEditCoverImageUrl] = useState<string>('');
100 const [currentPayment, setCurrentPayment] = useState<PaymentRecord | null>(null);
101 const [isEditingPromotion, setIsEditingPromotion] = useState(false);
102
103 useEffect(() => {
104 if (activeTab === 'myPosts') {
105 fetchMyPosts();
106 } else if (activeTab === 'favorites') {
107 fetchFavorites();
108 }
109 }, [activeTab]);
110
111 useEffect(() => {
112 fetchAvailableTags();
113 fetchPromotionPlans();
114 }, []);
115
116 const fetchMyPosts = async () => {
117 setLoading(true);
118 try {
119 const response = await getMyPosts({ pageNum: 1, pageSize: 100 });
120 if (response.code === 200) {
121 setMyPosts(response.rows || []);
122 } else {
123 message.error(response.msg || '获取我的帖子失败');
124 }
125 } catch (error) {
126 message.error('获取我的帖子失败');
127 } finally {
128 setLoading(false);
129 }
130 };
131
132 const fetchFavorites = async () => {
133 setLoading(true);
134 try {
135 const response = await getMyFavorites({ pageNum: 1, pageSize: 100 });
136 if (response.code === 200) {
137 setFavorites(response.rows || []);
138 } else {
139 message.error(response.msg || '获取收藏列表失败');
140 }
141 } catch (error) {
142 message.error('获取收藏列表失败');
143 } finally {
144 setLoading(false);
145 }
146 };
147
148 const fetchAvailableTags = async () => {
149 try {
150 const response = await getAvailableTags();
151 if (response.code === 200) {
152 setAvailableTags(response.data || []);
153 } else {
154 message.error(response.msg || '获取可用标签失败');
155 }
156 } catch (error) {
157 message.error('获取可用标签失败');
158 }
159 };
160
161 const fetchPromotionPlans = async () => {
162 try {
163 const response = await getPromotionPlans();
164 if (response.code === 200) {
165 setPromotionPlans(response.data || []);
166 }
167 } catch (error) {
168 console.error('获取推广计划失败:', error);
169 }
170 };
171
172 const handlePublishPost = async (values: PostFormData) => {
173 try {
174 if (values.promotionPlan && selectedPromotion) {
175 // 如果选择了推广,创建支付记录
176 const paymentResponse = await createPayment({
177 postId: 0, // 新帖子,暂时设为0,后端会处理
178 planId: selectedPromotion.id,
179 amount: selectedPromotion.price
180 });
181
182 if (paymentResponse.code === 200) {
183 setCurrentPayment(paymentResponse.data);
184 setPaymentModalVisible(true);
185 return;
186 } else {
187 message.error(paymentResponse.msg || '创建支付记录失败');
188 return;
189 }
190 }
191
192 // 直接发布帖子
193 await submitPost(values);
194 } catch (error) {
195 message.error('发布帖子失败');
196 }
197 };
198
199 const submitPost = async (values: PostFormData) => {
200 try {
201 // 处理标签格式
202 const tagsString = Array.isArray(values.tags) ? values.tags.join(',') : values.tags;
203
204 const postData = {
205 title: values.title,
206 content: values.content,
207 summary: values.summary,
208 tags: tagsString,
209 promotionPlan: values.promotionPlan,
210 coverImage: coverImageUrl || undefined
211 };
212
213 const response = await publishPost(postData);
214 if (response.code === 200) {
215 message.success('帖子发布成功');
216 setPublishModalVisible(false);
217 form.resetFields();
218 setSelectedPromotion(null);
219 setCoverImageUrl('');
220 fetchMyPosts();
221 } else {
222 message.error(response.msg || '发布帖子失败');
223 }
224 } catch (error) {
225 message.error('发布帖子失败');
226 }
227 };
228
229 const handleEditPost = async (post: API.Post.PostInfo) => {
230 setCurrentEditPost(post);
231 const tagsArray = post.tags ? (typeof post.tags === 'string' ? post.tags.split(',') : post.tags) : [];
232
233 // 检查推广状态
234 try {
235 const promotionResponse = await getPromotionStatus(post.postId || post.id || 0);
236 if (promotionResponse.code === 200) {
237 const { hasPromotion, promotionPlanId } = promotionResponse.data;
238 setIsEditingPromotion(hasPromotion);
239
240 editForm.setFieldsValue({
241 title: post.title,
242 content: post.content,
243 summary: post.summary,
244 tags: tagsArray,
245 promotionPlan: hasPromotion ? promotionPlanId : undefined
246 });
247 }
248 } catch (error) {
249 console.error('获取推广状态失败:', error);
250 editForm.setFieldsValue({
251 title: post.title,
252 content: post.content,
253 summary: post.summary,
254 tags: tagsArray,
255 promotionPlan: post.promotionPlanId
256 });
257 }
258
259 setEditCoverImageUrl(post.coverImage || '');
260 setEditModalVisible(true);
261 };
262
263 const handleUpdatePost = async (values: any) => {
264 if (!currentEditPost) return;
265
266 try {
267 // 处理标签格式
268 const tagsString = Array.isArray(values.tags) ? values.tags.join(',') : values.tags;
269
270 // 检查是否选择了新的推广计划
271 const hasNewPromotion = values.promotionPlan && !isEditingPromotion;
272
273 if (hasNewPromotion) {
274 // 如果选择了新的推广计划,需要先创建支付记录
275 const selectedPlan = promotionPlans.find(p => p.id === values.promotionPlan);
276 if (selectedPlan) {
277 setSelectedPromotion(selectedPlan);
278
279 // 创建支付记录
280 const paymentResponse = await createPayment({
281 postId: currentEditPost.postId || currentEditPost.id || 0,
282 planId: selectedPlan.id,
283 amount: selectedPlan.price
284 });
285
286 if (paymentResponse.code === 200) {
287 setCurrentPayment(paymentResponse.data);
288 setPaymentModalVisible(true);
289 return; // 等待支付完成后再更新帖子
290 } else {
291 message.error(paymentResponse.msg || '创建支付记录失败');
292 return;
293 }
294 }
295 }
296
297 // 直接更新帖子(没有新推广或已有推广)
298 await updatePostDirectly(values, tagsString);
299 } catch (error) {
300 message.error('更新帖子失败');
301 }
302 };
303
304 const updatePostDirectly = async (values: any, tagsString: string) => {
305 if (!currentEditPost) return;
306
307 const updateData = {
308 ...currentEditPost,
309 title: values.title,
310 content: values.content,
311 summary: values.summary,
312 tags: tagsString,
313 coverImage: editCoverImageUrl || currentEditPost.coverImage,
314 promotionPlanId: values.promotionPlan
315 };
316
317 const response = await updatePost(updateData);
318 if (response.code === 200) {
319 message.success('帖子更新成功');
320 setEditModalVisible(false);
321 editForm.resetFields();
322 setCurrentEditPost(null);
323 setEditCoverImageUrl('');
324 setIsEditingPromotion(false);
325 fetchMyPosts();
326 } else {
327 message.error(response.msg || '更新帖子失败');
328 }
329 };
330
331 const handleDeletePost = async (postId: number) => {
332 try {
333 const response = await deletePost(postId);
334 if (response.code === 200) {
335 message.success('帖子删除成功');
336 fetchMyPosts();
337 } else {
338 message.error(response.msg || '删除帖子失败');
339 }
340 } catch (error) {
341 message.error('删除帖子失败');
342 }
343 };
344
345 const handleViewPost = (postId: number) => {
346 navigate(`/post-detail/${postId}`);
347 };
348
349 const handlePaymentConfirm = async () => {
350 if (!currentPayment) return;
351
352 try {
353 const response = await confirmPayment(currentPayment.paymentId);
354 if (response.code === 200) {
355 message.success('支付成功,推广已生效');
356 setPaymentModalVisible(false);
357 setCurrentPayment(null);
358
359 // 如果是编辑模式,完成帖子更新
360 if (editModalVisible && currentEditPost) {
361 const values = editForm.getFieldsValue();
362 const tagsString = Array.isArray(values.tags) ? values.tags.join(',') : values.tags;
363 await updatePostDirectly(values, tagsString);
364 } else {
365 // 如果是发布模式
366 setPublishModalVisible(false);
367 form.resetFields();
368 setSelectedPromotion(null);
369 setCoverImageUrl('');
370 fetchMyPosts();
371 }
372 } else {
373 message.error(response.msg || '支付确认失败');
374 }
375 } catch (error) {
376 message.error('支付确认失败');
377 }
378 };
379
380 const handlePaymentCancel = async () => {
381 if (!currentPayment) return;
382
383 try {
384 await cancelPayment(currentPayment.paymentId);
385 message.info('支付已取消');
386 setPaymentModalVisible(false);
387 setCurrentPayment(null);
388 setSelectedPromotion(null);
389 } catch (error) {
390 console.error('取消支付失败:', error);
391 setPaymentModalVisible(false);
392 setCurrentPayment(null);
393 setSelectedPromotion(null);
394 }
395 };
396
397 const handleImageUpload = async (file: any) => {
398 setUploadLoading(true);
399 try {
400 const formData = new FormData();
401 formData.append('file', file);
402
403 const response = await uploadImage(formData);
404 if (response.code === 200 && response.data) {
405 setCoverImageUrl(response.data.url);
406 message.success('图片上传成功');
407 return false; // 阻止自动上传
408 } else {
409 message.error(response.msg || '图片上传失败');
410 }
411 } catch (error) {
412 message.error('图片上传失败');
413 } finally {
414 setUploadLoading(false);
415 }
416 return false;
417 };
418
419 const handleDeleteImage = async () => {
420 if (coverImageUrl) {
421 try {
422 const filename = coverImageUrl.split('/').pop();
423 if (filename) {
424 await deleteImage(filename);
425 }
426 setCoverImageUrl('');
427 message.success('图片删除成功');
428 } catch (error) {
429 message.error('图片删除失败');
430 }
431 }
432 };
433
434 const handleCancelPublish = async () => {
435 // 如果有上传的图片但没有发布帖子,删除图片
436 if (coverImageUrl) {
437 try {
438 const filename = coverImageUrl.split('/').pop();
439 if (filename) {
440 await deleteImage(filename);
441 }
442 } catch (error) {
443 console.error('删除图片失败:', error);
444 }
445 }
446
447 setPublishModalVisible(false);
448 form.resetFields();
449 setSelectedPromotion(null);
450 setCoverImageUrl('');
451 };
452
453 const uploadButton = (
454 <div>
455 {uploadLoading ? <LoadingOutlined /> : <PlusOutlined />}
456 <div style={{ marginTop: 8 }}>上传封面</div>
457 </div>
458 );
459
460 const handleEditImageUpload = async (file: any) => {
461 setEditUploadLoading(true);
462 try {
463 const formData = new FormData();
464 formData.append('file', file);
465
466 const response = await uploadImage(formData);
467 if (response.code === 200 && response.data) {
468 // 如果有旧图片,删除它
469 if (editCoverImageUrl) {
470 const oldFilename = editCoverImageUrl.split('/').pop();
471 if (oldFilename) {
472 await deleteImage(oldFilename);
473 }
474 }
475
476 setEditCoverImageUrl(response.data.url);
477 message.success('图片上传成功');
478 return false;
479 } else {
480 message.error(response.msg || '图片上传失败');
481 }
482 } catch (error) {
483 message.error('图片上传失败');
484 } finally {
485 setEditUploadLoading(false);
486 }
487 return false;
488 };
489
490 const handleDeleteEditImage = async () => {
491 if (editCoverImageUrl) {
492 try {
493 const filename = editCoverImageUrl.split('/').pop();
494 if (filename) {
495 await deleteImage(filename);
496 }
497 setEditCoverImageUrl('');
498 message.success('图片删除成功');
499 } catch (error) {
500 message.error('图片删除失败');
501 }
502 }
503 };
504
505 const editUploadButton = (
506 <div>
507 {editUploadLoading ? <LoadingOutlined /> : <PlusOutlined />}
508 <div style={{ marginTop: 8 }}>上传封面</div>
509 </div>
510 );
511
512 const myPostsColumns = [
513 {
514 title: '标题',
515 dataIndex: 'title',
516 key: 'title',
517 render: (text: string, record: API.Post.PostInfo) => (
518 <a onClick={() => handleViewPost(record.postId || record.id || 0)}>{text}</a>
519 ),
520 },
521 {
522 title: '状态',
523 dataIndex: 'status',
524 key: 'status',
525 render: (status: string) => {
526 const statusMap: Record<string, { color: string; text: string }> = {
527 '0': { color: 'orange', text: '待审核' },
528 '1': { color: 'green', text: '已发布' },
529 '2': { color: 'red', text: '已拒绝' },
530 '3': { color: 'gray', text: '已下架' }
531 };
532 const statusInfo = statusMap[status] || { color: 'gray', text: '未知' };
533 return <Tag color={statusInfo.color}>{statusInfo.text}</Tag>;
534 },
535 },
536 {
537 title: '浏览量',
538 dataIndex: 'views',
539 key: 'views',
540 },
541 {
542 title: '评论数',
543 dataIndex: 'comments',
544 key: 'comments',
545 },
546 {
547 title: '收藏数',
548 dataIndex: 'favorites',
549 key: 'favorites',
550 },
551 {
552 title: '点赞数',
553 dataIndex: 'likes',
554 key: 'likes',
555 },
556 {
557 title: '发布时间',
558 dataIndex: 'publishTime',
559 key: 'publishTime',
560 },
561 {
562 title: '操作',
563 key: 'action',
564 render: (text: any, record: API.Post.PostInfo) => (
565 <Space size="middle">
566 <Button
567 type="link"
568 icon={<EyeOutlined />}
569 onClick={() => handleViewPost(record.postId || record.id || 0)}
570 >
571 查看
572 </Button>
573 <Button
574 type="link"
575 icon={<EditOutlined />}
576 onClick={() => handleEditPost(record)}
577 >
578 编辑
579 </Button>
580 <Popconfirm
581 title="确定要删除这篇帖子吗?"
582 onConfirm={() => handleDeletePost(record.postId || record.id || 0)}
583 okText="确定"
584 cancelText="取消"
585 >
586 <Button type="link" danger icon={<DeleteOutlined />}>
587 删除
588 </Button>
589 </Popconfirm>
590 </Space>
591 ),
592 },
593 ];
594
595 return (
596 <div className={styles.userCenterContainer}>
597 <Card title="个人中心" className={styles.userCenterCard}>
598 <Tabs activeKey={activeTab} onChange={setActiveTab}>
599 <TabPane tab="我的帖子" key="myPosts">
600 <div className={styles.tabContent}>
601 <div className={styles.tabHeader}>
602 <Button
603 type="primary"
604 icon={<PlusOutlined />}
605 onClick={() => setPublishModalVisible(true)}
606 >
607 发布新帖子
608 </Button>
609 </div>
610 <Table
611 columns={myPostsColumns}
612 dataSource={myPosts}
613 loading={loading}
614 rowKey="id"
615 pagination={{
616 pageSize: 10,
617 showTotal: (total) => `共 ${total} 条记录`,
618 }}
619 />
620 </div>
621 </TabPane>
622
623 <TabPane tab="我的收藏" key="favorites">
624 <div className={styles.tabContent}>
625 <Row gutter={[24, 24]}>
626 {favorites.map((post: any) => {
627 // 确保post对象有正确的id字段
628 const formattedPost = {
629 ...post,
630 id: post.postId || post.id,
631 tags: post.tags ? (Array.isArray(post.tags) ? post.tags : post.tags.split(',')) : []
632 };
633 return (
634 <Col xs={24} sm={12} md={8} key={formattedPost.id}>
635 <PostCard post={formattedPost} />
636 </Col>
637 );
638 })}
639 </Row>
640 {favorites.length === 0 && !loading && (
641 <div className={styles.emptyState}>
642 <HeartOutlined style={{ fontSize: 48, color: '#ccc' }} />
643 <p>暂无收藏的帖子</p>
644 </div>
645 )}
646 </div>
647 </TabPane>
648 </Tabs>
649 </Card>
650
651 {/* 发布帖子弹窗 */}
652 <Modal
653 title="发布新帖子"
654 open={publishModalVisible}
655 onCancel={handleCancelPublish}
656 footer={null}
657 width={800}
658 >
659 <Form
660 form={form}
661 layout="vertical"
662 onFinish={handlePublishPost}
663 >
664 <Form.Item
665 name="title"
666 label="帖子标题"
667 rules={[{ required: true, message: '请输入帖子标题' }]}
668 >
669 <Input placeholder="请输入帖子标题" />
670 </Form.Item>
671
672 <Form.Item
673 name="summary"
674 label="帖子摘要"
675 rules={[{ required: true, message: '请输入帖子摘要' }]}
676 >
677 <TextArea rows={3} placeholder="请输入帖子摘要" />
678 </Form.Item>
679
680 <Form.Item
681 name="content"
682 label="帖子内容"
683 rules={[{ required: true, message: '请输入帖子内容' }]}
684 >
685 <TextArea rows={8} placeholder="请输入帖子内容" />
686 </Form.Item>
687
688 <Form.Item
689 name="coverImage"
690 label="封面图片(可选)"
691 >
692 <Upload
693 listType="picture-card"
694 showUploadList={false}
695 beforeUpload={handleImageUpload}
696 >
697 {coverImageUrl ? (
698 <Image
699 src={coverImageUrl}
700 alt="封面"
701 width="100%"
702 height="100%"
703 style={{ objectFit: 'cover' }}
704 />
705 ) : (
706 uploadButton
707 )}
708 </Upload>
709 {coverImageUrl && (
710 <Button
711 type="link"
712 onClick={handleDeleteImage}
713 style={{ padding: 0, marginTop: 8 }}
714 >
715 删除图片
716 </Button>
717 )}
718 </Form.Item>
719
720 <Form.Item
721 name="tags"
722 label="标签"
723 rules={[{ required: true, message: '请选择标签' }]}
724 >
725 <Select
726 mode="multiple"
727 placeholder="请选择标签"
728 allowClear
729 style={{ width: '100%' }}
730 >
731 {availableTags.map(tag => (
732 <Select.Option key={tag.tagId} value={tag.tagName}>
733 <Tag color={tag.tagColor}>{tag.tagName}</Tag>
734 </Select.Option>
735 ))}
736 </Select>
737 </Form.Item>
738
739 <Form.Item
740 name="promotionPlan"
741 label="推广选项(可选)"
742 >
743 <Radio.Group>
744 <Space direction="vertical">
745 <Radio value={undefined}>不选择推广</Radio>
746 {promotionPlans.map(plan => (
747 <Radio key={plan.id} value={plan.id}>
748 <div>
749 <strong>{plan.name}</strong> - ¥{plan.price} ({plan.duration}天)
750 <br />
751 <span style={{ color: '#666', fontSize: '12px' }}>
752 {plan.description}
753 </span>
754 </div>
755 </Radio>
756 ))}
757 </Space>
758 </Radio.Group>
759 </Form.Item>
760
761 <Form.Item>
762 <Space>
763 <Button type="primary" htmlType="submit">
764 {selectedPromotion ? '选择支付方式' : '发布帖子'}
765 </Button>
766 <Button onClick={handleCancelPublish}>
767 取消
768 </Button>
769 </Space>
770 </Form.Item>
771 </Form>
772 </Modal>
773
774 {/* 编辑帖子弹窗 */}
775 <Modal
776 title="编辑帖子"
777 open={editModalVisible}
778 onCancel={() => {
779 setEditModalVisible(false);
780 editForm.resetFields();
781 setCurrentEditPost(null);
782 setEditCoverImageUrl('');
783 setIsEditingPromotion(false);
784 }}
785 footer={null}
786 width={800}
787 >
788 <Form
789 form={editForm}
790 layout="vertical"
791 onFinish={handleUpdatePost}
792 >
793 <Form.Item
794 name="title"
795 label="帖子标题"
796 rules={[{ required: true, message: '请输入帖子标题' }]}
797 >
798 <Input placeholder="请输入帖子标题" />
799 </Form.Item>
800
801 <Form.Item
802 name="summary"
803 label="帖子摘要"
804 rules={[{ required: true, message: '请输入帖子摘要' }]}
805 >
806 <TextArea rows={3} placeholder="请输入帖子摘要" />
807 </Form.Item>
808
809 <Form.Item
810 name="content"
811 label="帖子内容"
812 rules={[{ required: true, message: '请输入帖子内容' }]}
813 >
814 <TextArea rows={8} placeholder="请输入帖子内容" />
815 </Form.Item>
816
817 <Form.Item
818 name="coverImage"
819 label="封面图片"
820 >
821 <Upload
822 listType="picture-card"
823 showUploadList={false}
824 beforeUpload={handleEditImageUpload}
825 >
826 {editCoverImageUrl ? (
827 <Image
828 src={editCoverImageUrl}
829 alt="封面"
830 width="100%"
831 height="100%"
832 style={{ objectFit: 'cover' }}
833 />
834 ) : (
835 editUploadButton
836 )}
837 </Upload>
838 {editCoverImageUrl && (
839 <Button
840 type="link"
841 onClick={handleDeleteEditImage}
842 style={{ padding: 0, marginTop: 8 }}
843 >
844 删除图片
845 </Button>
846 )}
847 </Form.Item>
848
849 <Form.Item
850 name="tags"
851 label="标签"
852 rules={[{ required: true, message: '请选择标签' }]}
853 >
854 <Select
855 mode="multiple"
856 placeholder="请选择标签"
857 allowClear
858 style={{ width: '100%' }}
859 >
860 {availableTags.map(tag => (
861 <Select.Option key={tag.tagId} value={tag.tagName}>
862 <Tag color={tag.tagColor}>{tag.tagName}</Tag>
863 </Select.Option>
864 ))}
865 </Select>
866 </Form.Item>
867
868 <Form.Item
869 name="promotionPlan"
870 label="推广选项(可选)"
871 >
872 <Radio.Group disabled={isEditingPromotion}>
873 <Space direction="vertical">
874 <Radio value={undefined}>不选择推广</Radio>
875 {isEditingPromotion && (
876 <div style={{ color: '#ff4d4f', fontSize: '12px', marginBottom: 8 }}>
877 该帖子已购买推广,无法更改推广选项
878 </div>
879 )}
880 {promotionPlans.map(plan => (
881 <Radio key={plan.id} value={plan.id} disabled={isEditingPromotion}>
882 <div>
883 <strong>{plan.name}</strong> - ¥{plan.price} ({plan.duration}天)
884 <br />
885 <span style={{ color: '#666', fontSize: '12px' }}>
886 {plan.description}
887 </span>
888 </div>
889 </Radio>
890 ))}
891 </Space>
892 </Radio.Group>
893 </Form.Item>
894
895 <Form.Item>
896 <Space>
897 <Button type="primary" htmlType="submit">
898 更新帖子
899 </Button>
900 <Button onClick={() => {
901 setEditModalVisible(false);
902 editForm.resetFields();
903 setCurrentEditPost(null);
904 setEditCoverImageUrl('');
905 setIsEditingPromotion(false);
906 }}>
907 取消
908 </Button>
909 </Space>
910 </Form.Item>
911 </Form>
912 </Modal>
913
914 {/* 支付弹窗 */}
915 <Modal
916 title="支付推广费用"
917 open={paymentModalVisible}
918 onCancel={handlePaymentCancel}
919 footer={null}
920 width={400}
921 >
922 <div className={styles.paymentModal}>
923 {selectedPromotion && (
924 <>
925 <div className={styles.paymentInfo}>
926 <h3>{selectedPromotion.name}</h3>
927 <p>{selectedPromotion.description}</p>
928 <p>费用: <strong>¥{selectedPromotion.price}</strong></p>
929 <p>时长: {selectedPromotion.duration}天</p>
930 </div>
931
932 <div className={styles.qrCode}>
933 <div className={styles.qrCodePlaceholder}>
934 <p>支付二维码</p>
935 <p style={{ fontSize: '12px', color: '#666' }}>
936 请使用支付宝扫描二维码支付
937 </p>
938 <div className={styles.mockQrCode}>
939 <p>模拟二维码</p>
940 <p>¥{selectedPromotion.price}</p>
941 </div>
942 </div>
943 </div>
944
945 <div className={styles.paymentActions}>
946 <Button
947 type="primary"
948 onClick={handlePaymentConfirm}
949 style={{ width: '100%', marginBottom: 8 }}
950 >
951 我已完成支付
952 </Button>
953 <Button
954 onClick={handlePaymentCancel}
955 style={{ width: '100%' }}
956 >
957 取消支付
958 </Button>
959 </div>
960 </>
961 )}
962 </div>
963 </Modal>
964 </div>
965 );
966};
967
968export default UserCenter;