fix: 论坛页面优化 & 新增修改帖子
This commit is contained in:
parent
279e5d81f7
commit
9a7b889ada
@ -1,4 +1,4 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { useParams } from '@umijs/max';
|
import { useParams } from '@umijs/max';
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
@ -19,44 +19,90 @@ import {
|
|||||||
StarFilled,
|
StarFilled,
|
||||||
ShareAltOutlined,
|
ShareAltOutlined,
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
|
import { getPostVoByIdUsingGet } from '@/services/hebi/postController';
|
||||||
|
import { doThumbUsingPost } from '@/services/hebi/postThumbController';
|
||||||
|
import { doPostFavourUsingPost } from '@/services/hebi/postFavourController';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import MDEditor from '@uiw/react-md-editor';
|
||||||
|
import '@uiw/react-md-editor/markdown-editor.css';
|
||||||
|
import '@uiw/react-markdown-preview/markdown.css';
|
||||||
|
|
||||||
const { Title, Paragraph } = Typography;
|
const { Title } = Typography;
|
||||||
const { TextArea } = Input;
|
const { TextArea } = Input;
|
||||||
|
|
||||||
// Mock comment data
|
|
||||||
const mockComments = [
|
|
||||||
{
|
import { useModel } from '@umijs/max'; // 新增
|
||||||
id: 1,
|
|
||||||
author: '张三',
|
|
||||||
avatar: 'https://joeschmoe.io/api/v1/random',
|
|
||||||
content: '这个帖子很有帮助,感谢分享!',
|
|
||||||
createTime: '2024-03-20 10:00',
|
|
||||||
likes: 5,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
author: '李四',
|
|
||||||
avatar: 'https://joeschmoe.io/api/v1/random',
|
|
||||||
content: '我也有一些补充,大家可以参考一下...',
|
|
||||||
createTime: '2024-03-20 11:30',
|
|
||||||
likes: 3,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
author: '王五',
|
|
||||||
avatar: 'https://joeschmoe.io/api/v1/random',
|
|
||||||
content: '学到了很多新知识,期待更多分享!',
|
|
||||||
createTime: '2024-03-20 14:15',
|
|
||||||
likes: 2,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const ForumDetail: React.FC = () => {
|
const ForumDetail: React.FC = () => {
|
||||||
|
const { initialState } = useModel('@@initialState'); // 获取当前登录用户信息
|
||||||
|
const currentUser = initialState?.currentUser;
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
const [liked, setLiked] = useState(false);
|
const [liked, setLiked] = useState(false);
|
||||||
const [collected, setCollected] = useState(false);
|
const [collected, setCollected] = useState(false);
|
||||||
const [comment, setComment] = useState('');
|
const [comment, setComment] = useState('');
|
||||||
const [comments, setComments] = useState(mockComments);
|
// mock 评论数据
|
||||||
|
const [comments, setComments] = useState([
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
author: '小明',
|
||||||
|
avatar: 'https://joeschmoe.io/api/v1/1',
|
||||||
|
content: '很棒的分享,受益匪浅!',
|
||||||
|
createTime: '2025-05-15 10:00:00',
|
||||||
|
likes: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
author: 'AI助手',
|
||||||
|
avatar: 'https://joeschmoe.io/api/v1/2',
|
||||||
|
content: '请问有完整代码示例吗?',
|
||||||
|
createTime: '2025-05-15 11:20:00',
|
||||||
|
likes: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
author: '匿名用户',
|
||||||
|
avatar: 'https://joeschmoe.io/api/v1/random',
|
||||||
|
content: '期待更多相关内容!',
|
||||||
|
createTime: '2025-05-15 12:45:00',
|
||||||
|
likes: 0,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 帖子详情状态
|
||||||
|
const [post, setPost] = useState<any>(null);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
|
// 拉取帖子详情
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchDetail = async () => {
|
||||||
|
setLoading(true);
|
||||||
|
const res = await getPostVoByIdUsingGet({ id });
|
||||||
|
console.log('res', res);
|
||||||
|
if (res && res.code === 0 && res.data) {
|
||||||
|
setPost(res.data);
|
||||||
|
setLiked(!!res.data.hasThumb);
|
||||||
|
setCollected(!!res.data.hasFavour);
|
||||||
|
}
|
||||||
|
setLoading(false);
|
||||||
|
};
|
||||||
|
if (id) fetchDetail();
|
||||||
|
}, [id]);
|
||||||
|
|
||||||
|
// 点赞处理
|
||||||
|
const handleLike = async () => {
|
||||||
|
try {
|
||||||
|
const res = await doThumbUsingPost({ postId: id }); // 直接传字符串 id
|
||||||
|
if (res && res.code === 0) {
|
||||||
|
setLiked(!liked);
|
||||||
|
message.success(liked ? '已取消点赞' : '点赞成功');
|
||||||
|
} else {
|
||||||
|
message.error(res?.message || '操作失败');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
message.error('操作失败');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleSubmitComment = () => {
|
const handleSubmitComment = () => {
|
||||||
if (!comment.trim()) {
|
if (!comment.trim()) {
|
||||||
@ -77,46 +123,80 @@ const ForumDetail: React.FC = () => {
|
|||||||
setComment('');
|
setComment('');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 收藏处理
|
||||||
|
const handleFavour = async () => {
|
||||||
|
try {
|
||||||
|
const res = await doPostFavourUsingPost({ postId: id });
|
||||||
|
if (res && res.code === 0) {
|
||||||
|
setCollected(!collected);
|
||||||
|
message.success(collected ? '已取消收藏' : '收藏成功');
|
||||||
|
} else {
|
||||||
|
message.error(res?.message || '操作失败');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
message.error('操作失败');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 判断是否是当前用户的帖子
|
||||||
|
const isMyPost = currentUser?.id === post?.userId;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card loading={loading}>
|
||||||
<article>
|
<article>
|
||||||
<header style={{ marginBottom: 24 }}>
|
<header style={{ marginBottom: 24 }}>
|
||||||
<Title level={2}>帖子标题</Title>
|
<Title level={2}>{post?.title || '帖子标题'}</Title>
|
||||||
<Space split={<Divider type="vertical" />}>
|
<Space split={<Divider type="vertical" />}>
|
||||||
<Space>
|
<Space>
|
||||||
<Avatar src="https://joeschmoe.io/api/v1/random" />
|
<Avatar src={post?.user?.userAvatar || 'https://joeschmoe.io/api/v1/random'} />
|
||||||
<span>作者名称</span>
|
<span>{post?.user?.userName || '无'}</span>
|
||||||
</Space>
|
</Space>
|
||||||
<span>发布时间</span>
|
<span>
|
||||||
<Tag color="blue">分类</Tag>
|
{post?.createTime ? dayjs(post.createTime).format('YYYY-MM-DD HH:mm:ss') : '发布时间'}
|
||||||
<span>阅读 1000</span>
|
</span>
|
||||||
|
{post?.tagList?.map((tag: string) => (
|
||||||
|
<Tag color="blue" key={tag}>{tag}</Tag>
|
||||||
|
))}
|
||||||
|
|
||||||
</Space>
|
</Space>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<Paragraph>
|
{/* Markdown 渲染帖子内容 */}
|
||||||
帖子内容
|
<div data-color-mode="light" style={{ background: '#fff' }}>
|
||||||
</Paragraph>
|
<MDEditor.Markdown source={post?.content || '帖子内容'} />
|
||||||
|
</div>
|
||||||
|
|
||||||
<div style={{ marginTop: 24 }}>
|
<div style={{ marginTop: 24 }}>
|
||||||
<Space size="large">
|
<Space size="large">
|
||||||
<Button
|
<Button
|
||||||
icon={liked ? <LikeFilled /> : <LikeOutlined />}
|
icon={liked ? <LikeFilled style={{ color: '#ff4d4f' }} /> : <LikeOutlined />}
|
||||||
onClick={() => setLiked(!liked)}
|
onClick={handleLike}
|
||||||
|
style={liked ? { color: '#ff4d4f', borderColor: '#ff4d4f', background: '#fff0f0' } : {}}
|
||||||
>
|
>
|
||||||
点赞
|
点赞 {post?.thumbNum ?? 0}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
icon={collected ? <StarFilled /> : <StarOutlined />}
|
icon={collected ? <StarFilled style={{ color: '#ff4d4f' }} /> : <StarOutlined />}
|
||||||
onClick={() => setCollected(!collected)}
|
onClick={handleFavour}
|
||||||
|
style={collected ? { color: '#ff4d4f', borderColor: '#ff4d4f', background: '#fff0f0' } : {}}
|
||||||
>
|
>
|
||||||
收藏
|
收藏 {post?.favourNum ?? 0}
|
||||||
</Button>
|
</Button>
|
||||||
|
{isMyPost && (
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
onClick={() => {
|
||||||
|
window.location.href = `/forum/publish?id=${id}`; // 使用 window.location.href 进行跳转
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
修改
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
<Button icon={<ShareAltOutlined />}>
|
<Button icon={<ShareAltOutlined />}>
|
||||||
分享
|
分享
|
||||||
</Button>
|
</Button>
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
@ -99,9 +99,9 @@ const ForumList: React.FC = () => {
|
|||||||
className={styles.postItem}
|
className={styles.postItem}
|
||||||
actions={[
|
actions={[
|
||||||
<Space key={"actions"} className={styles.postStats}>
|
<Space key={"actions"} className={styles.postStats}>
|
||||||
<span key="views" className={styles.statItem}>
|
{/* <span key="views" className={styles.statItem}>
|
||||||
<EyeOutlined /> {item.viewNum || 0}
|
<EyeOutlined /> {item.viewNum || 0}
|
||||||
</span>
|
</span> */}
|
||||||
<span key="comments" className={styles.statItem}>
|
<span key="comments" className={styles.statItem}>
|
||||||
<MessageOutlined /> {item.commentNum || 0}
|
<MessageOutlined /> {item.commentNum || 0}
|
||||||
</span>
|
</span>
|
||||||
@ -158,7 +158,13 @@ const ForumList: React.FC = () => {
|
|||||||
</Space>
|
</Space>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<div className={styles.postContent}>
|
<div className={styles.postContent} style={{
|
||||||
|
display: '-webkit-box',
|
||||||
|
WebkitBoxOrient: 'vertical',
|
||||||
|
WebkitLineClamp: 5,
|
||||||
|
overflow: 'hidden',
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
}}>
|
||||||
{item.content}
|
{item.content}
|
||||||
</div>
|
</div>
|
||||||
</List.Item>
|
</List.Item>
|
||||||
|
@ -1,43 +1,66 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState,useEffect } from 'react';
|
||||||
import { Card, Form, Input, Button, Select, message, Alert } from 'antd';
|
import { Card, Form, Input, Button, Select, message, Alert } from 'antd';
|
||||||
import { history } from '@umijs/max';
|
import { history } from '@umijs/max';
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
import MDEditor from '@uiw/react-md-editor'; // 直接引入,不用 next/dynamic
|
import MDEditor from '@uiw/react-md-editor'; // 直接引入,不用 next/dynamic
|
||||||
import '@uiw/react-md-editor/markdown-editor.css';
|
import '@uiw/react-md-editor/markdown-editor.css';
|
||||||
import '@uiw/react-markdown-preview/markdown.css';
|
import '@uiw/react-markdown-preview/markdown.css';
|
||||||
import { addPostUsingPost } from '@/services/hebi/postController';
|
import { addPostUsingPost, updatePostUsingPost} from '@/services/hebi/postController';
|
||||||
|
import { useSearchParams } from '@umijs/max'; // 新增
|
||||||
|
import { getPostVoByIdUsingGet } from '@/services/hebi/postController'; // 新增
|
||||||
|
|
||||||
const { TextArea } = Input;
|
const { TextArea } = Input;
|
||||||
|
|
||||||
const ForumPublish: React.FC = () => {
|
const ForumPublish: React.FC = () => {
|
||||||
|
const [searchParams] = useSearchParams();
|
||||||
|
const id = searchParams.get('id'); // 获取帖子 ID
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const [submitting, setSubmitting] = useState(false);
|
const [submitting, setSubmitting] = useState(false);
|
||||||
const [mdContent, setMdContent] = useState<string>(''); // 新增
|
const [mdContent, setMdContent] = useState<string>('');
|
||||||
|
|
||||||
|
// 如果是编辑模式,加载帖子内容
|
||||||
|
useEffect(() => {
|
||||||
|
const loadPost = async () => {
|
||||||
|
if (id) {
|
||||||
|
const res = await getPostVoByIdUsingGet({ id });
|
||||||
|
if (res?.code === 0 && res.data) {
|
||||||
|
form.setFieldsValue({
|
||||||
|
title: res.data.title,
|
||||||
|
tags: res.data.tagList,
|
||||||
|
});
|
||||||
|
|
||||||
|
setMdContent(res.data.content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
loadPost();
|
||||||
|
}, [id, form]);
|
||||||
|
|
||||||
const handleSubmit = async (values: any) => {
|
const handleSubmit = async (values: any) => {
|
||||||
setSubmitting(true);
|
setSubmitting(true);
|
||||||
try {
|
try {
|
||||||
const postAddRequest = {
|
const postAddRequest = {
|
||||||
|
id: id || undefined,
|
||||||
title: values.title,
|
title: values.title,
|
||||||
content: mdContent, // 使用 Markdown 内容
|
content: mdContent,
|
||||||
tags: values.tags || [],
|
tags: values.tags || [],
|
||||||
};
|
};
|
||||||
const res = await addPostUsingPost(postAddRequest);
|
const res = id ? await updatePostUsingPost(postAddRequest as any): await addPostUsingPost(postAddRequest);
|
||||||
if (res && res.code === 0) {
|
if (res && res.code === 0) {
|
||||||
message.success('发布成功');
|
message.success(id ? '修改成功' : '发布成功');
|
||||||
history.push('/forum/list');
|
history.push('/forum/list');
|
||||||
} else {
|
} else {
|
||||||
message.error(res?.message || '发布失败');
|
message.error(res?.message || (id ? '修改失败' : '发布失败'));
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
message.error('发布失败');
|
message.error(id ? '修改失败' : '发布失败');
|
||||||
}
|
}
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.publishContainer}>
|
<div className={styles.publishContainer}>
|
||||||
<Card title="发布帖子" className={styles.publishCard}>
|
<Card title={id ? '修改帖子' : '发布帖子'} className={styles.publishCard}>
|
||||||
<Alert
|
<Alert
|
||||||
message="发帖提示"
|
message="发帖提示"
|
||||||
description="请确保发布的内容与 AIGC 数据分析相关,并遵守社区规范。"
|
description="请确保发布的内容与 AIGC 数据分析相关,并遵守社区规范。"
|
||||||
@ -99,7 +122,7 @@ const ForumPublish: React.FC = () => {
|
|||||||
取消
|
取消
|
||||||
</Button>
|
</Button>
|
||||||
<Button type="primary" htmlType="submit" loading={submitting}>
|
<Button type="primary" htmlType="submit" loading={submitting}>
|
||||||
发布帖子
|
{id ? '保存修改' : '发布帖子'}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</Form>
|
</Form>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user