fix: API param localStorge useId
Change-Id: Ifd624462360111f08ca308ea07b6fcaac0747104
diff --git "a/src/app/resource/resource-detail/\133resourceId\135/page.tsx" "b/src/app/resource/resource-detail/\133resourceId\135/page.tsx"
index 8d28596..5e0a634 100644
--- "a/src/app/resource/resource-detail/\133resourceId\135/page.tsx"
+++ "b/src/app/resource/resource-detail/\133resourceId\135/page.tsx"
@@ -1,10 +1,10 @@
'use client';
-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 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';
@@ -24,22 +24,24 @@
import { Sidebar } from 'primereact/sidebar';
// 分页
import { Paginator, PaginatorPageChangeEvent } from 'primereact/paginator';
-
+import { useLocalStorage } from '../../../hook/useLocalStorage';
// 样式
import './resource-detail.scss';
-
+interface User {
+ Id: number;
+}
// 种子
interface Torrent {
- torrentRecordId: number;
+ torrentRecordId: number;
torrentUrl: string;
- infoHash: string;
- uploadTime: string;
- uploaderUserId: number;
+ infoHash: string;
+ uploadTime: string;
+ uploaderUserId: number;
}
// 资源版本
interface ResourceVersion {
- resourceVersionId: string; // 资源版本id
+ resourceVersionId: string; // 资源版本id
resourceVersionName: string; // 资源版本名称
compatibleVersions: string[]; // 兼容的游戏版本列表
torrentList: Torrent[]; // 种子列表
@@ -47,27 +49,27 @@
// 资源信息
interface Resource {
- resourceId: number;
+ resourceId: number;
resourceName: string; // 资源标题
- resourcePicture: string; // 资源照片网址
+ resourcePicture: string; // 资源照片网址
resourceSummary: string; // 资源简介(一句话)
- resourceDetail: string; // 资源介绍
- uploadTime: string; // 上传时间
+ resourceDetail: string; // 资源介绍
+ uploadTime: string; // 上传时间
lastUpdateTime: string; // 最近更新时间
- price: number;
+ price: number;
downloads: number; // 下载数
- likes: number; // 点赞数
- collections: number; // 收藏数
- comments: number; // 评论数
- seeds: number; // 种子数
- classify: string; // 资源分类(材质包:resourcePack,模组:mod,整合包:modPack ,地图:map
+ 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; // 是否是该用户上传的
+ resourceVersionList: ResourceVersion[]; // 资源版本列表
+ isCollect: boolean; // 是否被收藏
+ isLike: boolean; // 是否被点赞
+ isPurchase: boolean; // 是否被购买
+ isUpload: boolean; // 是否是该用户上传的
userId: number; // 资源上传者的id
}
@@ -116,7 +118,7 @@
// 关注
interface Subscriber {
userId: number;
- username: string;
+ username: string;
}
// 关注列表
@@ -125,6 +127,8 @@
}
export default function ResourceDetail() {
+ const user = useLocalStorage<User>('user');
+ const userId: number = user?.Id ?? -1;
// 获取URL参数
const params = useParams<{ resourceId: string }>();
const resourceId = decodeURIComponent(params.resourceId); // 防止中文路径乱码
@@ -182,8 +186,8 @@
const fetchResourceInfo = async () => {
try {
// console.log(resourceId);
- const response = await axios.get<Resource>(process.env.PUBLIC_URL +`/resource/info`, {
- params: { resourceId: resourceId, userId: 22301010 }
+ const response = await axios.get<Resource>(process.env.PUBLIC_URL + `/resource/info`, {
+ params: { resourceId: resourceId, userId }
});
console.log('获取资源信息:', response.data);
setResource(response.data);
@@ -211,7 +215,7 @@
try {
// console.log(resourceId);
// console.log(resourceAuthorId);
- const response = await axios.get<ResourceAuthor>(process.env.PUBLIC_URL +`/user/info`, {
+ const response = await axios.get<ResourceAuthor>(process.env.PUBLIC_URL + `/user/info`, {
params: { userId: resourceAuthorId }
});
console.log('获取资源作者信息:', response.data);
@@ -231,7 +235,7 @@
const fetchSubscriber = async () => {
try {
const response = await axios.get<SubscriberList>(process.env.PUBLIC_URL + `/user/subscriber`, {
- params: {userId: 223010100}
+ params: { userId }
});
console.log("关注列表:", response.data);
setSubscriberList(response.data);
@@ -255,177 +259,110 @@
// 资源作者 ID
const authorId = resource?.userId;
// 当前登录用户 ID
- const currentUserId = 223010100;
+ const currentUserId = userId;
// 资源作者与浏览用户是同一人,不显示按钮
- if (!authorId || authorId == currentUserId){
+ 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 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
- );
+ // 若未关注,则显示关注按钮
+ <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: '已成功关注该用户',
- });
+ 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: '关注失败',
- });
- }
- }}
- />
+ 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?.userId == userId) {
+ return;
}
// 该资源已被购买
if (resource?.isPurchase) {
return (
- <Button label="已购买" style={{width:"120px", height:"44px",
- borderRadius:"20px 0 0 20px",}} disabled={true}/>
+ <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)}
- />
+ <Button
+ label="立即购买"
+ style={{
+ width: "120px", height: "44px",
+ borderRadius: "20px 0 0 20px",
+ }}
+ onClick={() => setVisible(true)}
+ />
)
}
}
@@ -434,7 +371,7 @@
const handlePurchase = async () => {
try {
const postData = {
- userId: 223010100, // 记得用户登录状态获取
+ userId, // 记得用户登录状态获取
resourceId: resource?.resourceId
};
// 发送POST请求
@@ -467,14 +404,14 @@
try {
if (newCollectionState) {
// 收藏操作
- await axios.post(process.env.PUBLIC_URL +`/resource/collection`, {
- params: { resourceId: resourceId, userId: 22301010 }
+ await axios.post(process.env.PUBLIC_URL + `/resource/collection`, {
+ params: { resourceId: resourceId, userId }
});
console.log('收藏资源');
} else {
// 取消收藏操作
- await axios.delete(process.env.PUBLIC_URL +`/resource/collection`, {
- params: { resourceId: resourceId, userId: 22301010 }
+ await axios.delete(process.env.PUBLIC_URL + `/resource/collection`, {
+ params: { resourceId: resourceId, userId }
});
console.log('取消收藏资源');
}
@@ -504,14 +441,14 @@
try {
if (newLikeState) {
// 点赞操作
- await axios.post(process.env.PUBLIC_URL +`/resource/like`, {
- params: { resourceId: resourceId, userId: 22301010 }
+ await axios.post(process.env.PUBLIC_URL + `/resource/like`, {
+ params: { resourceId: resourceId, userId }
});
console.log('点赞资源');
} else {
// 取消点赞操作
- await axios.delete(process.env.PUBLIC_URL +`/resource/like`, {
- params: { resourceId: resourceId, userId: 22301010 }
+ await axios.delete(process.env.PUBLIC_URL + `/resource/like`, {
+ params: { resourceId: resourceId, userId }
});
console.log('取消点赞资源');
}
@@ -538,8 +475,8 @@
const format = (num: number, suffix: string) => {
const fixed = num.toFixed(1);
return fixed.endsWith('.0')
- ? `${Math.floor(num)}${suffix}`
- : `${fixed}${suffix}`;
+ ? `${Math.floor(num)}${suffix}`
+ : `${fixed}${suffix}`;
};
if (absCount >= 1e6) return format(count / 1e6, "m");
@@ -551,9 +488,9 @@
useEffect(() => {
if (!resource) return;
// 发帖人
- axios.get(process.env.PUBLIC_URL +`/user/info?userId=${resource?.userId}`)
- .then(res => setUserInfo(res.data))
- .catch(console.error);
+ axios.get(process.env.PUBLIC_URL + `/user/info?userId=${resource?.userId}`)
+ .then(res => setUserInfo(res.data))
+ .catch(console.error);
}, [resource]);
// 当 resourceId 或分页参数变化时重新拉评论
@@ -578,9 +515,9 @@
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' }
- }
+ process.env.PUBLIC_URL + `/comments`, {
+ params: { id: resourceId, pageNumber, rows, type: 'resource' }
+ }
);
console.log('获取评论列表:', response.data.records);
setComments(response.data.records);
@@ -589,8 +526,8 @@
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 } }
+ process.env.PUBLIC_URL + `/user/info`,
+ { params: { userId: comment.userId } }
).then(res => {
setCommentUserInfos(prev => new Map(prev).set(comment.userId!, res.data));
});
@@ -609,7 +546,7 @@
// console.log(typeof resourceId);
try {
const newComment: NewComment = {
- userId: 22301145,
+ userId,
rewardId: 0,
threadId: 0,
resourceId: resource.resourceId,
@@ -618,7 +555,7 @@
createAt: new Date().toISOString().slice(0, 19).replace('T', ' ')
};
- const response = await axios.post(process.env.PUBLIC_URL +'/comment', newComment);
+ const response = await axios.post(process.env.PUBLIC_URL + '/comment', newComment);
if (response.status === 200) {
toast.current?.show({ severity: 'success', summary: 'Success', detail: '回复成功' });
@@ -640,7 +577,7 @@
try {
const newComment: NewComment = {
- userId: 22301145,
+ userId,
rewardId: 0,
threadId: 0,
resourceId: resource.resourceId,
@@ -649,7 +586,7 @@
createAt: new Date().toISOString().slice(0, 19).replace('T', ' ')
};
- const response = await axios.post(process.env.PUBLIC_URL +'/comment', newComment);
+ const response = await axios.post(process.env.PUBLIC_URL + '/comment', newComment);
if (response.status === 200) {
toast.current?.show({ severity: 'success', summary: 'Success', detail: '评论成功' });
@@ -671,7 +608,7 @@
try {
// 调用 DELETE 接口,URL 中最后一段是要删除的 commentId
const response = await axios.delete(
- process.env.PUBLIC_URL +`/comment?commentId=${commentId}`
+ process.env.PUBLIC_URL + `/comment?commentId=${commentId}`
);
if (response.status === 200) {
@@ -687,219 +624,219 @@
};
const ReplyHeader = (
- <div className="flex align-items-center gap-1">
- <h3>回复评论</h3>
- </div>
+ <div className="flex align-items-center gap-1">
+ <h3>回复评论</h3>
+ </div>
);
if (!resourceId || !userInfo) return <div>Loading...</div>;
- 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>
+ 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 === userId &&
+ <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>
)
}