This commit is contained in:
jeremygan2021
2026-02-12 14:20:03 +08:00
parent ba78470052
commit f00cc9a28e
14 changed files with 553 additions and 3 deletions

View File

@@ -0,0 +1,205 @@
import React, { useState, useEffect } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import { Typography, Card, Avatar, Tag, Space, Button, Divider, Input, message, Breadcrumb, Tooltip } from 'antd';
import { UserOutlined, ClockCircleOutlined, EyeOutlined, CheckCircleFilled, LeftOutlined, LikeOutlined } from '@ant-design/icons';
import { getTopicDetail, createReply } from '../api';
import { useAuth } from '../context/AuthContext';
import LoginModal from '../components/LoginModal';
const { Title, Text, Paragraph } = Typography;
const { TextArea } = Input;
const ForumDetail = () => {
const { id } = useParams();
const navigate = useNavigate();
const { user } = useAuth();
const [loading, setLoading] = useState(true);
const [topic, setTopic] = useState(null);
const [replyContent, setReplyContent] = useState('');
const [submitting, setSubmitting] = useState(false);
const [loginModalVisible, setLoginModalVisible] = useState(false);
const fetchTopic = async () => {
try {
const res = await getTopicDetail(id);
setTopic(res.data);
} catch (error) {
console.error(error);
message.error('加载失败');
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchTopic();
}, [id]);
const handleSubmitReply = async () => {
if (!user) {
setLoginModalVisible(true);
return;
}
if (!replyContent.trim()) {
message.warning('请输入回复内容');
return;
}
setSubmitting(true);
try {
await createReply({
topic: id,
content: replyContent
});
message.success('回复成功');
setReplyContent('');
fetchTopic(); // Refresh to show new reply
} catch (error) {
console.error(error);
message.error('回复失败');
} finally {
setSubmitting(false);
}
};
if (loading) return <div style={{ padding: 100, textAlign: 'center', color: '#fff' }}>Loading...</div>;
if (!topic) return <div style={{ padding: 100, textAlign: 'center', color: '#fff' }}>Topic not found</div>;
return (
<div style={{ padding: '40px 20px', minHeight: '100vh', maxWidth: 1000, margin: '0 auto' }}>
<Button
type="text"
icon={<LeftOutlined />}
style={{ color: '#fff', marginBottom: 20 }}
onClick={() => navigate('/forum')}
>
返回列表
</Button>
{/* Topic Content */}
<Card
style={{
background: 'rgba(20,20,20,0.8)',
border: '1px solid rgba(255,255,255,0.1)',
backdropFilter: 'blur(10px)',
marginBottom: 30
}}
bodyStyle={{ padding: '30px' }}
>
<div style={{ marginBottom: 20 }}>
{topic.is_pinned && <Tag color="red" style={{ marginRight: 10 }}>置顶</Tag>}
{topic.product_info && <Tag color="blue">{topic.product_info.name}</Tag>}
<Title level={2} style={{ color: '#fff', margin: '10px 0' }}>{topic.title}</Title>
<Space size="large" style={{ color: '#888', marginTop: 10 }}>
<Space>
<Avatar src={topic.author_info?.avatar_url} icon={<UserOutlined />} />
<span style={{ color: '#ccc' }}>{topic.author_info?.nickname}</span>
{topic.is_verified_owner && (
<Tooltip title="已验证购买过相关产品">
<CheckCircleFilled style={{ color: '#00b96b' }} />
</Tooltip>
)}
</Space>
<Space>
<ClockCircleOutlined />
<span>{new Date(topic.created_at).toLocaleString()}</span>
</Space>
<Space>
<EyeOutlined />
<span>{topic.view_count} 阅读</span>
</Space>
</Space>
</div>
<Divider style={{ borderColor: 'rgba(255,255,255,0.1)' }} />
<div style={{
color: '#ddd',
fontSize: 16,
lineHeight: 1.8,
minHeight: 200,
whiteSpace: 'pre-wrap' // Preserve formatting
}}>
{topic.content}
</div>
</Card>
{/* Replies List */}
<div style={{ marginBottom: 30 }}>
<Title level={4} style={{ color: '#fff', marginBottom: 20 }}>
{topic.replies?.length || 0} 条回复
</Title>
{topic.replies?.map((reply, index) => (
<Card
key={reply.id}
style={{
background: 'rgba(255,255,255,0.05)',
border: 'none',
marginBottom: 16,
borderRadius: 8
}}
>
<div style={{ display: 'flex', gap: 16 }}>
<Avatar src={reply.author_info?.avatar_url} icon={<UserOutlined />} />
<div style={{ flex: 1 }}>
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 8 }}>
<Space>
<Text style={{ color: '#aaa', fontWeight: 'bold' }}>{reply.author_info?.nickname}</Text>
<Text style={{ color: '#666', fontSize: 12 }}>{new Date(reply.created_at).toLocaleString()}</Text>
</Space>
<Text style={{ color: '#444' }}>#{index + 1}</Text>
</div>
<div style={{ color: '#eee', whiteSpace: 'pre-wrap' }}>{reply.content}</div>
</div>
</div>
</Card>
))}
</div>
{/* Reply Form */}
<Card
style={{
background: 'rgba(20,20,20,0.8)',
border: '1px solid rgba(255,255,255,0.1)'
}}
>
<Title level={5} style={{ color: '#fff', marginBottom: 16 }}>发表回复</Title>
{user ? (
<>
<TextArea
rows={4}
value={replyContent}
onChange={e => setReplyContent(e.target.value)}
placeholder="友善回复,分享你的见解..."
style={{ marginBottom: 16, background: '#111', border: '1px solid #333', color: '#fff' }}
/>
<div style={{ textAlign: 'right' }}>
<Button type="primary" onClick={handleSubmitReply} loading={submitting}>
提交回复
</Button>
</div>
</>
) : (
<div style={{ textAlign: 'center', padding: 20 }}>
<Text style={{ color: '#888' }}>登录后参与讨论</Text>
<br/>
<Button type="primary" style={{ marginTop: 10 }} onClick={() => setLoginModalVisible(true)}>
立即登录
</Button>
</div>
)}
</Card>
<LoginModal
visible={loginModalVisible}
onClose={() => setLoginModalVisible(false)}
onLoginSuccess={() => {}}
/>
</div>
);
};
export default ForumDetail;