发送评论

main
lee-zt 4 days ago
parent 2220f3f3e9
commit 0fc3a5e109

@ -169,5 +169,64 @@ export const usePostDetailStore = defineStore("postDetail", {
this.detailLoading = false; this.detailLoading = false;
} }
}, },
// 发送评论或回复
async sendComment(newCommentData) {
if (!content || !this.post?.postId) return;
const RequestData = {
id: null,
postId: newCommentData.postId, // 帖子ID
content: newCommentData.content, // 评论内容
parentCommentId: newCommentData.parentCommentId, // 如果是一级评论则为0
};
try {
const res = await request.post('/comment', RequestData);
if (res.code === 200) {
const commentObj = {
id: res.data.id,
content: newCommentData.content,
userId: newCommentData.userId,
userName: newCommentData.userName,
userAvatar: newCommentData.userAvatar,
createTime: new Date().toISOString(),
likeCount: 0,
replyCount: 0,
postId: this.post.postId,
parentCommentId: newCommentData.parentCommentId,
topId: newCommentData.topId,
isLike: 0,
replies: [],
repliesLastVal: 0,
repliesOffset: 0,
repliesSize: 5,
repliesFinished: false,
repliesLoading: false,
};
// 新增评论后刷新评论列表或插入到对应位置
if (!parentCommentId) {
// 一级评论,插入到最前面
this.comments.unshift(commentObj);
this.post.commentCount = (this.post.commentCount || 0) + 1; // 更新帖子评论数
} else {
// 回复评论,插入到对应父评论的 replies
// 先找顶级父评论
let parent = this.comments.find(c => c.id === commentObj.parentCommentId || c.id === commentObj.topId);
// 如果是二级及以上回复,需递归查找
if (!parent && commentObj.topId) {
parent = this.comments.find(c => c.id === commentObj.topId);
}
if (parent) {
parent.replies.unshift(commentObj);
parent.replyCount = (parent.replyCount || 0) + 1;
}
}
console.log("评论成功:", res);
}else {
console.error("评论失败:", res);
ElMessage.error(res.message || '评论失败');
}
} catch (e) {
alert(e.response?.message || '发送评论失败,请稍后重试');
}
},
}, },
}); });

@ -35,25 +35,34 @@
<img :src="comment.userAvatar || require('@/assets/default-avatar/boy_1.png')" alt="评论者头像" class="comment-avatar" /> <img :src="comment.userAvatar || require('@/assets/default-avatar/boy_1.png')" alt="评论者头像" class="comment-avatar" />
<div class="comment-content"> <div class="comment-content">
<p class="comment-name">{{ comment.userName }}</p> <p class="comment-name">{{ comment.userName }}</p>
<p class="comment-text">{{ comment.content }}</p> <p class="comment-text">{{ comment.content }}</p>
<div class="comment-meta"> <div class="comment-meta">
<span class="comment-time">{{ comment.createTime ? formatTime(comment.createTime) : '' }}</span> <span class="comment-time">{{ comment.createTime ? formatTime(comment.createTime) : '' }}</span>
<span class="comment-likes"> {{ comment.likeCount ?? 0 }}</span> <span class="comment-likes"> {{ comment.likeCount ?? 0 }}</span>
<span v-if="comment.replyCount > 0"> <span v-if="comment.replyCount > 0">
<button @click="loadReplies(comment)"> <button @click="loadReplies(comment)">
{{ comment.replies.length > 0 ? '收起回复' : '展开回复' }} ({{ comment.replyCount }}) {{ comment.replies.length > 0 ? '收起回复' : '展开回复' }} ({{ comment.replyCount }})
</button> </button>
</span> </span>
</div> <button class="reply-btn" @click="startReply(comment)"></button>
<!-- 子评论列表 --> </div>
<ul v-if="comment.replies.length > 0" class="replies-list"> <!-- 子评论列表 -->
<li v-for="reply in comment.replies" :key="reply.id" class="reply-item"> <ul v-if="comment.replies.length > 0" class="replies-list">
<span class="reply-user">{{ reply.userName }}</span> <li v-for="reply in comment.replies" :key="reply.id" class="comment-item reply-item">
<span class="reply-content">{{ reply.content }}</span> <img :src="reply.userAvatar" alt="评论者头像" class="comment-avatar" />
</li> <div class="comment-content">
<li v-if="comment.repliesLoading" class="reply-loading">...</li> <p class="comment-name">{{ reply.userName }}</p>
<li v-if="comment.repliesFinished" class="reply-finished"></li> <p class="comment-text">{{ reply.content }}</p>
</ul> <div class="comment-meta">
<span class="comment-time">{{ reply.createTime ? formatTime(reply.createTime) : '' }}</span>
<span class="comment-likes"> {{ reply.likeCount ?? 0 }}</span>
<button class="reply-btn" @click="startReply(reply)"></button>
</div>
</div>
</li>
<li v-if="comment.repliesLoading" class="reply-loading">...</li>
<li v-if="comment.repliesFinished" class="reply-finished"></li>
</ul>
</div> </div>
</li> </li>
</ul> </ul>
@ -61,6 +70,10 @@
<!-- 发送评论 --> <!-- 发送评论 -->
<div class="comment-box"> <div class="comment-box">
<div v-if="replyingComment" class="replying-tip">
正在回复 @{{ replyingComment.userName }}
<button class="cancel-reply-btn" @click="cancelReply"></button>
</div>
<textarea <textarea
v-model="newComment" v-model="newComment"
placeholder="写下你的评论..." placeholder="写下你的评论..."
@ -81,14 +94,23 @@ const route = useRoute();
const postDetailStore = usePostDetailStore(); const postDetailStore = usePostDetailStore();
const userStore = useUserStore(); // const userStore = useUserStore(); //
const newComment = ref(''); const newComment = ref('');
const replyingComment = ref(null); // null
// //
const author = postDetailStore.post?.author ; const author = postDetailStore.post?.author ;
const isFollowing = ref(false); const isFollowing = ref(false);
const toggleFollow = () => { const toggleFollow = () => {
isFollowing.value = !isFollowing.value; isFollowing.value = !isFollowing.value;
}; };
//
function startReply(comment) {
replyingComment.value = comment;
}
//
function cancelReply() {
replyingComment.value = null;
}
// //
function formatTime(timeStr) { function formatTime(timeStr) {
const date = new Date(timeStr); const date = new Date(timeStr);
@ -106,7 +128,6 @@ function handleScroll() {
postDetailStore.fetchComments(); postDetailStore.fetchComments();
} }
} }
// //
function loadReplies(comment) { function loadReplies(comment) {
if (!comment.repliesLoading && !comment.repliesFinished) { if (!comment.repliesLoading && !comment.repliesFinished) {
@ -114,21 +135,27 @@ function loadReplies(comment) {
} }
} }
// //
const sendComment = () => { const sendComment = () => {
if (!userStore.isLoggedIn) { if (!userStore.isLoggedIn) {
alert('请先登录后再评论'); alert('请先登录后再评论');
return; return;
} }
if (newComment.value.trim()) { if (newComment.value.trim()) {
postDetailStore.addComment({ const newCommentData = {
content: newComment.value,
userAvatar: userStore.userInfo.avatar, userAvatar: userStore.userInfo.avatar,
userName: userStore.userInfo.username, userName: userStore.userInfo.username,
userId: userStore.userInfo.userid, userId: userStore.userInfo.userid,
content: newComment.value,
createTime: new Date().toISOString(), createTime: new Date().toISOString(),
likeCount: 0, likeCount: 0,
}); replyCount: 0,
postId: postDetailStore.post.id,
parentCommentId: replyingComment.value ? replyingComment.value.id : null, // parentCommentId
topId: null,
isLike: false,
}
postDetailStore.addComment(newCommentData);
newComment.value = ''; newComment.value = '';
} }
}; };
@ -405,4 +432,28 @@ onUnmounted(() => {
padding: 6px 0; padding: 6px 0;
text-align: left; text-align: left;
} }
.replying-tip {
color: #409eff;
margin-bottom: 6px;
font-size: 14px;
}
.cancel-reply-btn {
background: none;
border: none;
color: #f56c6c;
margin-left: 8px;
cursor: pointer;
font-size: 13px;
}
.reply-btn {
background: none;
border: none;
color: #409eff;
cursor: pointer;
font-size: 13px;
margin-left: 10px;
}
.reply-btn:hover {
text-decoration: underline;
}
</style> </style>
Loading…
Cancel
Save