继续完善帖子查询

main
lee-zt 6 days ago
parent 61cc9965ff
commit 83ebb5436e

@ -1,39 +1,117 @@
import { defineStore } from "pinia";
import request from "@/utils/request";
import { ElMessage } from "element-plus";
export const usePostDetailStore = defineStore("postDetail", {
state: () => ({
post: null, // 帖子主要信息
comments: [], // 评论列表
userInfo: null, // 用户信息
totalComments: 0, // 评论总数
likeCount: 0, // 点赞数
commentCount: 0, // 评论数
favoriteCount: 0, // 收藏数
viewCount: 0, // 浏览数
isLike: false, // 是否点赞
loading: false, // 加载状态
lastVal: 0, // 用于滚动分页的时间戳
offset: 0, // 偏移量
size: 5, // 每页评论数
detailLoading: false, // 加载状态
commentsLoading: false, // 评论加载状态
commentsFinished: false, // 是否加载完全部评论
}),
actions: {
setPost(post) {
this.post = post;
},
setComments(comments) {
this.comments = comments;
this.totalComments = comments.length;
},
setUserInfo(userInfo) {
this.userInfo = userInfo;
async fetchComments() {
if (this.commentsLoading || this.commentsFinished) return;
this.commentsLoading = true;
const RequestCommentData = {
lastVal: this.lastVal,
offset: this.offset,
size: this.size,
postId: this.post.postId
};
try {
const response = await request.post('/comment/list', RequestCommentData);
if (response.code === 200) {
// 初始化每条评论的子评论分页状态
const comments = (response.data.records || []).map(item => ({
...item,
replies: [],
repliesLastVal: Date.now(),
repliesOffset: 0,
repliesSize: 5,
repliesLoading: false,
repliesFinished: false,
}));
this.comments.push(...comments);
this.lastVal = response.data.lastVal;
this.offset = response.data.offset;
if (comments.length < this.size) {
this.commentsFinished = true; // 如果评论数少于每页大小,标记为已加载完
}
}
else {
ElMessage({
message: '获取评论失败,请稍后重试',
type: 'error',
duration: 500
});
}
} catch (error) {
console.error("获取评论失败:", error);
alert(error.response?.message || '获取评论失败,请稍后重试');
}finally {
this.commentsLoading = false;
}
},
addComment(comment) {
this.comments.push(comment);
this.totalComments += 1;
// 获取某条评论的子评论(多级结构,分页)
async fetchReplies(parentCommentId, commentObj) {
if (commentObj.repliesLoading || commentObj.repliesFinished) return;
commentObj.repliesLoading = true;
// 请求子评论数据
const RequestReplyData = {
lastVal: commentObj.repliesLastVal,
offset: commentObj.repliesOffset,
size: commentObj.repliesSize,
postId: this.post?.postId,
parentCommentId: parentCommentId,
};
try {
const res = await request.post('/comment/list/reply', RequestReplyData);
if (res.code === 200) {
const records = (res.data.records || []).map(item => ({
...item,
replies: [],
repliesLastVal: 0,
repliesOffset: 0,
repliesSize: 5,
repliesFinished: false,
repliesLoading: false,
}));
commentObj.replies.push(...records);
commentObj.repliesLastVal = res.data.lastVal;
commentObj.repliesOffset = res.data.offset;
if (records.length < commentObj.repliesSize) {
commentObj.repliesFinished = true;
}
}
else {
ElMessage({
message: '获取子评论失败,请稍后重试',
type: 'error',
duration: 500
});
}
} catch (error) {
console.error("获取子评论失败:", error);
alert(error.response?.message || '获取子评论失败,请稍后重试');
} finally {
commentObj.repliesLoading = false;
}
},
async fetchPostDetail(postId, { lastVal = Date.now(), offset = 0, size = 10 } = {}) {
this.loading = true;
async fetchPostDetail(postId) {
this.detailLoading = true;
try {
// 获取帖子详情
const postRes = await request.get(`/post/detail/${postId}`);
const RequestPostDetailData = {
id: postId,
};
const postRes = await request.get('/post/detail/', RequestPostDetailData);
if (postRes.code === 200 && postRes.data) {
const {
id,
@ -58,6 +136,10 @@ export const usePostDetailStore = defineStore("postDetail", {
title,
content,
createTime,
commentCount,
likeCount,
favoriteCount,
viewCount,
};
// 用户信息
@ -67,27 +149,24 @@ export const usePostDetailStore = defineStore("postDetail", {
userAvatar: userAvatar,
followers:1234,//先预设粉丝占位
};
// 其余字段
this.likeCount = likeCount;
this.commentCount = commentCount;
this.favoriteCount = favoriteCount;
this.viewCount = viewCount;
this.isLike = isLike;
// 获取评论列表
this.lastVal = Date.now();
this.fetchComments();
}
// 获取评论列表
const commentRes = await request.post('/comment/list', {
lastVal,
offset,
size,
postId
});
if (commentRes.code === 200) {
this.setComments(commentRes.data.records || []);
}
} finally {
this.loading = false;
else {
ElMessage({
message: '获取帖子详情失败,请稍后重试',
type: 'error',
duration: 500
});
}
} catch (error) {
console.error("获取帖子详情失败:", error);
alert(error.response?.message || '获取帖子详情失败,请稍后重试');
}finally {
this.detailLoading = false;
}
},
},

@ -1,5 +1,6 @@
import {defineStore} from 'pinia';
import request from '@/utils/request';
import { ElMessage } from 'element-plus';
export const usePostListStore = defineStore('postList', {
state: () => ({
@ -33,18 +34,24 @@ export const usePostListStore = defineStore('postList', {
this.posts = this.posts.filter(post => post.id !== postId);
this.total -= 1; // 更新总数
},
async getList({ lastVal = this.lastVal, offset = this.offset, size = this.pageSize } = {}) {
async getList() {
if (this.loading || this.finished) return;
this.loading = true;
const requestData = {
lastVal:this.lastVal,
offset: this.offset,
size: this.pageSize,
};
try {
const res = await request.post('/post/list', { lastVal, offset, size });
const res = await request.post('/post/list', requestData);
if (res.code === 200) {
const { records, lastVal: newLastVal, offset: newOffset, size: newSize } = res.data;
if (records.length > 0) {
// 字段映射
const mappedRecords = records.map(post => ({
id: post.id,
avatar: post.userAvatar || post.image || require('@/assets/default-avatar/boy_1.png'),
image: post.image,
avatar: post.userAvatar ,
title: post.title,
summary: post.content ? post.content.slice(0, 40) + (post.content.length > 40 ? '...' : '') : '',
likes: post.likeCount,
@ -59,11 +66,23 @@ export const usePostListStore = defineStore('postList', {
this.offset = newOffset;
this.pageSize = newSize;
}
if (records.length < size) {
if (records.length < this.pageSize) {
this.finished = true; // 没有更多数据
}
}
} finally {
else {
// 登录失败
ElMessage({
message: '获取帖子列表失败,请稍后重试',
type: 'error',
duration: 500
});
}
}catch (error) {
console.error("获取帖子列表失败:", error);
console.error('获取失败', error);
alert(error.response?.message || '获取失败,请稍后重试');
}finally {
this.loading = false;
}
},

@ -35,11 +35,25 @@
<img :src="comment.userAvatar || require('@/assets/default-avatar/boy_1.png')" alt="评论者头像" class="comment-avatar" />
<div class="comment-content">
<p class="comment-name">{{ comment.userName }}</p>
<p class="comment-text">{{ comment.content }}</p>
<div class="comment-meta">
<span class="comment-time">{{ comment.createTime ? formatTime(comment.createTime) : '' }}</span>
<span class="comment-likes"> {{ comment.likeCount ?? 0 }}</span>
</div>
<p class="comment-text">{{ comment.content }}</p>
<div class="comment-meta">
<span class="comment-time">{{ comment.createTime ? formatTime(comment.createTime) : '' }}</span>
<span class="comment-likes"> {{ comment.likeCount ?? 0 }}</span>
<span v-if="comment.replyCount > 0">
<button @click="loadReplies(comment)">
{{ comment.replies.length > 0 ? '收起回复' : '展开回复' }} ({{ comment.replyCount }})
</button>
</span>
</div>
<!-- 子评论列表 -->
<ul v-if="comment.replies.length > 0" class="replies-list">
<li v-for="reply in comment.replies" :key="reply.id" class="reply-item">
<span class="reply-user">{{ reply.userName }}</span>
<span class="reply-content">{{ reply.content }}</span>
</li>
<li v-if="comment.repliesLoading" class="reply-loading">...</li>
<li v-if="comment.repliesFinished" class="reply-finished"></li>
</ul>
</div>
</li>
</ul>
@ -58,7 +72,7 @@
</template>
<script setup lang="js" name="PostDetail">
import { ref, onMounted } from 'vue';
import { ref, onMounted , onUnmounted} from 'vue';
import { useRoute } from 'vue-router';
import { usePostDetailStore } from '@/stores/postdetail.js';
import { useUserStore } from '@/stores/user.js';
@ -81,6 +95,25 @@ function formatTime(timeStr) {
return `${date.getFullYear()}${date.getMonth() + 1}${date.getDate()}${date.getHours()}:${date.getMinutes().toString().padStart(2, '0')}`;
}
//
function handleScroll() {
const el = document.documentElement;
if (
el.scrollTop + window.innerHeight >= el.scrollHeight - 100 &&
!postDetailStore.commentsLoading &&
!postDetailStore.commentsFinished
) {
postDetailStore.fetchComments();
}
}
//
function loadReplies(comment) {
if (!comment.repliesLoading && !comment.repliesFinished) {
postDetailStore.fetchReplies(comment.id, comment);
}
}
//
const sendComment = () => {
if (!userStore.isLoggedIn) {
@ -89,7 +122,7 @@ const sendComment = () => {
}
if (newComment.value.trim()) {
postDetailStore.addComment({
userAvatar: userStore.userInfo.avatar || require('@/assets/default-avatar/boy_1.png'),
userAvatar: userStore.userInfo.avatar,
userName: userStore.userInfo.username,
userId: userStore.userInfo.userid,
content: newComment.value,
@ -102,6 +135,10 @@ const sendComment = () => {
onMounted(() => {
postDetailStore.fetchPostDetail(route.params.id);
window.addEventListener('scroll', handleScroll);
});
onUnmounted(() => {
window.removeEventListener('scroll', handleScroll);
});
</script>
@ -334,4 +371,38 @@ onMounted(() => {
text-align: left; /* 设置居左 */
color: #333;
}
.replies-list {
list-style: none;
padding-left: 40px; /* 缩进,区分主评论 */
margin: 8px 0 0 0;
}
.reply-item {
display: flex;
align-items: flex-start;
padding: 6px 0;
border-bottom: 1px solid #f3f3f3;
font-size: 13px;
color: #444;
}
.reply-user {
font-weight: bold;
margin-right: 6px;
color: #5aa76f;
}
.reply-content {
flex: 1;
color: #333;
}
.reply-loading,
.reply-finished {
color: #999;
font-size: 12px;
padding: 6px 0;
text-align: left;
}
</style>
Loading…
Cancel
Save