diff --git a/pages/notes/detail.vue b/pages/notes/detail.vue index 1b316d5..a6c3eb6 100644 --- a/pages/notes/detail.vue +++ b/pages/notes/detail.vue @@ -89,57 +89,139 @@ >评论 ({{ noteDetail.commentCount || 0 }}) - - - - - {{ comment.nickname }} - {{ comment.formatTime }} + + + + + {{ comment.nickname }} + + {{ comment.content }} + + {{ comment.formatTime }} + 回复 + + + + + + + + + {{ reply.nickname }} + 回复 @{{ reply.toNickname }} + + {{ reply.content }} + + {{ reply.formatTime }} + 回复 + + + + + + + + + 展开更多 + + + + 展开{{ comment.commentCount }}条回复 + + + + 收起 + + + + - {{ comment.content }} - - - - 加载中... - - - 没有更多评论了 - - - 暂无评论,快来发表第一条评论吧 - - - + + + 加载中... + + + 没有更多评论了 + + + 暂无评论,快来发表第一条评论吧 + + + + + item.id === commentId + ); + let comment = this.noteDetail.comments[index]; + if (!comment) return; + + // 除了点击"展开更多"的情况外,其他情况都应该从第1页开始加载 + if (!isLoadMore) { + // 重置页码为1,确保从第一页开始加载 + if (!comment.replyPageNum) { + this.$set(comment, "replyPageNum", 1); + } else { + comment.replyPageNum = 1; + } + + if (!comment.replies) { + this.$set(comment, "replies", []); + } else if (!isNewComment) { + // 只有在非新评论提交的情况下才清空回复列表 + // 如果是新评论提交,保留现有回复列表,新评论会添加到列表开头 + comment.replies = []; + } + + // 设置hasMoreReplies为true,确保可以加载更多回复 + this.$set(comment, "hasMoreReplies", true); + } + + // 如果没有更多回复,直接返回 + if (isLoadMore && !comment.hasMoreReplies) return; + + const res = await this.Post( + { + parentId: commentId, + noteId: this.noteId, + pageNum: comment.replyPageNum, + pageSize: 5, // 每页加载5条回复 + }, + "/framework/comment/pageList", + "DES" + ); + + if (res.code === 200) { + const newReplies = res.rows || []; + + // 更新回复列表 + if (isLoadMore) { + comment.replies = [...comment.replies, ...newReplies]; + } else { + // 非加载更多的情况(包括新评论提交和首次加载),直接使用新获取的评论列表 + // 这确保了页码为1时的数据总是最新的 + comment.replies = newReplies; + } + + // 更新回复总数 + const newCommentCount = res.total || 0; + comment.commentCount = newCommentCount; + + // 判断是否还有更多回复 + comment.hasMoreReplies = + comment.replies.length < comment.commentCount; + + // 如果有更多回复,页码加1 + if (comment.hasMoreReplies) { + comment.replyPageNum++; + } + this.$forceUpdate(); + + // 如果是新评论提交,更新一级评论列表中的评论计数 + if (isNewComment) { + // 查找一级评论列表中的对应评论,确保评论计数同步 + const mainComment = this.noteDetail.comments.find( + (item) => item.id === commentId + ); + if (mainComment) { + mainComment.commentCount = newCommentCount; + } + } + } else { + uni.showToast({ + title: res.msg || "加载回复失败", + icon: "none", + }); + } + } catch (error) { + console.error("加载二级评论失败:", error); + uni.showToast({ + title: "加载回复失败", + icon: "none", + }); + } finally { + uni.hideLoading(); + } + }, + + // 切换显示/隐藏二级评论 + toggleReplies(commentId) { + const comment = this.noteDetail.comments.find( + (item) => item.id === commentId + ); + if (!comment) return; + + // 如果已经加载过回复,直接切换显示状态 + if (this.showRepliesMap[commentId] === undefined) { + this.$set(this.showRepliesMap, commentId, true); + // 加载二级评论 + this.loadReplies(commentId); + } else if (this.showRepliesMap[commentId]) { + // 如果当前是展开状态,则收起 + this.$set(this.showRepliesMap, commentId, false); + } else { + // 如果当前是收起状态,则展开 + this.$set(this.showRepliesMap, commentId, true); + // 如果已经有回复数据,不需要重新加载 + if (!comment.replies || comment.replies.length === 0) { + this.loadReplies(commentId); + } + } + }, // 加载笔记详情 async loadNoteDetail() { try { @@ -285,6 +497,11 @@ export default { ); if (res.code === 200) { + if (res.rows) { + res.rows.forEach((comment) => { + this.$set(comment, "replies", []); + }); + } // 更新评论列表 if (isLoadMore) { // 加载更多时,追加评论 @@ -296,10 +513,8 @@ export default { // 首次加载或刷新时,替换评论 this.noteDetail.comments = res.rows || []; } - // 更新评论数量 this.noteDetail.commentCount = res.total || 0; - // 判断是否还有更多评论 this.hasMoreComments = this.noteDetail.comments.length < res.total; @@ -307,6 +522,7 @@ export default { if (this.hasMoreComments) { this.pageNum++; } + console.log(this.noteDetail.comments); } else { console.error("加载评论列表失败:", res.msg); uni.showToast({ @@ -446,12 +662,22 @@ export default { try { uni.showLoading({ title: "提交中..." }); + const params = { + noteId: this.noteId, + content: this.commentText, + method: "POST", + }; + + // 如果是回复评论,添加相关参数 + if (this.parentId) { + params.parentId = this.parentId; + params.toUserId = this.toUserId; + params.toHeadImg = this.toHeadImg; + params.toNickname = this.toNickname; + } + const res = await this.Post( - { - noteId: this.noteId, - content: this.commentText, - method: "POST", - }, + params, "/framework/comment/addComment", "DES" ); @@ -459,8 +685,27 @@ export default { // 清空评论输入框 this.commentText = ""; - // 评论成功后只加载评论列表,不需要重新加载整个笔记详情 - await this.loadCommentList(); + // 清空回复信息 + const parentIdCopy = this.parentId; + this.parentId = null; + this.toUserId = null; + this.toHeadImg = ""; + this.toNickname = ""; + // 关闭文本域模式 + this.showTextarea = false; + + // 如果是回复评论,只刷新该评论的二级评论列表 + if (parentIdCopy) { + // 刷新二级评论列表,传递isNewComment=true表示这是新提交的评论 + // 传递isLoadMore=false确保从第一页开始加载二级评论 + await this.loadReplies(parentIdCopy, false, true); + + // 确保展开二级评论 + this.$set(this.showRepliesMap, parentIdCopy, true); + } else { + // 如果是一级评论,才重新加载评论列表 + await this.loadCommentList(); + } uni.showToast({ title: "评论成功", @@ -516,6 +761,34 @@ export default { // url: `/pages/tags/detail?tag=${encodeURIComponent(tag)}` // }); }, + + // 回复评论 + replyToComment(comment) { + // 如果是回复二级评论,需要设置正确的parentId(一级评论的ID) + if (comment.parentId) { + // 这是二级评论,parentId应该是其父评论的ID + this.parentId = comment.parentId; + } else { + // 这是一级评论,直接使用其ID + this.parentId = comment.id; + } + + this.toUserId = comment.userId; + this.toHeadImg = comment.headImg; + this.toNickname = comment.nickname; + + // 在uni-app中,需要先将焦点状态重置为false,然后再设置为true才能生效 + this.inputFocusState = false; + + // 确保DOM更新后再尝试聚焦 + this.$nextTick(() => { + console.log(this.$refs.inputComment); + // 添加延时确保元素已完全渲染 + setTimeout(() => { + this.inputFocusState = true; + }, 100); + }); + }, }, }; @@ -659,41 +932,160 @@ export default { } .comment-item { - display: flex; margin-bottom: 32rpx; - .comment-avatar { - width: 64rpx; - height: 64rpx; - border-radius: 32rpx; - margin-right: 24rpx; + .main-comment { + display: flex; + + .comment-avatar { + width: 64rpx; + height: 64rpx; + border-radius: 32rpx; + margin-right: 24rpx; + } + + .comment-content { + flex: 1; + + .comment-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 12rpx; + + .comment-user { + font-size: 30rpx; + font-weight: bold; + color: #000000; + } + + .comment-time { + font-size: 26rpx; + color: #999; + } + } + + .comment-text { + font-size: 28rpx; + line-height: 1.5; + color: #000000; + margin-bottom: 12rpx; + } + + .comment-footer { + display: flex; + justify-content: space-between; + align-items: center; + + .comment-time { + font-size: 26rpx; + color: #999; + } + + .reply-btn { + font-size: 26rpx; + color: #666; + padding: 6rpx 12rpx; + border-radius: 4rpx; + + &:active { + background-color: #f0f0f0; + } + } + } + + .toggle-replies { + margin-top: 16rpx; + + text { + font-size: 28rpx; + color: #666; + padding: 6rpx 0; + } + } + } } + .load-more-replies { + margin-right: 20rpx; - .comment-content { + text { + font-size: 28rpx; + color: #666; + padding: 6rpx 0; + } + } + } +} +.replies-container { + margin-top: 16rpx; + + .reply-item { + display: flex; + margin-bottom: 20rpx; + + .reply-avatar { + width: 56rpx; + height: 56rpx; + border-radius: 28rpx; + margin-right: 16rpx; + } + + .reply-content { flex: 1; - .comment-header { + .reply-header { display: flex; - justify-content: space-between; align-items: center; - margin-bottom: 12rpx; + margin-bottom: 8rpx; - .comment-user { - font-size: 30rpx; + .reply-user { + font-size: 28rpx; font-weight: bold; color: #000000; + margin-right: 8rpx; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + max-width: 250rpx; } - .comment-time { + .reply-to { font-size: 26rpx; - color: #999; + color: #666; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + max-width: 250rpx; } } - .comment-text { - font-size: 28rpx; + .reply-text { + font-size: 26rpx; line-height: 1.5; color: #000000; + margin-bottom: 8rpx; + } + + .reply-footer { + display: flex; + justify-content: space-between; + align-items: center; + + .reply-time { + font-size: 24rpx; + color: #999; + } + + .reply-btn { + font-size: 26rpx; + color: #666; + padding: 6rpx 12rpx; + border-radius: 4rpx; + + &:active { + background-color: #f0f0f0; + } + } } } } @@ -726,6 +1118,13 @@ export default { margin-bottom: max(env(safe-area-inset-bottom), 24rpx); } + .cancel-reply { + font-size: 28rpx; + color: #666; + margin-right: 16rpx; + margin-bottom: max(env(safe-area-inset-bottom), 24rpx); + } + .send-btn { width: 140rpx; height: 67rpx;