add resource detail and user part interface

Change-Id: I83541b5dc80bde465878cdccdbc77431b56dbff6
diff --git "a/src/app/resource/resource-detail/\133resourceId\135/page.tsx" "b/src/app/resource/resource-detail/\133resourceId\135/page.tsx"
index c48b626..8d28596 100644
--- "a/src/app/resource/resource-detail/\133resourceId\135/page.tsx"
+++ "b/src/app/resource/resource-detail/\133resourceId\135/page.tsx"
@@ -1,12 +1,905 @@
 'use client';
-import React from 'react';
+import React, {useEffect, useRef, useState} from 'react';
+import {Image} from "primereact/image";
+import {Button} from "primereact/button";
+import {Avatar} from "primereact/avatar";
+import {ButtonGroup} from "primereact/buttongroup";
+import {InputText} from "primereact/inputtext";
+import { Dialog } from 'primereact/dialog';
+// 引入图标
+import 'primeicons/primeicons.css';
+// 引入火苗图标
+import Fire from '@icon-park/react/lib/icons/Fire';
+import SmilingFace from '@icon-park/react/lib/icons/SmilingFace';
+// 消息提醒
+import { Toast } from 'primereact/toast';
+// 接口传输
+import axios from "axios";
+// 页面跳转
+import { useParams } from 'next/navigation'
+import { useRouter } from 'next/navigation';
+import Link from 'next/link';
+// 回复评论
+import { OverlayPanel } from 'primereact/overlaypanel';
+import { Sidebar } from 'primereact/sidebar';
+// 分页
+import { Paginator, PaginatorPageChangeEvent } from 'primereact/paginator';
 
-const EmptyPage: React.FC = () => {
-  return (
-    <div className="p-d-flex p-jc-center p-ai-center" style={{ height: '100vh' }}>
-      {"一个空页面"}
-    </div>
+// 样式
+import './resource-detail.scss';
+
+// 种子
+interface Torrent {
+  torrentRecordId:  number;
+  torrentUrl: string;
+  infoHash:  string;
+  uploadTime:  string;
+  uploaderUserId:  number;
+}
+
+// 资源版本
+interface ResourceVersion {
+  resourceVersionId:  string; // 资源版本id
+  resourceVersionName: string; // 资源版本名称
+  compatibleVersions: string[]; // 兼容的游戏版本列表
+  torrentList: Torrent[]; // 种子列表
+}
+
+// 资源信息
+interface Resource {
+  resourceId:  number;
+  resourceName: string;   // 资源标题
+  resourcePicture:  string;   // 资源照片网址
+  resourceSummary: string;    // 资源简介(一句话)
+  resourceDetail:  string;    // 资源介绍
+  uploadTime:  string;     // 上传时间
+  lastUpdateTime: string;     // 最近更新时间
+  price:  number;
+  downloads: number;  // 下载数
+  likes:  number;  // 点赞数
+  collections:   number;   // 收藏数
+  comments:  number;   // 评论数
+  seeds:   number;    // 种子数
+  classify:  string;  // 资源分类(材质包:resourcePack,模组:mod,整合包:modPack ,地图:map
+  hot: number;     // 资源热度
+  gameplayList: string[];  // 资源标签
+  resourceVersionList:  ResourceVersion[];     // 资源版本列表
+  isCollect:  boolean;     // 是否被收藏
+  isLike:  boolean;   // 是否被点赞
+  isPurchase:  boolean;   // 是否被购买
+  isUpload:  boolean;     // 是否是该用户上传的
+  userId: number;     // 资源上传者的id
+}
+
+// 评论信息
+interface Comment {
+  commentId: number;
+  userId: number | null;
+  replyId: number;
+  resourceId: number;
+  reawardId: number;
+  content: string;
+  createAt: string;
+}
+// 评论列表
+interface CommentList {
+  total: number;         // 评论总数
+  records: Comment[];      // 当前页评论数组
+}
+
+// 用户信息
+interface UserInfo {
+  userId: number;
+  username: string;
+  avatar: string;
+  signature: string;
+}
+// 新评论接口
+interface NewComment {
+  userId: number;
+  threadId: number;
+  resourceId: number;
+  rewardId: number;
+  replyId: number;
+  content: string;
+  createAt: string;
+}
+
+// 资源作者
+interface ResourceAuthor {
+  userId: number;
+  username: string;
+  avatar: string;
+  signature: string;
+}
+
+// 关注
+interface Subscriber {
+  userId: number;
+  username:  string;
+}
+
+// 关注列表
+interface SubscriberList {
+  userList: Subscriber[];
+}
+
+export default function ResourceDetail() {
+  // 获取URL参数
+  const params = useParams<{ resourceId: string }>();
+  const resourceId = decodeURIComponent(params.resourceId); // 防止中文路径乱码
+  // 页面跳转
+  const router = useRouter();
+
+  // 资源信息
+  const [resource, setResource] = useState<Resource | null>(null);
+  // 资源作者信息
+  const [resourceAuthor, setResourceAuthor] = useState<ResourceAuthor>();
+  // 资源作者id
+  const [resourceAuthorId, setResourceAuthorId] = useState<number>(0);
+  // 关注列表
+  const [subscriberList, setSubscriberList] = useState<SubscriberList>();
+  // 添加本地关注状态
+  const [isSubscribed, setIsSubscribed] = useState(false);
+  // 添加本地收藏状态
+  const [isCollected, setIsCollected] = useState(false);
+  const [collectionCount, setCollectionCount] = useState(0);
+  // 添加本地点赞状态
+  const [isLiked, setIsLiked] = useState(false);
+  const [likeCount, setLikeCount] = useState(0);
+  // 发帖人信息
+  const [userInfo, setUserInfo] = useState<UserInfo | null>(null);
+  // 评论人信息
+  const [commentUserInfos, setCommentUserInfos] = useState<Map<number, UserInfo>>(new Map());
+  //评论
+  const [comments, setComments] = useState<Comment[]>([]);
+  const [commentValue, setCommentValue] = useState<string>('');
+  const [totalComments, setTotalComments] = useState<number>(0);
+  // 回复
+  const [replyValue, setReplyValue] = useState<string>('');
+  const [visibleReply, setVisibleReply] = useState<boolean>(false);// 回复评论可视
+  // 评论选择框
+  const ops = useRef<OverlayPanel[]>([]);
+  // 购买弹窗状态
+  const [visible, setVisible] = useState<boolean>(false);
+  // 消息提醒
+  const toast = useRef<Toast>(null);
+
+  // 分页
+  const [first, setFirst] = useState<number>(0);
+  const [rows, setRows] = useState<number>(5);
+  const onPageChange = (event: PaginatorPageChangeEvent) => {
+    setFirst(event.first);
+    setRows(event.rows);
+  };
+
+  useEffect(() => {
+    fetchResourceInfo();
+  }, []);
+
+
+  // 获取资源信息
+  const fetchResourceInfo = async () => {
+    try {
+      // console.log(resourceId);
+      const response = await axios.get<Resource>(process.env.PUBLIC_URL +`/resource/info`, {
+        params: { resourceId: resourceId, userId: 22301010 }
+      });
+      console.log('获取资源信息:', response.data);
+      setResource(response.data);
+
+      // 初始化本地收藏状态
+      if (response.data) {
+        setIsCollected(response.data.isCollect);
+        setCollectionCount(response.data.collections);
+      }
+
+      setResourceAuthorId(response.data.userId);
+    } catch (err) {
+      console.error('获取资源信息失败', err);
+      toast.current?.show({ severity: 'error', summary: 'error', detail: "获取资源信息失败" });
+    }
+  };
+
+  // 获取到资源作者id时,获取资源作者信息
+  useEffect(() => {
+    fetchResourceAuthor();
+  }, [resourceAuthorId]);
+
+  // 获取资源作者信息
+  const fetchResourceAuthor = async () => {
+    try {
+      // console.log(resourceId);
+      // console.log(resourceAuthorId);
+      const response = await axios.get<ResourceAuthor>(process.env.PUBLIC_URL +`/user/info`, {
+        params: { userId: resourceAuthorId }
+      });
+      console.log('获取资源作者信息:', response.data);
+      setResourceAuthor(response.data);
+      // setResourceAuthorId(response.data.userId);
+    } catch (err) {
+      console.error('获取资源作者信息失败', err);
+      toast.current?.show({ severity: 'error', summary: 'error', detail: "获取资源作者信息失败" });
+    }
+  };
+
+  useEffect(() => {
+    fetchSubscriber();
+  }, []);
+
+  // 获取正在浏览资源的用户的关注列表
+  const fetchSubscriber = async () => {
+    try {
+      const response = await axios.get<SubscriberList>(process.env.PUBLIC_URL + `/user/subscriber`, {
+        params: {userId: 223010100}
+      });
+      console.log("关注列表:", response.data);
+      setSubscriberList(response.data);
+    } catch (err) {
+      console.error('获取浏览用户关注列表失败', err);
+      toast.current?.show({ severity: 'error', summary: 'error', detail: "获取浏览用户关注列表失败" });
+    }
+  }
+
+  useEffect(() => {
+    if (!resource?.userId || !subscriberList?.userList) return;
+
+    const authorId = resource.userId;
+    // 设置 isSubscribed 状态
+    const subscribed = subscriberList.userList.some(user => user.userId === authorId);
+    setIsSubscribed(subscribed);
+  }, [subscriberList, resource]);
+
+  // 若浏览用户与资源作者是同一人,则不显示关注按钮。若不是同一人,则显示按钮
+  const handleSubscribe = () => {
+    // 资源作者 ID
+    const authorId = resource?.userId;
+    // 当前登录用户 ID
+    const currentUserId = 223010100;
+
+    // 资源作者与浏览用户是同一人,不显示按钮
+    if (!authorId || authorId == currentUserId){
+      return null;
+    }
+
+    return isSubscribed ? (
+        // 如果已关注,显示“取消关注”按钮
+        <Button
+            label="取消关注"
+            onClick={async () => {
+              try {
+                const response = await axios.delete(
+                    process.env.PUBLIC_URL + '/user/subscription',
+                    {
+                      params: { userId: currentUserId, followerId: authorId }
+                    }
+                );
+
+                if (response.status === 200) {
+                  setIsSubscribed(false); // 🔥立刻更新按钮状态
+                  toast.current?.show({
+                    severity: 'success',
+                    summary: '取消成功',
+                    detail: '已取消关注该用户',
+                  });
+
+                  fetchSubscriber(); // 重新拉取完整关注列表
+                }
+              } catch (error) {
+                console.error('取消关注失败:', error);
+                toast.current?.show({
+                  severity: 'error',
+                  summary: '错误',
+                  detail: '取消关注失败',
+                });
+              }
+            }}
+        />
+    ) : (
+        // 若未关注,则显示关注按钮
+        <Button
+            label="关注"
+            onClick={async () => {
+              try {
+                const postData = {
+                  userId: currentUserId,
+                  followerId: authorId,
+                };
+                const response = await axios.post(
+                    process.env.PUBLIC_URL + '/user/subscription',
+                    postData
+                );
+
+                if (response.status === 200) {
+                  setIsSubscribed(true);  // 🔥立刻更新按钮状态
+                  toast.current?.show({
+                    severity: 'success',
+                    summary: '关注成功',
+                    detail: '已成功关注该用户',
+                  });
+
+                  fetchSubscriber(); // 刷新列表
+                }
+              } catch (error) {
+                console.error('关注失败:', error);
+                toast.current?.show({
+                  severity: 'error',
+                  summary: '错误',
+                  detail: '关注失败',
+                });
+              }
+            }}
+        />
+    );
+
+
+    // if (isSubscribed) {
+    //     return (
+    //         <Button
+    //             label="取消关注"
+    //             onClick={async () => {
+    //                 try {
+    //                     const response = await axios.delete(
+    //                         process.env.PUBLIC_URL + '/user/subscription', {
+    //                             params: {userId: currentUserId, followerId: authorId}
+    //                         }
+    //                     );
+    //
+    //                     if (response.status === 200) {
+    //                         toast.current?.show({
+    //                             severity: 'success',
+    //                             summary: '取消成功',
+    //                             detail: '已取消关注该用户',
+    //                         });
+    //                         // 重新拉取关注列表
+    //                         fetchSubscriber();
+    //                     }
+    //                 } catch (error) {
+    //                     console.error('取消关注失败:', error);
+    //                     toast.current?.show({
+    //                         severity: 'error',
+    //                         summary: '错误',
+    //                         detail: '取消关注失败',
+    //                     });
+    //                 }
+    //             }}
+    //         />
+    //     );
+    // } else {
+    //     // 未关注,显示“关注”按钮
+    //     return (
+    //         <Button
+    //             label="关注"
+    //             onClick={async () => {
+    //                 try {
+    //                     const postData = {
+    //                         userId: currentUserId,
+    //                         followerId: authorId,
+    //                     };
+    //                     const response = await axios.post(
+    //                         process.env.PUBLIC_URL + '/user/subscription',
+    //                         postData
+    //                     );
+    //
+    //                     if (response.status === 200) {
+    //                         toast.current?.show({
+    //                             severity: 'success',
+    //                             summary: '关注成功',
+    //                             detail: '已成功关注该用户',
+    //                         });
+    //                         // 重新拉取关注列表
+    //                         fetchSubscriber();
+    //                     }
+    //                 } catch (error) {
+    //                     console.error('关注失败:', error);
+    //                     toast.current?.show({
+    //                         severity: 'error',
+    //                         summary: '错误',
+    //                         detail: '关注失败',
+    //                     });
+    //                 }
+    //             }}
+    //         />
+    //     );
+    // }
+  }
+
+  // 判断该资源是否已被购买, 返回不同的购买按钮
+  const isPurchase = () => {
+    // 作者本人查看资源,不显示购买按钮
+    if (resource?.userId == 223010100) {
+      return ;
+    }
+
+    // 该资源已被购买
+    if (resource?.isPurchase) {
+      return (
+          <Button label="已购买" style={{width:"120px", height:"44px",
+            borderRadius:"20px 0 0 20px",}} disabled={true}/>
+      )
+    } else {
+      // 该资源未被购买
+      return (
+          <Button
+              label="立即购买"
+              style={{width:"120px", height:"44px",
+                borderRadius:"20px 0 0 20px",}}
+              onClick={() => setVisible(true)}
+          />
+      )
+    }
+  }
+
+  // 购买按钮接口
+  const handlePurchase = async () => {
+    try {
+      const postData = {
+        userId: 223010100, // 记得用户登录状态获取
+        resourceId: resource?.resourceId
+      };
+      // 发送POST请求
+      const response = await axios.post(process.env.PUBLIC_URL + '/resource/purchase', postData);
+
+      if (response.status === 200) {
+        toast.current?.show({ severity: 'success', summary: 'Success', detail: '成功购买资源' });
+        // 购买成功
+        setVisible(false);
+        // 刷新购买按钮
+        isPurchase();
+      } else if (response.status === 412) {
+        toast.current?.show({ severity: 'error', summary: 'error', detail: '积分不足,购买失败' });
+      }
+    } catch (error) {
+      console.error('购买资源失败:', error);
+      toast.current?.show({ severity: 'error', summary: 'error', detail: '购买资源失败' });
+    }
+  };
+
+  // 处理收藏操作
+  const handleCollection = async () => {
+    const newCollectionState = !isCollected;
+    const newCollectionCount = newCollectionState ? collectionCount + 1 : collectionCount - 1;
+
+    // 立即更新本地状态
+    setIsCollected(newCollectionState);
+    setCollectionCount(newCollectionCount);
+
+    try {
+      if (newCollectionState) {
+        // 收藏操作
+        await axios.post(process.env.PUBLIC_URL +`/resource/collection`, {
+          params: { resourceId: resourceId, userId: 22301010 }
+        });
+        console.log('收藏资源');
+      } else {
+        // 取消收藏操作
+        await axios.delete(process.env.PUBLIC_URL +`/resource/collection`, {
+          params: { resourceId: resourceId, userId: 22301010 }
+        });
+        console.log('取消收藏资源');
+      }
+    } catch (err) {
+      console.error(newCollectionState ? '收藏资源失败' : '取消收藏失败', err);
+      toast.current?.show({
+        severity: 'error',
+        summary: 'error',
+        detail: newCollectionState ? "收藏资源失败" : "取消收藏失败"
+      });
+
+      // 如果请求失败,回滚状态
+      setIsCollected(!newCollectionState);
+      setCollectionCount(newCollectionState ? collectionCount - 1 : collectionCount + 1);
+    }
+  };
+
+  // 处理点赞行为
+  const handleLike = async () => {
+    const newLikeState = !isLiked;
+    const newLikeCount = newLikeState ? likeCount + 1 : likeCount - 1;
+
+    // 立即更新本地状态
+    setIsLiked(newLikeState);
+    setLikeCount(newLikeCount);
+
+    try {
+      if (newLikeState) {
+        // 点赞操作
+        await axios.post(process.env.PUBLIC_URL +`/resource/like`, {
+          params: { resourceId: resourceId, userId: 22301010 }
+        });
+        console.log('点赞资源');
+      } else {
+        // 取消点赞操作
+        await axios.delete(process.env.PUBLIC_URL +`/resource/like`, {
+          params: { resourceId: resourceId, userId: 22301010 }
+        });
+        console.log('取消点赞资源');
+      }
+    } catch (err) {
+      console.error(newLikeState ? '点赞资源失败' : '取消点赞失败', err);
+      toast.current?.show({
+        severity: 'error',
+        summary: 'error',
+        detail: newLikeState ? "点赞资源失败" : "取消点赞失败"
+      });
+
+      // 如果请求失败,回滚状态
+      setIsLiked(!newLikeState);
+      setLikeCount(newLikeState ? newLikeCount - 1 : newLikeCount + 1);
+    }
+  };
+
+  // 格式化数字显示 (3000 -> 3k)
+  const formatCount = (count?: number): string => {
+    if (count == null) return "0"; // 同时处理 undefined/null
+
+    const absCount = Math.abs(count); // 处理负数
+
+    const format = (num: number, suffix: string) => {
+      const fixed = num.toFixed(1);
+      return fixed.endsWith('.0')
+          ? `${Math.floor(num)}${suffix}`
+          : `${fixed}${suffix}`;
+    };
+
+    if (absCount >= 1e6) return format(count / 1e6, "m");
+    if (absCount >= 1e3) return format(count / 1e3, "k");
+    return count.toString();
+  };
+
+  // 获取发帖人
+  useEffect(() => {
+    if (!resource) return;
+    // 发帖人
+    axios.get(process.env.PUBLIC_URL +`/user/info?userId=${resource?.userId}`)
+        .then(res => setUserInfo(res.data))
+        .catch(console.error);
+  }, [resource]);
+
+  // 当 resourceId 或分页参数变化时重新拉评论
+  useEffect(() => {
+    if (!resourceId) return;
+
+    fetchComments();
+  }, [resourceId, first, rows]);
+
+
+  //通过评论ID获取评论人信息
+  const getReplyUserName = (replyId: number) => {
+    if (replyId == null || replyId == 0) return '';
+    const replyComment = comments.find(comment => comment.commentId === replyId);
+    if (!replyComment?.userId) return '匿名用户';
+    return "回复 " + commentUserInfos.get(replyComment.userId)?.username || '匿名用户';
+  };
+
+  // 获取该资源的评论
+  const fetchComments = async () => {
+    try {
+      const pageNumber = first / rows + 1;
+      console.log("当前页" + pageNumber + "size" + rows);
+      const response = await axios.get<CommentList>(
+          process.env.PUBLIC_URL +`/comments`, {
+            params: { id: resourceId, pageNumber, rows, type: 'resource' }
+          }
+      );
+      console.log('获取评论列表:', response.data.records);
+      setComments(response.data.records);
+      setTotalComments(response.data.total);
+      // 拉取评论对应用户信息
+      response.data.records.forEach(comment => {
+        if (comment.userId != null && !commentUserInfos.has(comment.userId)) {
+          axios.get<UserInfo>(
+              process.env.PUBLIC_URL +`/user/info`,
+              { params: { userId: comment.userId } }
+          ).then(res => {
+            setCommentUserInfos(prev => new Map(prev).set(comment.userId!, res.data));
+          });
+        }
+      });
+    } catch (err) {
+      console.error('获取评论失败', err);
+      toast.current?.show({ severity: 'error', summary: 'error', detail: '获取评论失败' });
+    }
+  };
+
+  // 回复评论接口
+  const publishReply = async (commentId: number) => {
+    if (!replyValue.trim() || !resource) return;
+    console.log('发布评论:', commentId);
+    // console.log(typeof resourceId);
+    try {
+      const newComment: NewComment = {
+        userId: 22301145,
+        rewardId: 0,
+        threadId: 0,
+        resourceId: resource.resourceId,
+        replyId: commentId,
+        content: commentValue,
+        createAt: new Date().toISOString().slice(0, 19).replace('T', ' ')
+      };
+
+      const response = await axios.post(process.env.PUBLIC_URL +'/comment', newComment);
+
+      if (response.status === 200) {
+        toast.current?.show({ severity: 'success', summary: 'Success', detail: '回复成功' });
+        // 更新评论列表
+        fetchComments();
+        setVisibleReply(false)
+        // 清空输入框
+        setReplyValue('');
+      }
+    } catch (error) {
+      console.error('发布评论失败:', error);
+      toast.current?.show({ severity: 'error', summary: 'error', detail: '回复失败' });
+    }
+  };
+
+  // 发布评论接口
+  const publishComment = async () => {
+    if (!commentValue.trim() || !resource) return;
+
+    try {
+      const newComment: NewComment = {
+        userId: 22301145,
+        rewardId: 0,
+        threadId: 0,
+        resourceId: resource.resourceId,
+        replyId: 0, // 直接评论,不是回复
+        content: commentValue,
+        createAt: new Date().toISOString().slice(0, 19).replace('T', ' ')
+      };
+
+      const response = await axios.post(process.env.PUBLIC_URL +'/comment', newComment);
+
+      if (response.status === 200) {
+        toast.current?.show({ severity: 'success', summary: 'Success', detail: '评论成功' });
+        // 更新评论列表
+        fetchComments();
+        // 清空输入框
+        setCommentValue('');
+      }
+    } catch (error) {
+      console.error('发布评论失败:', error);
+      toast.current?.show({ severity: 'error', summary: 'error', detail: '发布评论失败' });
+    }
+  };
+
+  // 删除评论接口
+  const deleteComment = async (commentId: number) => {
+    if (!resourceId) return;
+
+    try {
+      // 调用 DELETE 接口,URL 中最后一段是要删除的 commentId
+      const response = await axios.delete(
+          process.env.PUBLIC_URL +`/comment?commentId=${commentId}`
+      );
+
+      if (response.status === 200) {
+        fetchComments();
+        toast.current?.show({ severity: 'success', summary: 'Success', detail: '删除评论成功' });
+      } else {
+        toast.current?.show({ severity: 'error', summary: 'error', detail: '删除评论失败' });
+        console.error('删除评论失败,状态码:', response.status);
+      }
+    } catch (error) {
+      console.error('删除评论接口报错:', error);
+    }
+  };
+
+  const ReplyHeader = (
+      <div className="flex align-items-center gap-1">
+        <h3>回复评论</h3>
+      </div>
   );
-};
+  if (!resourceId || !userInfo) return <div>Loading...</div>;
 
-export default EmptyPage;
+  return(
+      <div className="resource-detail-container">
+        <Toast ref={toast}></Toast>
+        {/*资源标题*/}
+        <div className="resource-header">
+          {resource?.resourceName}
+        </div>
+        {/*资源详细信息*/}
+        <div className="resource-info">
+          <Image
+              src={(process.env.NEXT_PUBLIC_NGINX_URL! + resource?.resourcePicture)}
+              alt= {resource?.resourceName}
+              width="540px"
+              height="300px"
+          />
+          <div className="resource-info-detail">
+            {/*资源热度*/}
+            <div className="resource-hot">
+              <Fire theme="outline" size="50" fill="#f5a623" strokeWidth={3}/>
+              <span className="resource-hot-data">{resource?.hot}</span>
+            </div>
+
+            {/*资源标签*/}
+            <div className="resource-label">
+              {resource?.gameplayList.map((tag, index) => (
+                  <Button
+                      key={index} label={tag}
+                      className="resource-label-button"
+                      onClick={() => {
+                        router.push(`/resource/classification/`)
+                      }}
+                  />
+              ))}
+            </div>
+
+            {/*资源浏览量和下载量*/}
+            <div className="resource-data">
+              <div className="resource-data-container">
+                <i className="pi pi-download"/>
+                <span className="resource-data-container-number">下载量:{formatCount(resource?.downloads)}</span>
+              </div>
+            </div>
+
+            {/*资源发布时间和更新时间*/}
+            <div className="resource-time">
+              <div className="resource-time-data">
+                发布时间:{resource?.uploadTime}
+              </div>
+              <div className="resource-time-data">
+                更新时间:{resource?.lastUpdateTime}
+              </div>
+            </div>
+          </div>
+        </div>
+        {/*资源总结*/}
+        <div className="resource-summary">
+          {resource?.resourceSummary}
+        </div>
+        {/*关注作者、点赞、抽藏、购买资源*/}
+        <div className="resource-operation">
+          <div className="resource-author">
+            <Avatar
+                image={`${process.env.NEXT_PUBLIC_NGINX_URL}/users/${resourceAuthor?.avatar}`}
+                shape="circle"
+                style={{ width: "60px", height: "60px" }}
+            />
+            <span className="resource-author-name">{resourceAuthor?.username}</span>
+
+            {handleSubscribe()}
+          </div>
+
+          <div className="resource-operation-detail">
+            <div className="resource-operation-detail-data">
+              <i
+                  className={isCollected ? "pi pi-star-fill" : "pi pi-star"}
+                  onClick={handleCollection}
+                  style={{
+                    cursor: 'pointer',
+                    fontSize: '30px',
+                    color: isCollected ? 'rgba(82, 102, 101, 1)' : 'inherit',
+                    transition: 'color 0.3s ease'
+                  }}
+              />
+              <span>{formatCount(collectionCount)}</span>
+            </div>
+
+
+
+            <div className="resource-operation-detail-data">
+              {isLiked ? <SmilingFace
+                      theme="filled"
+                      size="30"
+                      fill="#526665"
+                      strokeWidth={5}
+                      onClick={handleLike}
+                      style={{ cursor: 'pointer' }}
+                  />
+                  : <SmilingFace
+                      theme="outline"
+                      size="30"
+                      fill="#526665"
+                      strokeWidth={5}
+                      onClick={handleLike}
+                      style={{ cursor: 'pointer' }}
+                  />
+              }
+              <span>{formatCount(likeCount)}</span>
+            </div>
+
+            <ButtonGroup >
+              {isPurchase()}
+              <Button label={"$" + resource?.price} style={{
+                height:"44px", background:"rgba(82, 102, 101, 1)",
+                borderStyle:"solid", borderWidth:"1px", borderColor:"rgba(82, 102, 101, 1)",
+                borderRadius:"0 20px 20px 0", fontSize:"26px",
+              }} disabled={true}/>
+            </ButtonGroup>
+          </div>
+        </div>
+        {/*资源详情*/}
+        <div className="resource-detail">
+          <h1 className="resource-detail-title">资源详情</h1>
+          <div className="resource-detail-text">
+            {resource?.resourceDetail}
+          </div>
+        </div>
+        {/* 评论列表 */}
+        <div className="comments-section">
+          <div className="comments-header">
+            <h2>评论 ({totalComments})</h2>
+            <Link href="/community" className="no-underline">进入社区</Link>
+          </div>
+          <div className="comments-input">
+            <Avatar image={process.env.NEXT_PUBLIC_NGINX_URL + "users/" + userInfo.avatar} size="large" shape="circle" />
+            <InputText value={commentValue} placeholder="发布你的评论" onChange={(e) => setCommentValue(e.target.value)} />
+            <Button label="发布评论" onClick={publishComment} disabled={!commentValue.trim()} />
+          </div>
+          <div className="comments-list">
+            {comments.map((comment, index) => (
+                <div key={comment.commentId} className="comment-item">
+                  <div className="comment-user">
+                    <Avatar
+                        image={comment.userId ? process.env.NEXT_PUBLIC_NGINX_URL + "users/" + commentUserInfos.get(comment.userId)?.avatar : '/default-avatar.png'}
+                        size="normal"
+                        shape="circle"
+                    />
+                    <div className="comment-meta">
+                                    <span className="username">
+                                        {comment.userId ? commentUserInfos.get(comment.userId)?.username : '匿名用户'}
+                                    </span>
+                      <div className="comment-time">
+                        <span className="floor">#{first + index + 1}楼</span>
+                        <span className="time">{comment.createAt}</span>
+                      </div>
+                    </div>
+                    <i className='pi pi-ellipsis-v' onClick={(e) => ops.current[index].toggle(e)} />
+                  </div>
+                  <div className="comment-content">
+                    {<span className="reply-to">{getReplyUserName(comment.replyId)}</span>}
+                    <p>{comment.content}</p>
+                  </div>
+                  <OverlayPanel       // 回调 ref:把实例放到 ops.current 对应的位置
+                      ref={el => {
+                        if (el) ops.current[index] = el;
+                      }}>
+                    <Button label="回复" text size="small" onClick={() => setVisibleReply(true)} />
+                    {comment.userId === 22301010 &&
+                        <Button
+                            label="删除"
+                            text
+                            size="small"
+                            onClick={() => { console.log('Deleting comment:', comment.commentId, 'by user:', comment.userId); deleteComment(comment.commentId) }}
+                        />
+                    }
+                  </OverlayPanel>
+                  <Sidebar className='reply' header={ReplyHeader} visible={visibleReply} position="bottom" onHide={() => setVisibleReply(false)}>
+                    <div className="reply-input">
+                      <Avatar image={process.env.NEXT_PUBLIC_NGINX_URL + "users/" + userInfo.avatar} size="large" shape="circle" />
+                      <InputText value={replyValue} placeholder="发布你的评论" onChange={(e) => setReplyValue(e.target.value)} />
+                      <Button label="发布评论" onClick={() => publishReply(comment.commentId)} disabled={!replyValue.trim()} />
+                    </div>
+                  </Sidebar>
+                </div>
+            ))}
+            {totalComments > 5 && (<Paginator className="Paginator" first={first} rows={rows} totalRecords={totalComments} rowsPerPageOptions={[5, 10]} onPageChange={onPageChange} />)}
+          </div>
+        </div>
+
+        {/*用户购买资源弹窗*/}
+        <Dialog
+            header="购买资源"
+            visible={visible}
+            onHide={() => setVisible(false)}
+            className="purchase-dialog"
+            modal
+            footer={
+              <div className="dialog-footer">
+                <Button label="购买" icon="pi pi-check" onClick={handlePurchase} autoFocus />
+                <Button label="取消" icon="pi pi-times" onClick={() => setVisible(false)} className="p-button-text" />
+              </div>
+            }
+        >
+          <div className="form-text">
+            购买该资源需要{resource?.price}积分,是否购买?
+          </div>
+        </Dialog>
+      </div>
+  )
+}
+