创作中心模块包含首页展示、个人中心、帖子审核。
“首页展示”支持广告轮播展示、推广帖子优先展示、分页显示所有帖子、导航栏便捷标签筛选帖子、全局标题模糊搜索帖子、点击帖子“查看更多”进入帖子详情页。帖子详情页展示帖子封面图片、作者时间、详细内容(可以插入种子链接对种子进行介绍与推广)等基本信息、对帖子点赞收藏举报评论回复、查看相关推荐帖子。相关推荐会推荐当前帖子作者的其他帖子(最多推荐5篇),还会推荐具有相似标签的其他帖子,两者总共最多推荐9篇帖子。
“个人中心”包含“我的中心”和“我的收藏”。
“我的中心”中可以管理已经成功发布的帖子(编辑、删除帖子),还可以发布新帖子。发布新帖子时除了填写帖子基本信息以外,帖子标签支持下拉多项选择,用户还可以选择帖子推广项目并进行支付。设置了多种推广项目,包含广告轮播推广、帖子置顶展示、限时优先展示、分类页首条展示。系统后台执行自动定时任务,每小时对帖子的推广时效性进行检查,如超出推广时限,则取消帖子的推广显示特权。用户点击发布帖子后帖子处于待审核状态,需要管理员审核通过才能正常发布在首页展示页面。编辑帖子时用户可以追加帖子推广,但如果帖子处于推广状态,则禁止修改推广项目。
“我的收藏”中可以便捷查看所有已收藏的帖子。
“帖子审核”包含“帖子发布管理”和“帖子举报管理”。“帖子审核”板块具有权限管理,只有管理员界面能够进入。
“帖子发布管理”对所有待审核帖子进行处理,支持预览待审核帖子详细内容,批准通过和拒绝通过选项。
“帖子举报管理”对所有用户的举报请求进行人工审核,如果举报内容属实,则将帖子下架处理,如果举报内容不属实,驳回举报请求。所有举报请求的处理结果均留存显示,方便后续再次审查。
Change-Id: If822351183e9d55a5a56ff5cf1e13b313fdbe231
diff --git a/src/pages/UserCenter/index.tsx b/src/pages/UserCenter/index.tsx
new file mode 100644
index 0000000..13de6ad
--- /dev/null
+++ b/src/pages/UserCenter/index.tsx
@@ -0,0 +1,968 @@
+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);
+
+ 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 && selectedPromotion) {
+ // 如果选择了推广,创建支付记录
+ const paymentResponse = await createPayment({
+ postId: 0, // 新帖子,暂时设为0,后端会处理
+ planId: selectedPromotion.id,
+ amount: selectedPromotion.price
+ });
+
+ if (paymentResponse.code === 200) {
+ setCurrentPayment(paymentResponse.data);
+ setPaymentModalVisible(true);
+ return;
+ } else {
+ message.error(paymentResponse.msg || '创建支付记录失败');
+ 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;
+
+ const postData = {
+ title: values.title,
+ content: values.content,
+ summary: values.summary,
+ tags: tagsString,
+ promotionPlan: values.promotionPlan,
+ 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 {
+ // 如果是发布模式
+ setPublishModalVisible(false);
+ form.resetFields();
+ setSelectedPromotion(null);
+ setCoverImageUrl('');
+ fetchMyPosts();
+ }
+ } 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);
+ } 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 (coverImageUrl) {
+ try {
+ const filename = coverImageUrl.split('/').pop();
+ if (filename) {
+ await deleteImage(filename);
+ }
+ } catch (error) {
+ console.error('删除图片失败:', error);
+ }
+ }
+
+ setPublishModalVisible(false);
+ form.resetFields();
+ setSelectedPromotion(null);
+ setCoverImageUrl('');
+ };
+
+ 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;
\ No newline at end of file