mirror of
https://github.com/snowykami/neo-blog.git
synced 2025-09-26 11:06:23 +00:00
✨ 优化了评论分页加载的列表显示
All checks were successful
Push to Helm Chart Repository / build (push) Successful in 15s
All checks were successful
Push to Helm Chart Repository / build (push) Successful in 15s
This commit is contained in:
@ -4,8 +4,8 @@ import { Calendar, Clock, FileText, Flame, Heart, MessageCircle, PenLine, Square
|
||||
import { RenderMarkdown } from "@/components/common/markdown";
|
||||
import { isMobileByUA } from "@/utils/server/device";
|
||||
import { calculateReadingTime } from "@/utils/common/post";
|
||||
import {CommentSection} from "@/components/comment";
|
||||
import { TargetType } from '../../models/types';
|
||||
import { CommentSection } from "@/components/comment";
|
||||
import { TargetType } from '@/models/types';
|
||||
|
||||
function PostMeta({ post }: { post: Post }) {
|
||||
return (
|
||||
@ -139,7 +139,7 @@ async function BlogPost({ post }: { post: Post }) {
|
||||
{/* <ScrollToTop /> */}
|
||||
<PostHeader post={post} />
|
||||
<PostContent post={post} />
|
||||
<CommentSection targetType={TargetType.Post} targetId={post.id} />
|
||||
<CommentSection targetType={TargetType.Post} targetId={post.id} totalCount={post.commentCount} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -12,7 +12,6 @@ import { useDoubleConfirm } from "@/hooks/use-double-confirm";
|
||||
import { CommentInput } from "./comment-input";
|
||||
import { createComment, deleteComment, listComments, updateComment } from "@/api/comment";
|
||||
import { OrderBy } from "@/models/common";
|
||||
import config from "@/config";
|
||||
import { formatDateTime } from "@/utils/common/datetime";
|
||||
|
||||
|
||||
@ -23,7 +22,8 @@ export function CommentItem(
|
||||
parentComment,
|
||||
onCommentDelete,
|
||||
activeInput,
|
||||
setActiveInputId
|
||||
setActiveInputId,
|
||||
onReplySubmitted // 评论区计数更新用
|
||||
}: {
|
||||
user: User | null,
|
||||
comment: Comment,
|
||||
@ -31,6 +31,7 @@ export function CommentItem(
|
||||
onCommentDelete: ({ commentId }: { commentId: number }) => void,
|
||||
activeInput: { id: number; type: 'reply' | 'edit' } | null,
|
||||
setActiveInputId: (input: { id: number; type: 'reply' | 'edit' } | null) => void,
|
||||
onReplySubmitted: ({ commentContent, isPrivate }: { commentContent: string, isPrivate: boolean }) => void,
|
||||
}
|
||||
) {
|
||||
const locale = useLocale();
|
||||
@ -96,7 +97,7 @@ export function CommentItem(
|
||||
orderBy: OrderBy.CreatedAt,
|
||||
desc: false,
|
||||
page: 1,
|
||||
size: config.commentsPerPage,
|
||||
size: 999999,
|
||||
commentId: comment.id
|
||||
}
|
||||
).then(response => {
|
||||
@ -136,6 +137,7 @@ export function CommentItem(
|
||||
setShowReplies(true);
|
||||
setActiveInputId(null);
|
||||
setReplyCount(replyCount + 1);
|
||||
onReplySubmitted({ commentContent, isPrivate });
|
||||
}).catch(error => {
|
||||
toast.error(t("comment_failed") + ": " +
|
||||
error?.response?.data?.message || error?.message
|
||||
@ -289,6 +291,7 @@ export function CommentItem(
|
||||
onCommentDelete={onReplyDelete}
|
||||
activeInput={activeInput}
|
||||
setActiveInputId={setActiveInputId}
|
||||
onReplySubmitted={onReplySubmitted}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
@ -5,7 +5,7 @@ import { useTranslations } from "next-intl";
|
||||
import { Suspense, useEffect, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { Comment } from "@/models/comment";
|
||||
import { createComment, deleteComment, listComments } from "@/api/comment";
|
||||
import { createComment, deleteComment, getComment, listComments } from "@/api/comment";
|
||||
import { TargetType } from "@/models/types";
|
||||
import { OrderBy } from "@/models/common";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
@ -22,18 +22,22 @@ import "./style.css";
|
||||
export function CommentSection(
|
||||
{
|
||||
targetType,
|
||||
targetId
|
||||
targetId,
|
||||
totalCount = 0
|
||||
}: {
|
||||
targetType: TargetType,
|
||||
targetId: number
|
||||
targetId: number,
|
||||
totalCount?: number
|
||||
}
|
||||
) {
|
||||
const t = useTranslations('Comment')
|
||||
|
||||
const [currentUser, setCurrentUser] = useState<User | null>(null);
|
||||
const [comments, setComments] = useState<Comment[]>([]);
|
||||
const [refreshCommentsKey, setRefreshCommentsKey] = useState(0);
|
||||
const [activeInput, setActiveInput] = useState<{ id: number; type: 'reply' | 'edit' } | null>(null);
|
||||
const [page, setPage] = useState(1); // 当前页码
|
||||
const [totalCommentCount, setTotalCommentCount] = useState(totalCount); // 评论总数
|
||||
const [needLoadMore, setNeedLoadMore] = useState(true); // 是否需要加载更多,当最后一次获取的评论数小于分页大小时设为false
|
||||
|
||||
// 获取当前登录用户
|
||||
useEffect(() => {
|
||||
@ -51,13 +55,13 @@ export function CommentSection(
|
||||
depth: 0,
|
||||
orderBy: OrderBy.CreatedAt,
|
||||
desc: true,
|
||||
page: 1,
|
||||
page: page,
|
||||
size: config.commentsPerPage,
|
||||
commentId: 0
|
||||
}).then(response => {
|
||||
setComments(response.data);
|
||||
});
|
||||
}, [refreshCommentsKey])
|
||||
}, [])
|
||||
|
||||
const onCommentSubmitted = ({ commentContent, isPrivate }: { commentContent: string, isPrivate: boolean }) => {
|
||||
createComment({
|
||||
@ -66,25 +70,56 @@ export function CommentSection(
|
||||
content: commentContent,
|
||||
replyId: null,
|
||||
isPrivate,
|
||||
}).then(() => {
|
||||
}).then(res => {
|
||||
toast.success(t("comment_success"));
|
||||
setRefreshCommentsKey(k => k + 1);
|
||||
setTotalCommentCount(c => c + 1);
|
||||
setComments(prevComments => prevComments.slice(0, -1));
|
||||
getComment({ id: res.data.id }).then(response => {
|
||||
console.log("New comment fetched:", response.data);
|
||||
setComments(prevComments => [response.data, ...prevComments]);
|
||||
});
|
||||
setActiveInput(null);
|
||||
})
|
||||
}
|
||||
|
||||
const onReplySubmitted = ({ }: { commentContent: string, isPrivate: boolean }) => {
|
||||
setTotalCommentCount(c => c + 1);
|
||||
}
|
||||
|
||||
const onCommentDelete = ({ commentId }: { commentId: number }) => {
|
||||
deleteComment({ id: commentId }).then(() => {
|
||||
toast.success(t("delete_success"));
|
||||
setRefreshCommentsKey(k => k + 1);
|
||||
setComments(prevComments => prevComments.filter(comment => comment.id !== commentId));
|
||||
setTotalCommentCount(c => c - 1);
|
||||
}).catch(error => {
|
||||
toast.error(t("delete_failed") + ": " + error.message);
|
||||
});
|
||||
}
|
||||
|
||||
const handleLoadMore = () => {
|
||||
const nextPage = page + 1;
|
||||
listComments({
|
||||
targetType,
|
||||
targetId,
|
||||
depth: 0,
|
||||
orderBy: OrderBy.CreatedAt,
|
||||
desc: true,
|
||||
page: nextPage,
|
||||
size: config.commentsPerPage,
|
||||
commentId: 0
|
||||
}).then(response => {
|
||||
if (response.data.length < config.commentsPerPage) {
|
||||
setNeedLoadMore(false);
|
||||
}
|
||||
setComments(prevComments => [...prevComments, ...response.data]);
|
||||
setPage(nextPage);
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Separator className="my-16" />
|
||||
<div className="font-bold text-2xl">{t("comment")}</div>
|
||||
<div className="font-bold text-2xl">{t("comment")} ({totalCommentCount})</div>
|
||||
<CommentInput
|
||||
user={currentUser}
|
||||
onCommentSubmitted={onCommentSubmitted}
|
||||
@ -92,7 +127,7 @@ export function CommentSection(
|
||||
<div className="mt-4">
|
||||
<Suspense fallback={<CommentLoading />}>
|
||||
{comments.map((comment, idx) => (
|
||||
<div key={comment.id} className="fade-in-up" style={{ animationDelay: `${idx * 60}ms` }}>
|
||||
<div key={comment.id} className="" style={{ animationDelay: `${idx * 60}ms` }}>
|
||||
<Separator className="my-2" />
|
||||
<CommentItem
|
||||
user={currentUser}
|
||||
@ -101,10 +136,20 @@ export function CommentSection(
|
||||
onCommentDelete={onCommentDelete}
|
||||
activeInput={activeInput}
|
||||
setActiveInputId={setActiveInput}
|
||||
onReplySubmitted={onReplySubmitted}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</Suspense>
|
||||
{needLoadMore ?
|
||||
<p onClick={handleLoadMore} className="text-center text-sm text-gray-500 my-4 cursor-pointer hover:underline">
|
||||
{t("load_more")}
|
||||
</p>
|
||||
:
|
||||
<p className="text-center text-sm text-gray-500 my-4">
|
||||
{t("no_more")}
|
||||
</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
Reference in New Issue
Block a user