帖子的相关用户前端与管理员后端
Change-Id: I957181738817aeeaa89fabe23ceaf59d00c37a71
diff --git a/src/components/PostAdminPanel.jsx b/src/components/PostAdminPanel.jsx
new file mode 100644
index 0000000..a01df08
--- /dev/null
+++ b/src/components/PostAdminPanel.jsx
@@ -0,0 +1,232 @@
+import React, { useEffect, useState } from 'react';
+import {
+ Table,
+ Button,
+ Modal,
+ Image,
+ message,
+ Tag,
+ Space,
+ Input,
+ Tooltip,
+} from 'antd';
+import { ExclamationCircleOutlined, SearchOutlined } from '@ant-design/icons';
+import {
+ getAllPostsSorted,
+ searchPosts,
+ deletePost,
+ togglePinPost,
+} from '../api/post';
+
+const { confirm } = Modal;
+
+const PostAdminPanel = () => {
+ const [posts, setPosts] = useState([]);
+ const [loading, setLoading] = useState(false);
+ const [keyword, setKeyword] = useState('');
+
+ const fetchPosts = async () => {
+ setLoading(true);
+ try {
+ const data = keyword.trim()
+ ? await searchPosts(keyword.trim())
+ : await getAllPostsSorted();
+ setPosts(data);
+ } catch (error) {
+ message.error('获取帖子失败');
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ useEffect(() => {
+ fetchPosts();
+ }, []);
+
+ const handleSearch = () => {
+ fetchPosts();
+ };
+
+ const handleDelete = (postid) => {
+ confirm({
+ title: '确认删除该帖子吗?',
+ icon: <ExclamationCircleOutlined />,
+ okText: '删除',
+ okType: 'danger',
+ cancelText: '取消',
+ onOk: async () => {
+ try {
+ const res = await deletePost(postid);
+ if (res) {
+ message.success('删除成功');
+ fetchPosts();
+ } else {
+ message.error('删除失败');
+ }
+ } catch {
+ message.error('删除请求失败');
+ }
+ },
+ });
+ };
+
+ const handlePinToggle = async (postid) => {
+ try {
+ const res = await togglePinPost(postid);
+ if (res === true || res === false) {
+ // ✅ 用后端返回的状态更新 UI
+ const newPosts = posts.map(post => {
+ if (post.postid === postid) {
+ return {
+ ...post,
+ is_pinned: res ? 1 : 0,
+ };
+ }
+ return post;
+ });
+ setPosts(newPosts);
+ message.success(`帖子${res ? '已置顶' : '已取消置顶'}`);
+ } else {
+ message.error('更新失败');
+ }
+ } catch {
+ message.error('更新置顶状态失败');
+ }
+ };
+
+
+
+
+ const columns = [
+ {
+ title: 'ID',
+ dataIndex: 'postid',
+ key: 'postid',
+ width: 80,
+ fixed: 'left',
+ },
+ {
+ title: '用户ID',
+ dataIndex: 'userid',
+ key: 'userid',
+ width: 100,
+ },
+ {
+ title: '标题',
+ dataIndex: 'postTitle',
+ key: 'postTitle',
+ width: 200,
+ ellipsis: true,
+ },
+ {
+ title: '内容',
+ dataIndex: 'postContent',
+ key: 'postContent',
+ ellipsis: { showTitle: false },
+ render: (text) => (
+ <Tooltip placement="topLeft" title={text}>
+ {text}
+ </Tooltip>
+ ),
+ },
+ {
+ title: '标签',
+ dataIndex: 'tags',
+ key: 'tags',
+ render: (tags) =>
+ tags ? tags.split(',').map((tag) => <Tag key={tag}>{tag}</Tag>) : <Tag color="default">无</Tag>,
+ },
+ {
+ title: '图片',
+ dataIndex: 'photo',
+ key: 'photo',
+ width: 100,
+ render: (url) =>
+ url ? (
+ <Image
+ src={`http://localhost:8080${url}`}
+ width={80}
+ height={80}
+ style={{ objectFit: 'cover' }}
+ />
+ ) : (
+ <Tag color="default">无</Tag>
+ ),
+ },
+ {
+ title: '点赞数',
+ dataIndex: 'likes',
+ key: 'likes',
+ width: 100,
+ render: (likes) => <Tag color="blue">{likes}</Tag>,
+ },
+ {
+ title: '置顶状态',
+ dataIndex: 'is_pinned',
+ key: 'is_pinned',
+ width: 100,
+ render: (val) =>
+ val ? <Tag color="green">已置顶</Tag> : <Tag color="default">未置顶</Tag>,
+ },
+ {
+ title: '发布时间',
+ dataIndex: 'postCreatedTime',
+ key: 'postCreatedTime',
+ width: 180,
+ render: (time) =>
+ time ? new Date(time).toLocaleString() : <Tag color="default">暂无</Tag>,
+ },
+ {
+ title: '操作',
+ key: 'action',
+ width: 180,
+ fixed: 'right',
+ render: (_, record) => (
+ <Space size="middle">
+ <Button
+ type="primary"
+ onClick={() => handlePinToggle(record.postid, record.is_pinned)}
+ >
+ {Boolean(record.is_pinned) ? '取消置顶' : '置顶'}
+ </Button>
+
+ <Button danger onClick={() => handleDelete(record.postid)}>
+ 删除
+ </Button>
+ </Space>
+ ),
+ },
+ ];
+
+ return (
+ <div style={{ padding: 20 }}>
+ <h2 style={{ marginBottom: 20 }}>帖子管理面板</h2>
+ <Space style={{ marginBottom: 16 }}>
+ <Input
+ placeholder="请输入关键词"
+ value={keyword}
+ onChange={(e) => setKeyword(e.target.value)}
+ style={{ width: 300 }}
+ />
+ <Button type="primary" icon={<SearchOutlined />} onClick={handleSearch}>
+ 搜索
+ </Button>
+ <Button onClick={() => { setKeyword(''); fetchPosts(); }}>
+ 重置
+ </Button>
+ </Space>
+ <Table
+ rowKey="postid"
+ columns={columns}
+ dataSource={posts}
+ loading={loading}
+ scroll={{ x: 1600 }}
+ pagination={{ pageSize: 10 }}
+ bordered
+ size="middle"
+ />
+ </div>
+ );
+};
+
+export default PostAdminPanel;