实现发送评论

main
lee-zt 4 weeks ago
parent b626045de2
commit c445c1ca5c

@ -1,21 +1,21 @@
##本地开发环境 ##本地开发环境
lj: #lj:
db: # db:
host: 192.168.59.129 # host: 192.168.59.129
password: Forely123! # password: Forely123!
redis: # redis:
host: 192.168.59.129 # host: 192.168.59.129
port: 6379 # port: 6379
password: Forely123! # password: Forely123!
rabbitmq: # rabbitmq:
host: 192.168.59.129 # host: 192.168.59.129
port: 5672 # port: 5672
username: admin # username: admin
password: Forely123! # password: Forely123!
minio: # minio:
endpoint: http://192.168.59.129:9000 # endpoint: http://192.168.59.129:9000
accessKey: forely # accessKey: forely
secretKey: Forely123! # secretKey: Forely123!
#lj: #lj:
# db: # db:
@ -33,4 +33,22 @@ lj:
# minio: # minio:
# endpoint: http://192.168.125.128:9000 # endpoint: http://192.168.125.128:9000
# accessKey: minio_admin # accessKey: minio_admin
# secretKey: Minio@1234 # secretKey: Minio@1234
lj:
db:
host: localhost
password: 123456
redis:
host: localhost
port: 6379
password: 123456
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
minio:
endpoint: http://localhost:9005
accessKey: leezt
secretKey: lzt264610

@ -34,7 +34,7 @@ const routes = [
component: PostDetail component: PostDetail
}, },
{ {
path: '/user', path: '/user/:userId',
name: 'UserPage', name: 'UserPage',
component: UserPage, component: UserPage,
meta: { requiresAuth: true } meta: { requiresAuth: true }

@ -16,67 +16,67 @@ export const usePostDetailStore = defineStore("postDetail", {
commentsFinished: false, // 是否加载完全部评论 commentsFinished: false, // 是否加载完全部评论
}), }),
actions: { actions: {
// ...existing code... async fetchComments() {
async fetchComments() { if (this.commentsLoading || this.commentsFinished) return;
if (this.commentsLoading || this.commentsFinished) return; this.commentsLoading = true;
this.commentsLoading = true; // 拼接参数到URL
// 拼接参数到URL const params = [
const params = [ `lastVal=${this.lastVal}`,
`lastVal=${this.lastVal}`, `offset=${this.offset}`,
`offset=${this.offset}`, `size=${this.size}`,
`size=${this.size}`, `postId=${this.post.postId}`
`postId=${this.post.postId}` ].join('&');
].join('&'); const url = `/comment/list?${params}`;
const url = `/comment/list?${params}`; try {
try { const response = await request.get(url);
const response = await request.get(url); if (response.code === 200) {
if (response.code === 200) { // 初始化每条评论的子评论分页状态
// 初始化每条评论的子评论分页状态 const comments = (response.data.records || []).map(item => ({
const comments = (response.data.records || []).map(item => ({ ...item,
...item, replies: [],
replies: [], showReplies: false, // 是否显示子评论
repliesLastVal: Date.now(), repliesLastVal: Date.now(),
repliesOffset: 0, repliesOffset: 0,
repliesSize: 5, repliesSize: 5,
repliesLoading: false, repliesLoading: false,
repliesFinished: false, repliesFinished: false,
})); }));
this.comments.push(...comments); this.comments.push(...comments);
this.lastVal = response.data.lastVal; this.lastVal = response.data.lastVal;
this.offset = response.data.offset; this.offset = response.data.offset;
if (comments.length < this.size) { if (comments.length < this.size) {
this.commentsFinished = true; // 如果评论数少于每页大小,标记为已加载完 this.commentsFinished = true; // 如果评论数少于每页大小,标记为已加载完
}
}
else {
ElMessage({
message: '获取评论失败,请稍后重试',
type: 'error',
duration: 500
});
}
} catch (error) {
console.error("获取评论失败:", error);
alert(error.response?.message || '获取评论失败,请稍后重试');
}finally {
this.commentsLoading = false;
} }
} },
else {
ElMessage({
message: '获取评论失败,请稍后重试',
type: 'error',
duration: 500
});
}
} catch (error) {
console.error("获取评论失败:", error);
alert(error.response?.message || '获取评论失败,请稍后重试');
}finally {
this.commentsLoading = false;
}
},
// ...existing code...
// 获取某条评论的子评论 // 获取某条评论的子评论
async fetchReplies(parentCommentId, commentObj) { async fetchReplies(parentCommentId, commentObj) {
if (commentObj.repliesLoading || commentObj.repliesFinished) return; if (commentObj.repliesLoading || commentObj.repliesFinished) return;
commentObj.repliesLoading = true; commentObj.repliesLoading = true;
// 请求子评论数据 /// 拼接参数到URL
const RequestReplyData = { const params = [
lastVal: commentObj.repliesLastVal, `lastVal=${commentObj.repliesLastVal}`,
offset: commentObj.repliesOffset, `offset=${commentObj.repliesOffset}`,
size: commentObj.repliesSize, `size=${commentObj.repliesSize}`,
postId: this.post?.postId, `postId=${this.post?.postId}`,
parentCommentId: parentCommentId, `parentCommentId=${parentCommentId}`
}; ].join('&');
const url = `/comment/list/reply?${params}`;
try { try {
const res = await request.post('/comment/list/reply', RequestReplyData); const res = await request.get(url);
if (res.code === 200) { if (res.code === 200) {
const records = (res.data.records || []).map(item => ({ const records = (res.data.records || []).map(item => ({
...item, ...item,
@ -169,15 +169,17 @@ async fetchComments() {
async sendComment(newCommentData) { async sendComment(newCommentData) {
if (!newCommentData.content || !this.post?.postId) return; if (!newCommentData.content || !this.post?.postId) return;
const RequestData = { const RequestData = {
id:null,
postId: newCommentData.postId, // 帖子ID postId: newCommentData.postId, // 帖子ID
content: newCommentData.content, // 评论内容 content: newCommentData.content, // 评论内容
parentCommentId: newCommentData.parentCommentId, parentCommentId: newCommentData.parentCommentId,
}; };
try { try {
const res = await request.post('/comment', RequestData); const res = await request.post('/comment', RequestData);
console.log("发送评论返回:", res.data);
if (res.code === 200) { if (res.code === 200) {
const commentObj = { const commentObj = {
id: res.data.id, id: res.data,
content: newCommentData.content, content: newCommentData.content,
userId: newCommentData.userId, userId: newCommentData.userId,
userName: newCommentData.userName, userName: newCommentData.userName,
@ -191,6 +193,7 @@ async fetchComments() {
topId: newCommentData.topId, topId: newCommentData.topId,
isLike: 0, isLike: 0,
}; };
console.log("发送评论数据:", commentObj.id);
// 新增评论后刷新评论列表或插入到对应位置 // 新增评论后刷新评论列表或插入到对应位置
if (!newCommentData.parentCommentId) { if (!newCommentData.parentCommentId) {
// 一级评论,插入到最前面 // 一级评论,插入到最前面
@ -201,17 +204,17 @@ async fetchComments() {
commentObj.repliesSize = 5; commentObj.repliesSize = 5;
commentObj.repliesLoading = false; commentObj.repliesLoading = false;
commentObj.repliesFinished = false; commentObj.repliesFinished = false;
this.comments.unshift(commentObj); this.comments.unshift(commentObj);
this.post.commentCount = (this.post.commentCount || 0) + 1; // 更新帖子评论数
} else { } else {
// 回复,只插入到一级评论的 replies // 回复,只插入到一级评论的 replies
let parent = this.comments.find(c => c.id === newCommentData.parentCommentId); let parent = this.comments.find(c => c.id === newCommentData.topId);
if (parent) { if (parent) {
parent.replies.unshift(commentObj); parent.replies.push(commentObj);
parent.replyCount = (parent.replyCount || 0) + 1; parent.replyCount = (parent.replyCount || 0) + 1;
} }
} }
console.log("评论成功:", res); this.post.commentCount = (this.post.commentCount || 0) + 1; // 更新帖子评论数
console.log("评论成功:", commentObj);
}else { }else {
console.error("评论失败:", res); console.error("评论失败:", res);
ElMessage.error(res.message || '评论失败'); ElMessage.error(res.message || '评论失败');

@ -2,7 +2,13 @@
<div class="post-detail-container"> <div class="post-detail-container">
<!-- 作者信息栏 --> <!-- 作者信息栏 -->
<div class="author-info" v-if="author && author.userName"> <div class="author-info" v-if="author && author.userName">
<img :src="author.userAvatar || require('@/assets/default-avatar/boy_1.png')" alt="头像" class="author-avatar" /> <img
:src="author.userAvatar || require('@/assets/default-avatar/boy_1.png')"
alt="头像"
class="author-avatar"
@click="goUserHome(author.userId)"
style="cursor: pointer;"
/>
<div class="author-details"> <div class="author-details">
<h3 class="author-name">{{ author.userName || '匿名用户' }}</h3> <h3 class="author-name">{{ author.userName || '匿名用户' }}</h3>
<p class="author-stats">粉丝数{{ author.followers ?? 0 }}</p> <p class="author-stats">粉丝数{{ author.followers ?? 0 }}</p>
@ -18,7 +24,7 @@
<h1 class="post-title">{{ postDetailStore.post?.title || '' }}</h1> <h1 class="post-title">{{ postDetailStore.post?.title || '' }}</h1>
<p class="post-body">{{ postDetailStore.post?.content || '' }}</p> <p class="post-body">{{ postDetailStore.post?.content || '' }}</p>
<div class="post-stats"> <div class="post-stats">
<span> 热度 {{ postDetailStore.post?.likeCount ?? 0 }}</span> <span> 浏览量 {{ postDetailStore.post?.likeCount ?? 0 }}</span>
<span> 点赞 {{ postDetailStore.post?.favoriteCount ?? 0 }}</span> <span> 点赞 {{ postDetailStore.post?.favoriteCount ?? 0 }}</span>
<span> 评论 {{ postDetailStore.post?.commentCount ?? 0 }}</span> <span> 评论 {{ postDetailStore.post?.commentCount ?? 0 }}</span>
</div> </div>
@ -32,7 +38,13 @@
<h2 class="comments-title">评论</h2> <h2 class="comments-title">评论</h2>
<ul class="comments-list"> <ul class="comments-list">
<li v-for="comment in postDetailStore.comments" :key="comment.id" class="comment-item"> <li v-for="comment in postDetailStore.comments" :key="comment.id" class="comment-item">
<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"
@click="goUserHome(comment.userId)"
style="cursor: pointer;"
/>
<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>
@ -41,15 +53,21 @@
<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 && comment.replies.length > 0 ? '收起回复' : '展开回复' }} ({{ comment.replyCount }}) {{ comment.showReplies> 0 ? '收起回复' : '展开回复' }} ({{ comment.replyCount }})
</button> </button>
</span> </span>
<button class="reply-btn" @click="startReply(comment)"></button> <button class="reply-btn" @click="startReply(comment)"></button>
</div> </div>
<!-- 子评论列表 --> <!-- 子评论列表 -->
<ul v-if="comment.replies && comment.replies.length > 0" class="replies-list"> <ul v-if="comment.showReplies && comment.replies && comment.replies.length > 0" class="replies-list">
<li v-for="reply in comment.replies" :key="reply.id" class="comment-item reply-item"> <li v-for="reply in comment.replies" :key="reply.id" class="comment-item reply-item">
<img :src="reply.userAvatar || require('@/assets/default-avatar/boy_1.png')" alt="评论者头像" class="comment-avatar" /> <img
:src="reply.userAvatar || require('@/assets/default-avatar/boy_1.png')"
alt="评论者头像"
class="comment-avatar"
@click="goUserHome(reply.userId)"
style="cursor: pointer;"
/>
<div class="comment-content"> <div class="comment-content">
<p class="comment-name"> <p class="comment-name">
{{ reply.userName || '匿名用户' }} {{ reply.userName || '匿名用户' }}
@ -89,10 +107,11 @@
<script setup lang="js" name="PostDetail"> <script setup lang="js" name="PostDetail">
import { ref, computed, onMounted , onUnmounted, watch } from 'vue'; import { ref, computed, onMounted , onUnmounted, watch } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute,useRouter } from 'vue-router';
import { usePostDetailStore } from '@/stores/postdetail.js'; import { usePostDetailStore } from '@/stores/postdetail.js';
import { useUserStore } from '@/stores/user.js'; import { useUserStore } from '@/stores/user.js';
const router = useRouter();
const route = useRoute(); const route = useRoute();
const postDetailStore = usePostDetailStore(); const postDetailStore = usePostDetailStore();
const userStore = useUserStore(); const userStore = useUserStore();
@ -106,7 +125,9 @@ const isFollowing = ref(false);
const toggleFollow = () => { const toggleFollow = () => {
isFollowing.value = !isFollowing.value; isFollowing.value = !isFollowing.value;
}; };
const goUserHome = (userId) => {
if (userId) router.push({name: 'UserPage',params: { userId: userId }})
}
function startReply(comment) { function startReply(comment) {
replyingComment.value = comment; replyingComment.value = comment;
} }
@ -132,7 +153,14 @@ function handleScroll() {
} }
} }
function loadReplies(comment) { function loadReplies(comment) {
if (!comment.repliesLoading && !comment.repliesFinished) { //
if (comment.showReplies) {
comment.showReplies = false;
return;
}
//
comment.showReplies = true;
if (!comment.repliesLoading && !comment.repliesFinished && comment.replies.length === 0) {
postDetailStore.fetchReplies(comment.id, comment); postDetailStore.fetchReplies(comment.id, comment);
} }
} }
@ -157,7 +185,9 @@ const sendComment = () => {
topId: replyingComment.value ? replyingComment.value.topId : null, topId: replyingComment.value ? replyingComment.value.topId : null,
isLike: false, isLike: false,
} }
postDetailStore.addComment(newCommentData); console.log('回复:', replyingComment.value);
console.log('发送评论:', newCommentData);
postDetailStore.sendComment(newCommentData);
newComment.value = ''; newComment.value = '';
} }
}; };

Loading…
Cancel
Save