blob: 6d278d58c7047bfcbfd1614e132889fe5b86e8a3 [file] [log] [blame] [edit]
import 'antd/dist/antd.css';
import React, { useState, useEffect, useMemo, useCallback } from 'react';
import { Layout, Tabs, Input, List, Card, Button, Tag, Spin, Typography, Divider } from 'antd';
import '../style/Admin.css';
import { fetchPosts, approvePost, rejectPost } from '../../../../Merge/front/src/api/posts';
export default function Admin() {
const ADMIN_USER_ID = 3;
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(true);
const [activeTab, setActiveTab] = useState('all');
const [selectedPost, setSelectedPost] = useState(null);
const [searchTerm, setSearchTerm] = useState('');
// 新增:拖拽相关状态
const [leftPanelWidth, setLeftPanelWidth] = useState(300);
const [isResizing, setIsResizing] = useState(false);
const statusColors = {
draft: 'orange',
pending: 'blue',
published: 'green',
deleted: 'gray',
rejected: 'red'
};
useEffect(() => {
async function load() {
const list = await fetchPosts(ADMIN_USER_ID)
setPosts(list)
setLoading(false)
}
load()
}, [])
// 过滤并排序
const sortedPosts = useMemo(() => {
return [...posts].sort((a, b) => {
if (a.status === 'pending' && b.status !== 'pending') return -1
if (b.status === 'pending' && a.status !== 'pending') return 1
return 0
})
}, [posts])
// 调整:根据 activeTab 及搜索关键词过滤
const filteredPosts = useMemo(() => {
let list
switch (activeTab) {
case 'pending':
list = sortedPosts.filter(p => p.status === 'pending'); break
case 'published':
list = sortedPosts.filter(p => p.status === 'published'); break
case 'rejected':
list = sortedPosts.filter(p => p.status === 'rejected'); break
default:
list = sortedPosts
}
return list.filter(p =>
p.title.toLowerCase().includes(searchTerm.toLowerCase())
)
}, [sortedPosts, activeTab, searchTerm])
const handleApprove = async id => {
await approvePost(id, ADMIN_USER_ID)
setPosts(ps => ps.map(x => x.id === id ? { ...x, status: 'published' } : x))
// 同步更新选中的帖子状态
if (selectedPost?.id === id) {
setSelectedPost(prev => ({ ...prev, status: 'published' }));
}
}
const handleReject = async id => {
await rejectPost(id, ADMIN_USER_ID)
setPosts(ps => ps.map(x => x.id === id ? { ...x, status: 'rejected' } : x))
// 同步更新选中的帖子状态
if (selectedPost?.id === id) {
setSelectedPost(prev => ({ ...prev, status: 'rejected' }));
}
}
const handleSelect = post => setSelectedPost(post)
// 修复:拖拽处理函数
const handleMouseMove = useCallback((e) => {
if (!isResizing) return;
const newWidth = e.clientX;
const minWidth = 200;
const maxWidth = window.innerWidth - 300;
if (newWidth >= minWidth && newWidth <= maxWidth) {
setLeftPanelWidth(newWidth);
}
}, [isResizing]);
const handleMouseUp = useCallback(() => {
setIsResizing(false);
document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('mouseup', handleMouseUp);
document.body.style.cursor = '';
document.body.style.userSelect = '';
}, [handleMouseMove]);
const handleMouseDown = useCallback((e) => {
e.preventDefault();
setIsResizing(true);
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', handleMouseUp);
document.body.style.cursor = 'col-resize';
document.body.style.userSelect = 'none';
}, [handleMouseMove, handleMouseUp]);
// 新增:组件卸载时清理事件监听器
useEffect(() => {
return () => {
document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('mouseup', handleMouseUp);
document.body.style.cursor = '';
document.body.style.userSelect = '';
};
}, [handleMouseMove, handleMouseUp]);
if (loading) return <Spin spinning tip="加载中…" style={{ width: '100%', marginTop: 100 }} />;
const { Content } = Layout;
const { TabPane } = Tabs;
const { Title, Text } = Typography;
return (
<div style={{ height: '100vh', display: 'flex' }}>
{/* 左侧面板 */}
<div
style={{
width: leftPanelWidth,
background: '#fff',
padding: 16,
borderRight: '1px solid #f0f0f0',
overflow: 'hidden'
}}
>
<div style={{ marginBottom: 24 }}>
<Title level={3}>小红书</Title>
<Input.Search
placeholder="搜索帖子标题..."
value={searchTerm}
onChange={e => setSearchTerm(e.target.value)}
enterButton
/>
</div>
<Tabs activeKey={activeTab} onChange={key => { setActiveTab(key); setSelectedPost(null); }}>
<TabPane tab="全部" key="all" />
<TabPane tab="待审核" key="pending" />
<TabPane tab="已通过" key="published" />
<TabPane tab="已驳回" key="rejected" />
</Tabs>
<div style={{ height: 'calc(100vh - 200px)', overflow: 'auto' }}>
<List
dataSource={filteredPosts}
pagination={{
pageSize: 5,
showSizeChanger: true,
pageSizeOptions: ['5','10','20'],
onChange: () => setSelectedPost(null)
}}
renderItem={p => (
<List.Item
key={p.id}
style={{
background: selectedPost?.id === p.id ? '#e6f7ff' : '',
cursor: 'pointer',
marginBottom: 8
}}
onClick={() => handleSelect(p)}
>
<List.Item.Meta
avatar={
p.thumbnail && (
<img
src={p.thumbnail}
alt=""
style={{ width: 64, height: 64, objectFit: 'cover' }}
/>
)
}
title={p.title}
description={`${p.createdAt} · ${p.author} · ${p.likes || 0}赞`}
/>
<Tag color={statusColors[p.status]}>{p.status}</Tag>
</List.Item>
)}
/>
</div>
</div>
{/* 拖拽分割条 */}
<div
style={{
width: 5,
cursor: 'col-resize',
background: isResizing ? '#1890ff' : '#f0f0f0',
transition: isResizing ? 'none' : 'background-color 0.2s',
position: 'relative',
flexShrink: 0
}}
onMouseDown={handleMouseDown}
onSelectStart={(e) => e.preventDefault()}
>
<div
style={{
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: 2,
height: 20,
background: '#999',
borderRadius: 1
}}
/>
</div>
{/* 右侧内容区域 */}
<div style={{ flex: 1, display: 'flex', flexDirection: 'column' }}>
<Content style={{ padding: 24, background: '#fff', overflow: 'auto' }}>
{selectedPost ? (
<Card
cover={selectedPost.image && <img alt="cover" src={selectedPost.image} />}
title={selectedPost.title}
extra={
<div>
{selectedPost.status === 'pending' && (
<>
<Button type="primary" onClick={() => handleApprove(selectedPost.id)}>通过</Button>
<Button danger onClick={() => handleReject(selectedPost.id)}>驳回</Button>
</>
)}
{selectedPost.status === 'published' && (
<Button danger onClick={() => handleReject(selectedPost.id)}>驳回</Button>
)}
{selectedPost.status === 'rejected' && (
<>
<Button onClick={() => {
setPosts(ps => ps.map(x => x.id === selectedPost.id ? { ...x, status: 'pending' } : x));
setSelectedPost(prev => ({ ...prev, status: 'pending' }));
}}>恢复待审</Button>
<Button onClick={() => {
setPosts(ps => ps.map(x => x.id === selectedPost.id ? { ...x, status: 'published' } : x));
setSelectedPost(prev => ({ ...prev, status: 'published' }));
}}>恢复已发</Button>
</>
)}
</div>
}
>
<Text type="secondary">
{`${selectedPost.createdAt} · ${selectedPost.author} · ${selectedPost.likes || 0}赞`}
</Text>
<Divider />
<p>{selectedPost.content}</p>
<Divider />
<Title level={4}>合规性指引</Title>
<ul>
<li>不含违法违规内容</li>
<li>不侵害他人合法权益</li>
</ul>
</Card>
) : (
<Text type="secondary">请选择左侧列表中的帖子查看详情</Text>
)}
</Content>
</div>
</div>
);
}