| import React, { useState, useEffect } from 'react'; |
| import { |
| Card, |
| Tabs, |
| Button, |
| Table, |
| Modal, |
| Form, |
| Input, |
| Select, |
| Upload, |
| message, |
| Tag, |
| Space, |
| Popconfirm, |
| Row, |
| Col, |
| Radio, |
| InputNumber, |
| Image |
| } from 'antd'; |
| import { |
| PlusOutlined, |
| EditOutlined, |
| DeleteOutlined, |
| EyeOutlined, |
| UploadOutlined, |
| HeartOutlined, |
| LoadingOutlined |
| } from '@ant-design/icons'; |
| import { useNavigate } from 'react-router-dom'; |
| import { |
| getMyPosts, |
| getMyFavorites, |
| publishPost, |
| updatePost, |
| deletePost, |
| getAvailableTags, |
| uploadImage, |
| deleteImage, |
| getPromotionPlans, |
| createPayment, |
| getPromotionStatus, |
| confirmPayment, |
| cancelPayment |
| } from '@/services/post'; |
| import PostCard from '../PostCenter/PostCard'; |
| import styles from './index.module.css'; |
| |
| const { TabPane } = Tabs; |
| const { TextArea } = Input; |
| const { Option } = Select; |
| |
| interface PostFormData { |
| title: string; |
| content: string; |
| summary: string; |
| tags: string[] | string; |
| promotionPlan?: number; |
| coverImage?: string; |
| } |
| |
| interface PromotionPlan { |
| id: number; |
| name: string; |
| description: string; |
| price: number; |
| duration: number; |
| } |
| |
| interface PaymentRecord { |
| paymentId: number; |
| postId: number; |
| planId: number; |
| userId: number; |
| amount: number; |
| paymentStatus: string; |
| paymentTime: string; |
| } |
| |
| const UserCenter: React.FC = () => { |
| const navigate = useNavigate(); |
| const [activeTab, setActiveTab] = useState('myPosts'); |
| const [publishModalVisible, setPublishModalVisible] = useState(false); |
| const [editModalVisible, setEditModalVisible] = useState(false); |
| const [paymentModalVisible, setPaymentModalVisible] = useState(false); |
| const [myPosts, setMyPosts] = useState<API.Post.PostInfo[]>([]); |
| const [favorites, setFavorites] = useState<API.Post.PostInfo[]>([]); |
| const [loading, setLoading] = useState(false); |
| const [form] = Form.useForm(); |
| const [editForm] = Form.useForm(); |
| const [selectedPromotion, setSelectedPromotion] = useState<PromotionPlan | null>(null); |
| const [currentEditPost, setCurrentEditPost] = useState<API.Post.PostInfo | null>(null); |
| const [availableTags, setAvailableTags] = useState<API.Post.PostTag[]>([]); |
| const [promotionPlans, setPromotionPlans] = useState<PromotionPlan[]>([]); |
| const [uploadLoading, setUploadLoading] = useState(false); |
| const [editUploadLoading, setEditUploadLoading] = useState(false); |
| const [coverImageUrl, setCoverImageUrl] = useState<string>(''); |
| const [editCoverImageUrl, setEditCoverImageUrl] = useState<string>(''); |
| const [currentPayment, setCurrentPayment] = useState<PaymentRecord | null>(null); |
| const [isEditingPromotion, setIsEditingPromotion] = useState(false); |
| const [currentPublishingPostId, setCurrentPublishingPostId] = useState<number | null>(null); // 跟踪当前正在发布的帖子ID |
| |
| useEffect(() => { |
| if (activeTab === 'myPosts') { |
| fetchMyPosts(); |
| } else if (activeTab === 'favorites') { |
| fetchFavorites(); |
| } |
| }, [activeTab]); |
| |
| useEffect(() => { |
| fetchAvailableTags(); |
| fetchPromotionPlans(); |
| }, []); |
| |
| const fetchMyPosts = async () => { |
| setLoading(true); |
| try { |
| const response = await getMyPosts({ pageNum: 1, pageSize: 100 }); |
| if (response.code === 200) { |
| setMyPosts(response.rows || []); |
| } else { |
| message.error(response.msg || '获取我的帖子失败'); |
| } |
| } catch (error) { |
| message.error('获取我的帖子失败'); |
| } finally { |
| setLoading(false); |
| } |
| }; |
| |
| const fetchFavorites = async () => { |
| setLoading(true); |
| try { |
| const response = await getMyFavorites({ pageNum: 1, pageSize: 100 }); |
| if (response.code === 200) { |
| setFavorites(response.rows || []); |
| } else { |
| message.error(response.msg || '获取收藏列表失败'); |
| } |
| } catch (error) { |
| message.error('获取收藏列表失败'); |
| } finally { |
| setLoading(false); |
| } |
| }; |
| |
| const fetchAvailableTags = async () => { |
| try { |
| const response = await getAvailableTags(); |
| if (response.code === 200) { |
| setAvailableTags(response.data || []); |
| } else { |
| message.error(response.msg || '获取可用标签失败'); |
| } |
| } catch (error) { |
| message.error('获取可用标签失败'); |
| } |
| }; |
| |
| const fetchPromotionPlans = async () => { |
| try { |
| const response = await getPromotionPlans(); |
| if (response.code === 200) { |
| setPromotionPlans(response.data || []); |
| } |
| } catch (error) { |
| console.error('获取推广计划失败:', error); |
| } |
| }; |
| |
| const handlePublishPost = async (values: PostFormData) => { |
| try { |
| // 如果选择了推广计划 |
| if (values.promotionPlan) { |
| const selectedPlan = promotionPlans.find(p => p.id === values.promotionPlan); |
| if (selectedPlan) { |
| setSelectedPromotion(selectedPlan); |
| |
| let postId = currentPublishingPostId; |
| |
| // 如果还没有创建帖子,先创建帖子 |
| if (!postId) { |
| const postData = { |
| title: values.title, |
| content: values.content, |
| summary: values.summary, |
| tags: Array.isArray(values.tags) ? values.tags.join(',') : values.tags, |
| coverImage: coverImageUrl || undefined |
| // 注意:这里不包含promotionPlan,等支付成功后再更新 |
| }; |
| |
| const publishResponse = await publishPost(postData); |
| if (publishResponse.code === 200) { |
| postId = publishResponse.data?.postId; |
| if (postId) { |
| setCurrentPublishingPostId(postId); // 保存已创建的帖子ID |
| } else { |
| message.error('帖子发布成功但无法获取帖子ID'); |
| return; |
| } |
| } else { |
| message.error(publishResponse.msg || '帖子发布失败'); |
| return; |
| } |
| } |
| |
| // 使用帖子ID创建支付记录 |
| if (postId) { |
| const paymentResponse = await createPayment({ |
| postId: postId, |
| planId: selectedPlan.id, |
| amount: selectedPlan.price |
| }); |
| |
| if (paymentResponse.code === 200) { |
| setCurrentPayment(paymentResponse.data); |
| setPaymentModalVisible(true); |
| return; |
| } else { |
| message.error(paymentResponse.msg || '创建支付记录失败'); |
| return; |
| } |
| } |
| } else { |
| message.error('无效的推广计划'); |
| return; |
| } |
| } |
| |
| // 直接发布帖子(没有选择推广) |
| await submitPost(values); |
| } catch (error) { |
| message.error('发布帖子失败'); |
| } |
| }; |
| |
| const submitPost = async (values: PostFormData) => { |
| try { |
| // 处理标签格式 |
| const tagsString = Array.isArray(values.tags) ? values.tags.join(',') : values.tags; |
| |
| // 如果有选择的推广计划,使用推广计划ID,否则使用表单中的值 |
| const promotionPlanId = selectedPromotion?.id || values.promotionPlan; |
| |
| const postData = { |
| title: values.title, |
| content: values.content, |
| summary: values.summary, |
| tags: tagsString, |
| promotionPlan: promotionPlanId, |
| coverImage: coverImageUrl || undefined |
| }; |
| |
| const response = await publishPost(postData); |
| if (response.code === 200) { |
| message.success('帖子发布成功'); |
| setPublishModalVisible(false); |
| form.resetFields(); |
| setSelectedPromotion(null); |
| setCoverImageUrl(''); |
| fetchMyPosts(); |
| } else { |
| message.error(response.msg || '发布帖子失败'); |
| } |
| } catch (error) { |
| message.error('发布帖子失败'); |
| } |
| }; |
| |
| const handleEditPost = async (post: API.Post.PostInfo) => { |
| setCurrentEditPost(post); |
| const tagsArray = post.tags ? (typeof post.tags === 'string' ? post.tags.split(',') : post.tags) : []; |
| |
| // 检查推广状态 |
| try { |
| const promotionResponse = await getPromotionStatus(post.postId || post.id || 0); |
| if (promotionResponse.code === 200) { |
| const { hasPromotion, promotionPlanId } = promotionResponse.data; |
| setIsEditingPromotion(hasPromotion); |
| |
| editForm.setFieldsValue({ |
| title: post.title, |
| content: post.content, |
| summary: post.summary, |
| tags: tagsArray, |
| promotionPlan: hasPromotion ? promotionPlanId : undefined |
| }); |
| } |
| } catch (error) { |
| console.error('获取推广状态失败:', error); |
| editForm.setFieldsValue({ |
| title: post.title, |
| content: post.content, |
| summary: post.summary, |
| tags: tagsArray, |
| promotionPlan: post.promotionPlanId |
| }); |
| } |
| |
| setEditCoverImageUrl(post.coverImage || ''); |
| setEditModalVisible(true); |
| }; |
| |
| const handleUpdatePost = async (values: any) => { |
| if (!currentEditPost) return; |
| |
| try { |
| // 处理标签格式 |
| const tagsString = Array.isArray(values.tags) ? values.tags.join(',') : values.tags; |
| |
| // 检查是否选择了新的推广计划 |
| const hasNewPromotion = values.promotionPlan && !isEditingPromotion; |
| |
| if (hasNewPromotion) { |
| // 如果选择了新的推广计划,需要先创建支付记录 |
| const selectedPlan = promotionPlans.find(p => p.id === values.promotionPlan); |
| if (selectedPlan) { |
| setSelectedPromotion(selectedPlan); |
| |
| // 创建支付记录 |
| const paymentResponse = await createPayment({ |
| postId: currentEditPost.postId || currentEditPost.id || 0, |
| planId: selectedPlan.id, |
| amount: selectedPlan.price |
| }); |
| |
| if (paymentResponse.code === 200) { |
| setCurrentPayment(paymentResponse.data); |
| setPaymentModalVisible(true); |
| return; // 等待支付完成后再更新帖子 |
| } else { |
| message.error(paymentResponse.msg || '创建支付记录失败'); |
| return; |
| } |
| } |
| } |
| |
| // 直接更新帖子(没有新推广或已有推广) |
| await updatePostDirectly(values, tagsString); |
| } catch (error) { |
| message.error('更新帖子失败'); |
| } |
| }; |
| |
| const updatePostDirectly = async (values: any, tagsString: string) => { |
| if (!currentEditPost) return; |
| |
| const updateData = { |
| ...currentEditPost, |
| title: values.title, |
| content: values.content, |
| summary: values.summary, |
| tags: tagsString, |
| coverImage: editCoverImageUrl || currentEditPost.coverImage, |
| promotionPlanId: values.promotionPlan |
| }; |
| |
| const response = await updatePost(updateData); |
| if (response.code === 200) { |
| message.success('帖子更新成功'); |
| setEditModalVisible(false); |
| editForm.resetFields(); |
| setCurrentEditPost(null); |
| setEditCoverImageUrl(''); |
| setIsEditingPromotion(false); |
| fetchMyPosts(); |
| } else { |
| message.error(response.msg || '更新帖子失败'); |
| } |
| }; |
| |
| const handleDeletePost = async (postId: number) => { |
| try { |
| const response = await deletePost(postId); |
| if (response.code === 200) { |
| message.success('帖子删除成功'); |
| fetchMyPosts(); |
| } else { |
| message.error(response.msg || '删除帖子失败'); |
| } |
| } catch (error) { |
| message.error('删除帖子失败'); |
| } |
| }; |
| |
| const handleViewPost = (postId: number) => { |
| navigate(`/post-detail/${postId}`); |
| }; |
| |
| const handlePaymentConfirm = async () => { |
| if (!currentPayment) return; |
| |
| try { |
| const response = await confirmPayment(currentPayment.paymentId); |
| if (response.code === 200) { |
| message.success('支付成功,推广已生效'); |
| setPaymentModalVisible(false); |
| setCurrentPayment(null); |
| |
| // 如果是编辑模式,完成帖子更新 |
| if (editModalVisible && currentEditPost) { |
| const values = editForm.getFieldsValue(); |
| const tagsString = Array.isArray(values.tags) ? values.tags.join(',') : values.tags; |
| await updatePostDirectly(values, tagsString); |
| } else if (publishModalVisible) { |
| // 如果是发布模式,支付成功后关闭所有弹窗,刷新帖子列表,清空状态 |
| setPublishModalVisible(false); |
| form.resetFields(); |
| setCoverImageUrl(''); |
| setCurrentPublishingPostId(null); // 清空已创建的帖子ID |
| fetchMyPosts(); |
| } |
| |
| setSelectedPromotion(null); |
| } else { |
| message.error(response.msg || '支付确认失败'); |
| } |
| } catch (error) { |
| message.error('支付确认失败'); |
| } |
| }; |
| |
| const handlePaymentCancel = async () => { |
| if (!currentPayment) return; |
| |
| try { |
| await cancelPayment(currentPayment.paymentId); |
| message.info('支付已取消'); |
| setPaymentModalVisible(false); |
| setCurrentPayment(null); |
| setSelectedPromotion(null); |
| |
| // 如果是发布模式,支付取消后返回发布页面(不关闭发布弹窗) |
| // 保持currentPublishingPostId,用户可以重新支付 |
| // 如果是编辑模式,关闭编辑弹窗 |
| if (editModalVisible) { |
| setEditModalVisible(false); |
| editForm.resetFields(); |
| setCurrentEditPost(null); |
| setEditCoverImageUrl(''); |
| setIsEditingPromotion(false); |
| } |
| } catch (error) { |
| console.error('取消支付失败:', error); |
| setPaymentModalVisible(false); |
| setCurrentPayment(null); |
| setSelectedPromotion(null); |
| } |
| }; |
| |
| const handleImageUpload = async (file: any) => { |
| setUploadLoading(true); |
| try { |
| const formData = new FormData(); |
| formData.append('file', file); |
| |
| const response = await uploadImage(formData); |
| if (response.code === 200 && response.data) { |
| setCoverImageUrl(response.data.url); |
| message.success('图片上传成功'); |
| return false; // 阻止自动上传 |
| } else { |
| message.error(response.msg || '图片上传失败'); |
| } |
| } catch (error) { |
| message.error('图片上传失败'); |
| } finally { |
| setUploadLoading(false); |
| } |
| return false; |
| }; |
| |
| const handleDeleteImage = async () => { |
| if (coverImageUrl) { |
| try { |
| const filename = coverImageUrl.split('/').pop(); |
| if (filename) { |
| await deleteImage(filename); |
| } |
| setCoverImageUrl(''); |
| message.success('图片删除成功'); |
| } catch (error) { |
| message.error('图片删除失败'); |
| } |
| } |
| }; |
| |
| const handleCancelPublish = async () => { |
| // 如果有已创建的帖子但还没有支付成功,需要删除这个帖子 |
| if (currentPublishingPostId) { |
| try { |
| await deletePost(currentPublishingPostId); |
| message.info('已取消发布并删除草稿帖子'); |
| } catch (error) { |
| console.error('删除草稿帖子失败:', error); |
| message.warning('取消发布成功,但删除草稿帖子失败'); |
| } |
| } |
| |
| // 如果有上传的图片但没有发布帖子,删除图片 |
| if (coverImageUrl) { |
| try { |
| const filename = coverImageUrl.split('/').pop(); |
| if (filename) { |
| await deleteImage(filename); |
| } |
| } catch (error) { |
| console.error('删除图片失败:', error); |
| } |
| } |
| |
| setPublishModalVisible(false); |
| form.resetFields(); |
| setSelectedPromotion(null); |
| setCoverImageUrl(''); |
| setCurrentPublishingPostId(null); // 清空已创建的帖子ID |
| }; |
| |
| const uploadButton = ( |
| <div> |
| {uploadLoading ? <LoadingOutlined /> : <PlusOutlined />} |
| <div style={{ marginTop: 8 }}>上传封面</div> |
| </div> |
| ); |
| |
| const handleEditImageUpload = async (file: any) => { |
| setEditUploadLoading(true); |
| try { |
| const formData = new FormData(); |
| formData.append('file', file); |
| |
| const response = await uploadImage(formData); |
| if (response.code === 200 && response.data) { |
| // 如果有旧图片,删除它 |
| if (editCoverImageUrl) { |
| const oldFilename = editCoverImageUrl.split('/').pop(); |
| if (oldFilename) { |
| await deleteImage(oldFilename); |
| } |
| } |
| |
| setEditCoverImageUrl(response.data.url); |
| message.success('图片上传成功'); |
| return false; |
| } else { |
| message.error(response.msg || '图片上传失败'); |
| } |
| } catch (error) { |
| message.error('图片上传失败'); |
| } finally { |
| setEditUploadLoading(false); |
| } |
| return false; |
| }; |
| |
| const handleDeleteEditImage = async () => { |
| if (editCoverImageUrl) { |
| try { |
| const filename = editCoverImageUrl.split('/').pop(); |
| if (filename) { |
| await deleteImage(filename); |
| } |
| setEditCoverImageUrl(''); |
| message.success('图片删除成功'); |
| } catch (error) { |
| message.error('图片删除失败'); |
| } |
| } |
| }; |
| |
| const editUploadButton = ( |
| <div> |
| {editUploadLoading ? <LoadingOutlined /> : <PlusOutlined />} |
| <div style={{ marginTop: 8 }}>上传封面</div> |
| </div> |
| ); |
| |
| const myPostsColumns = [ |
| { |
| title: '标题', |
| dataIndex: 'title', |
| key: 'title', |
| render: (text: string, record: API.Post.PostInfo) => ( |
| <a onClick={() => handleViewPost(record.postId || record.id || 0)}>{text}</a> |
| ), |
| }, |
| { |
| title: '状态', |
| dataIndex: 'status', |
| key: 'status', |
| render: (status: string) => { |
| const statusMap: Record<string, { color: string; text: string }> = { |
| '0': { color: 'orange', text: '待审核' }, |
| '1': { color: 'green', text: '已发布' }, |
| '2': { color: 'red', text: '已拒绝' }, |
| '3': { color: 'gray', text: '已下架' } |
| }; |
| const statusInfo = statusMap[status] || { color: 'gray', text: '未知' }; |
| return <Tag color={statusInfo.color}>{statusInfo.text}</Tag>; |
| }, |
| }, |
| { |
| title: '浏览量', |
| dataIndex: 'views', |
| key: 'views', |
| }, |
| { |
| title: '评论数', |
| dataIndex: 'comments', |
| key: 'comments', |
| }, |
| { |
| title: '收藏数', |
| dataIndex: 'favorites', |
| key: 'favorites', |
| }, |
| { |
| title: '点赞数', |
| dataIndex: 'likes', |
| key: 'likes', |
| }, |
| { |
| title: '发布时间', |
| dataIndex: 'publishTime', |
| key: 'publishTime', |
| }, |
| { |
| title: '操作', |
| key: 'action', |
| render: (text: any, record: API.Post.PostInfo) => ( |
| <Space size="middle"> |
| <Button |
| type="link" |
| icon={<EyeOutlined />} |
| onClick={() => handleViewPost(record.postId || record.id || 0)} |
| > |
| 查看 |
| </Button> |
| <Button |
| type="link" |
| icon={<EditOutlined />} |
| onClick={() => handleEditPost(record)} |
| > |
| 编辑 |
| </Button> |
| <Popconfirm |
| title="确定要删除这篇帖子吗?" |
| onConfirm={() => handleDeletePost(record.postId || record.id || 0)} |
| okText="确定" |
| cancelText="取消" |
| > |
| <Button type="link" danger icon={<DeleteOutlined />}> |
| 删除 |
| </Button> |
| </Popconfirm> |
| </Space> |
| ), |
| }, |
| ]; |
| |
| return ( |
| <div className={styles.userCenterContainer}> |
| <Card title="个人中心" className={styles.userCenterCard}> |
| <Tabs activeKey={activeTab} onChange={setActiveTab}> |
| <TabPane tab="我的帖子" key="myPosts"> |
| <div className={styles.tabContent}> |
| <div className={styles.tabHeader}> |
| <Button |
| type="primary" |
| icon={<PlusOutlined />} |
| onClick={() => setPublishModalVisible(true)} |
| > |
| 发布新帖子 |
| </Button> |
| </div> |
| <Table |
| columns={myPostsColumns} |
| dataSource={myPosts} |
| loading={loading} |
| rowKey="id" |
| pagination={{ |
| pageSize: 10, |
| showTotal: (total) => `共 ${total} 条记录`, |
| }} |
| /> |
| </div> |
| </TabPane> |
| |
| <TabPane tab="我的收藏" key="favorites"> |
| <div className={styles.tabContent}> |
| <Row gutter={[24, 24]}> |
| {favorites.map((post: any) => { |
| // 确保post对象有正确的id字段 |
| const formattedPost = { |
| ...post, |
| id: post.postId || post.id, |
| tags: post.tags ? (Array.isArray(post.tags) ? post.tags : post.tags.split(',')) : [] |
| }; |
| return ( |
| <Col xs={24} sm={12} md={8} key={formattedPost.id}> |
| <PostCard post={formattedPost} /> |
| </Col> |
| ); |
| })} |
| </Row> |
| {favorites.length === 0 && !loading && ( |
| <div className={styles.emptyState}> |
| <HeartOutlined style={{ fontSize: 48, color: '#ccc' }} /> |
| <p>暂无收藏的帖子</p> |
| </div> |
| )} |
| </div> |
| </TabPane> |
| </Tabs> |
| </Card> |
| |
| {/* 发布帖子弹窗 */} |
| <Modal |
| title="发布新帖子" |
| open={publishModalVisible} |
| onCancel={handleCancelPublish} |
| footer={null} |
| width={800} |
| > |
| <Form |
| form={form} |
| layout="vertical" |
| onFinish={handlePublishPost} |
| > |
| <Form.Item |
| name="title" |
| label="帖子标题" |
| rules={[{ required: true, message: '请输入帖子标题' }]} |
| > |
| <Input placeholder="请输入帖子标题" /> |
| </Form.Item> |
| |
| <Form.Item |
| name="summary" |
| label="帖子摘要" |
| rules={[{ required: true, message: '请输入帖子摘要' }]} |
| > |
| <TextArea rows={3} placeholder="请输入帖子摘要" /> |
| </Form.Item> |
| |
| <Form.Item |
| name="content" |
| label="帖子内容" |
| rules={[{ required: true, message: '请输入帖子内容' }]} |
| > |
| <TextArea rows={8} placeholder="请输入帖子内容" /> |
| </Form.Item> |
| |
| <Form.Item |
| name="coverImage" |
| label="封面图片(可选)" |
| > |
| <Upload |
| listType="picture-card" |
| showUploadList={false} |
| beforeUpload={handleImageUpload} |
| > |
| {coverImageUrl ? ( |
| <Image |
| src={coverImageUrl} |
| alt="封面" |
| width="100%" |
| height="100%" |
| style={{ objectFit: 'cover' }} |
| /> |
| ) : ( |
| uploadButton |
| )} |
| </Upload> |
| {coverImageUrl && ( |
| <Button |
| type="link" |
| onClick={handleDeleteImage} |
| style={{ padding: 0, marginTop: 8 }} |
| > |
| 删除图片 |
| </Button> |
| )} |
| </Form.Item> |
| |
| <Form.Item |
| name="tags" |
| label="标签" |
| rules={[{ required: true, message: '请选择标签' }]} |
| > |
| <Select |
| mode="multiple" |
| placeholder="请选择标签" |
| allowClear |
| style={{ width: '100%' }} |
| > |
| {availableTags.map(tag => ( |
| <Select.Option key={tag.tagId} value={tag.tagName}> |
| <Tag color={tag.tagColor}>{tag.tagName}</Tag> |
| </Select.Option> |
| ))} |
| </Select> |
| </Form.Item> |
| |
| <Form.Item |
| name="promotionPlan" |
| label="推广选项(可选)" |
| > |
| <Radio.Group> |
| <Space direction="vertical"> |
| <Radio value={undefined}>不选择推广</Radio> |
| {promotionPlans.map(plan => ( |
| <Radio key={plan.id} value={plan.id}> |
| <div> |
| <strong>{plan.name}</strong> - ¥{plan.price} ({plan.duration}天) |
| <br /> |
| <span style={{ color: '#666', fontSize: '12px' }}> |
| {plan.description} |
| </span> |
| </div> |
| </Radio> |
| ))} |
| </Space> |
| </Radio.Group> |
| </Form.Item> |
| |
| <Form.Item> |
| <Space> |
| <Button type="primary" htmlType="submit"> |
| {selectedPromotion ? '选择支付方式' : '发布帖子'} |
| </Button> |
| <Button onClick={handleCancelPublish}> |
| 取消 |
| </Button> |
| </Space> |
| </Form.Item> |
| </Form> |
| </Modal> |
| |
| {/* 编辑帖子弹窗 */} |
| <Modal |
| title="编辑帖子" |
| open={editModalVisible} |
| onCancel={() => { |
| setEditModalVisible(false); |
| editForm.resetFields(); |
| setCurrentEditPost(null); |
| setEditCoverImageUrl(''); |
| setIsEditingPromotion(false); |
| }} |
| footer={null} |
| width={800} |
| > |
| <Form |
| form={editForm} |
| layout="vertical" |
| onFinish={handleUpdatePost} |
| > |
| <Form.Item |
| name="title" |
| label="帖子标题" |
| rules={[{ required: true, message: '请输入帖子标题' }]} |
| > |
| <Input placeholder="请输入帖子标题" /> |
| </Form.Item> |
| |
| <Form.Item |
| name="summary" |
| label="帖子摘要" |
| rules={[{ required: true, message: '请输入帖子摘要' }]} |
| > |
| <TextArea rows={3} placeholder="请输入帖子摘要" /> |
| </Form.Item> |
| |
| <Form.Item |
| name="content" |
| label="帖子内容" |
| rules={[{ required: true, message: '请输入帖子内容' }]} |
| > |
| <TextArea rows={8} placeholder="请输入帖子内容" /> |
| </Form.Item> |
| |
| <Form.Item |
| name="coverImage" |
| label="封面图片" |
| > |
| <Upload |
| listType="picture-card" |
| showUploadList={false} |
| beforeUpload={handleEditImageUpload} |
| > |
| {editCoverImageUrl ? ( |
| <Image |
| src={editCoverImageUrl} |
| alt="封面" |
| width="100%" |
| height="100%" |
| style={{ objectFit: 'cover' }} |
| /> |
| ) : ( |
| editUploadButton |
| )} |
| </Upload> |
| {editCoverImageUrl && ( |
| <Button |
| type="link" |
| onClick={handleDeleteEditImage} |
| style={{ padding: 0, marginTop: 8 }} |
| > |
| 删除图片 |
| </Button> |
| )} |
| </Form.Item> |
| |
| <Form.Item |
| name="tags" |
| label="标签" |
| rules={[{ required: true, message: '请选择标签' }]} |
| > |
| <Select |
| mode="multiple" |
| placeholder="请选择标签" |
| allowClear |
| style={{ width: '100%' }} |
| > |
| {availableTags.map(tag => ( |
| <Select.Option key={tag.tagId} value={tag.tagName}> |
| <Tag color={tag.tagColor}>{tag.tagName}</Tag> |
| </Select.Option> |
| ))} |
| </Select> |
| </Form.Item> |
| |
| <Form.Item |
| name="promotionPlan" |
| label="推广选项(可选)" |
| > |
| <Radio.Group disabled={isEditingPromotion}> |
| <Space direction="vertical"> |
| <Radio value={undefined}>不选择推广</Radio> |
| {isEditingPromotion && ( |
| <div style={{ color: '#ff4d4f', fontSize: '12px', marginBottom: 8 }}> |
| 该帖子已购买推广,无法更改推广选项 |
| </div> |
| )} |
| {promotionPlans.map(plan => ( |
| <Radio key={plan.id} value={plan.id} disabled={isEditingPromotion}> |
| <div> |
| <strong>{plan.name}</strong> - ¥{plan.price} ({plan.duration}天) |
| <br /> |
| <span style={{ color: '#666', fontSize: '12px' }}> |
| {plan.description} |
| </span> |
| </div> |
| </Radio> |
| ))} |
| </Space> |
| </Radio.Group> |
| </Form.Item> |
| |
| <Form.Item> |
| <Space> |
| <Button type="primary" htmlType="submit"> |
| 更新帖子 |
| </Button> |
| <Button onClick={() => { |
| setEditModalVisible(false); |
| editForm.resetFields(); |
| setCurrentEditPost(null); |
| setEditCoverImageUrl(''); |
| setIsEditingPromotion(false); |
| }}> |
| 取消 |
| </Button> |
| </Space> |
| </Form.Item> |
| </Form> |
| </Modal> |
| |
| {/* 支付弹窗 */} |
| <Modal |
| title="支付推广费用" |
| open={paymentModalVisible} |
| onCancel={handlePaymentCancel} |
| footer={null} |
| width={400} |
| > |
| <div className={styles.paymentModal}> |
| {selectedPromotion && ( |
| <> |
| <div className={styles.paymentInfo}> |
| <h3>{selectedPromotion.name}</h3> |
| <p>{selectedPromotion.description}</p> |
| <p>费用: <strong>¥{selectedPromotion.price}</strong></p> |
| <p>时长: {selectedPromotion.duration}天</p> |
| </div> |
| |
| <div className={styles.qrCode}> |
| <div className={styles.qrCodePlaceholder}> |
| <p>支付二维码</p> |
| <p style={{ fontSize: '12px', color: '#666' }}> |
| 请使用支付宝扫描二维码支付 |
| </p> |
| <div className={styles.mockQrCode}> |
| <p>模拟二维码</p> |
| <p>¥{selectedPromotion.price}</p> |
| </div> |
| </div> |
| </div> |
| |
| <div className={styles.paymentActions}> |
| <Button |
| type="primary" |
| onClick={handlePaymentConfirm} |
| style={{ width: '100%', marginBottom: 8 }} |
| > |
| 我已完成支付 |
| </Button> |
| <Button |
| onClick={handlePaymentCancel} |
| style={{ width: '100%' }} |
| > |
| 取消支付 |
| </Button> |
| </div> |
| </> |
| )} |
| </div> |
| </Modal> |
| </div> |
| ); |
| }; |
| |
| export default UserCenter; |