前段

Change-Id: I718d4d07ea03c6d2b6bcbd4d426c5d1af2201bf4
diff --git a/src/components/TorrentDetail.jsx b/src/components/TorrentDetail.jsx
new file mode 100644
index 0000000..e743506
--- /dev/null
+++ b/src/components/TorrentDetail.jsx
@@ -0,0 +1,348 @@
+import React, { useState, useEffect } from 'react';

+import { useParams, useNavigate, useLocation } from 'react-router-dom';

+import { 

+  getTorrentDetail, 

+  likeTorrent, 

+  addTorrentComment 

+} from '../api/torrent';

+import { 

+  likeTorrentComment, 

+  addCommentReply 

+} from '../api/torrentComment';

+import './TorrentDetail.css';

+

+

+const TorrentDetail = ({ onLogout }) => {

+  const { id } = useParams();

+  const navigate = useNavigate();

+  const location = useLocation();

+  const [torrent, setTorrent] = useState(null);

+  const [comments, setComments] = useState([]);

+  const [newComment, setNewComment] = useState('');

+  const [setReplyingTo] = useState(null);

+  const [replyContent, setReplyContent] = useState('');

+  const [loading, setLoading] = useState(true);

+  const [error, setError] = useState('');

+  const [activeReplyId, setActiveReplyId] = useState(null);

+  const [replyModal, setReplyModal] = useState({

+    visible: false,

+    replyingTo: null,

+    replyingToUsername: '',

+    isReply: false

+  });

+

+  // 确保openReplyModal接收username参数

+const openReplyModal = (commentId, username) => {

+  setReplyModal({

+    visible: true,

+    replyingTo: commentId,

+    replyingToUsername: username,  // 确保这里接收username

+    isReply: false

+  });

+};

+

+  // 关闭回复弹窗

+  const closeReplyModal = () => {

+    setReplyModal({

+      visible: false,

+      replyingTo: null,

+      replyingToUsername: '',

+      isReply: false

+    });

+    setReplyContent('');

+  };

+

+  const Comment = ({ comment, onLike, onReply, isReply = false }) => {

+    return (

+      <div className={`comment-container ${isReply ? "is-reply" : ""}`}>

+        <div className="comment-item">

+          <div className="comment-avatar">

+            {(comment.authorId || "?").charAt(0)} {/* 修复点 */}

+          </div>

+          <div className="comment-content">

+            <div className="comment-header">

+              <span className="comment-user">{comment.authorId || "匿名用户"}</span>

+              {comment.replyTo && (

+                <span className="reply-to">回复 @{comment.replyTo}</span>

+              )}

+              <span className="comment-time">

+                {new Date(comment.createTime).toLocaleString()}

+              </span>

+            </div>

+            <p className="comment-text">{comment.content}</p>

+            <div className="comment-actions">

+              <button onClick={() => onLike(comment.id)}>

+                👍 ({comment.likeCount || 0})

+              </button>

+              <button onClick={() => onReply(comment.id, comment.authorId)}>

+                回复

+              </button>

+            </div>

+          </div>

+        </div>

+      </div>

+    );

+  };

+

+    // 递归渲染评论组件

+  const renderComment = (comment, depth = 0) => {

+    return (

+      <div key={comment.id} style={{ marginLeft: `${depth * 30}px` }}>

+        <Comment

+          comment={comment}

+          onLike={handleLikeComment}

+          onReply={openReplyModal}

+          isReply={depth > 0}

+        />

+        

+        {/* 递归渲染所有回复 */}

+        {comment.replies && comment.replies.map(reply => 

+          renderComment(reply, depth + 1)

+        )}

+      </div>

+    );

+  };

+    

+

+

+  const fetchTorrentDetail = async () => {

+    try {

+      setLoading(true);

+      const response = await getTorrentDetail(id);

+      console.log('API Response:', JSON.parse(JSON.stringify(response.data.data.comments))); // 深度拷贝避免Proxy影响

+      setTorrent(response.data.data.torrent);

+      setComments(response.data.data.comments);

+    } catch (err) {

+      setError(err.response?.data?.message || '获取种子详情失败');

+    } finally {

+      setLoading(false);

+    }

+  };

+

+  useEffect(() => {

+    fetchTorrentDetail();

+  }, [id]);

+

+  // 点赞种子

+  const handleLikeTorrent = async () => {

+    try {

+      await likeTorrent(id);

+      setTorrent(prev => ({

+        ...prev,

+        likeCount: prev.likeCount + 1

+      }));

+    } catch (err) {

+      setError('点赞失败: ' + (err.response?.data?.message || err.message));

+    }

+  };

+

+  const handleCommentSubmit = async (e) => {

+    e.preventDefault();

+    if (!newComment.trim()) return;

+    

+    try {

+      const username = localStorage.getItem('username');

+      const response = await addTorrentComment(id, {

+        content: newComment,

+        authorId: username

+      });

+  

+      // 修改这里的响应处理逻辑

+      if (response.data && response.data.code === 200) {

+        await fetchTorrentDetail();

+        

+        setNewComment('');

+      } else {

+        setError(response.data.message || '评论失败');

+      }

+    } catch (err) {

+      setError('评论失败: ' + (err.response?.data?.message || err.message));

+    }

+  };

+  

+  

+  const handleLikeComment = async (commentId) => {

+    try {

+      await likeTorrentComment(commentId);

+      

+      // 递归更新评论点赞数

+      const updateComments = (comments) => {

+        return comments.map(comment => {

+          // 当前评论匹配

+          if (comment.id === commentId) {

+            return { ...comment, likeCount: comment.likeCount + 1 };

+          }

+          

+          // 递归处理回复

+          if (comment.replies && comment.replies.length > 0) {

+            return {

+              ...comment,

+              replies: updateComments(comment.replies)

+            };

+          }

+          

+          return comment;

+        });

+      };

+  

+      setComments(prev => updateComments(prev));

+    } catch (err) {

+      setError('点赞失败: ' + (err.response?.data?.message || err.message));

+    }

+  };

+  // 修改startReply函数

+  const startReply = (commentId) => {

+    if (activeReplyId === commentId) {

+      // 如果点击的是已经激活的回复按钮,则关闭

+      setActiveReplyId(null);

+      setReplyingTo(null);

+    } else {

+      // 否则打开新的回复框

+      setActiveReplyId(commentId);

+      setReplyingTo(commentId);

+    }

+  };

+

+  const handleReplySubmit = async (e) => {

+    e.preventDefault();

+    if (!replyContent.trim()) return;

+  

+    try {

+      const username = localStorage.getItem('username');

+      const response = await addCommentReply(replyModal.replyingTo, {

+        content: replyContent,

+        authorId: username

+      });

+  

+      console.log('回复响应:', response.data); // 调试

+      

+      if (response.data && response.data.code === 200) {

+        await fetchTorrentDetail();

+  

+        closeReplyModal();

+      }

+    } catch (err) {

+      console.error('回复错误:', err);

+      setError('回复失败: ' + (err.response?.data?.message || err.message));

+    }

+  };

+

+

+  // 返回按钮

+  const handleBack = () => {

+    const fromTab = location.state?.fromTab || 'share';

+    navigate(`/dashboard/${fromTab}`);

+  };

+

+  if (loading) return <div className="loading">加载中...</div>;

+  if (error) return <div className="error">{error}</div>;

+  if (!torrent) return <div className="error">种子不存在</div>;

+

+  return (

+    <div className="torrent-detail-container">

+      <button className="back-button" onClick={handleBack}>

+        &larr; 返回资源区

+      </button>

+      

+      <div className="torrent-main">

+        <div className="torrent-cover">

+          <div className="cover-placeholder">

+            {torrent.torrentName.charAt(0)}

+          </div>

+        </div>

+        

+        <div className="torrent-info">

+          <h1 className="torrent-title">{torrent.torrentName}</h1>

+          

+          <div className="uploader-info">

+            <div className="uploader-avatar">

+              {torrent.username.charAt(0)}

+            </div>

+            <div className="uploader-details">

+              <span className="uploader-name">{torrent.username}</span>

+              <span className="upload-time">

+                {new Date(torrent.createTime).toLocaleString()}

+              </span>

+            </div>

+          </div>

+          

+          <div className="torrent-meta">

+            <p><strong>类型:</strong> {torrent.category}</p>

+            <p><strong>地区:</strong> {torrent.region}</p>

+            <p><strong>分辨率:</strong> {torrent.resolution}</p>

+            <p><strong>字幕:</strong> {torrent.subtitle}</p>

+            {torrent.size && <p><strong>大小:</strong> {torrent.size}</p>}

+          </div>

+          

+          <div className="torrent-description">

+            <h3>资源描述</h3>

+            <p>{torrent.description}</p>

+          </div>

+          

+          <div className="interaction-buttons">

+            <button 

+              className="like-button"

+              onClick={handleLikeTorrent}

+            >

+              <span>👍 点赞 ({torrent.likeCount})</span>

+            </button>

+            <button className="download-button">

+              <span>⬇️ 立即下载</span>

+            </button>

+          </div>

+        </div>

+      </div>

+      

+      <div className="comments-section">

+        <h2>评论 ({torrent.replyCount})</h2>

+        

+        <form onSubmit={handleCommentSubmit} className="comment-form">

+          <textarea

+            value={newComment}

+            onChange={(e) => setNewComment(e.target.value)}

+            placeholder="写下你的评论..."

+            rows="3"

+            required

+          />

+          <button type="submit">发表评论</button>

+        </form>

+        

+        <div className="comment-list">

+          {comments.map(comment => renderComment(comment))}

+        </div>

+

+        {replyModal.visible && (

+          <div className="reply-modal-overlay">

+            <div className="reply-modal">

+              <div className="modal-header">

+                <h3>回复 @{replyModal.replyingToUsername}</h3>

+                <button onClick={closeReplyModal} className="close-modal">&times;</button>

+              </div>

+              <form onSubmit={handleReplySubmit}>

+                <textarea

+                  value={replyContent}

+                  onChange={(e) => setReplyContent(e.target.value)}

+                  placeholder={`回复 @${replyModal.replyingToUsername}...`}

+                  rows="5"

+                  autoFocus

+                  required

+                />

+                <div className="modal-actions">

+                  <button type="button" onClick={closeReplyModal} className="cancel-btn">

+                    取消

+                  </button>

+                  <button type="submit" className="submit-btn">

+                    发送回复

+                  </button>

+                </div>

+              </form>

+            </div>

+          </div>

+        )}

+

+      </div>

+    </div>

+  );

+};

+

+export default TorrentDetail;
\ No newline at end of file