继续完善帖子查询

main
lee-zt 1 week ago
parent 61cc9965ff
commit 83ebb5436e

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

@ -1,5 +1,6 @@
import {defineStore} from 'pinia'; import {defineStore} from 'pinia';
import request from '@/utils/request'; import request from '@/utils/request';
import { ElMessage } from 'element-plus';
export const usePostListStore = defineStore('postList', { export const usePostListStore = defineStore('postList', {
state: () => ({ state: () => ({
@ -33,18 +34,24 @@ export const usePostListStore = defineStore('postList', {
this.posts = this.posts.filter(post => post.id !== postId); this.posts = this.posts.filter(post => post.id !== postId);
this.total -= 1; // 更新总数 this.total -= 1; // 更新总数
}, },
async getList({ lastVal = this.lastVal, offset = this.offset, size = this.pageSize } = {}) { async getList() {
if (this.loading || this.finished) return; if (this.loading || this.finished) return;
this.loading = true; this.loading = true;
const requestData = {
lastVal:this.lastVal,
offset: this.offset,
size: this.pageSize,
};
try { try {
const res = await request.post('/post/list', { lastVal, offset, size }); const res = await request.post('/post/list', requestData);
if (res.code === 200) { if (res.code === 200) {
const { records, lastVal: newLastVal, offset: newOffset, size: newSize } = res.data; const { records, lastVal: newLastVal, offset: newOffset, size: newSize } = res.data;
if (records.length > 0) { if (records.length > 0) {
// 字段映射 // 字段映射
const mappedRecords = records.map(post => ({ const mappedRecords = records.map(post => ({
id: post.id, id: post.id,
avatar: post.userAvatar || post.image || require('@/assets/default-avatar/boy_1.png'), image: post.image,
avatar: post.userAvatar ,
title: post.title, title: post.title,
summary: post.content ? post.content.slice(0, 40) + (post.content.length > 40 ? '...' : '') : '', summary: post.content ? post.content.slice(0, 40) + (post.content.length > 40 ? '...' : '') : '',
likes: post.likeCount, likes: post.likeCount,
@ -59,11 +66,23 @@ export const usePostListStore = defineStore('postList', {
this.offset = newOffset; this.offset = newOffset;
this.pageSize = newSize; this.pageSize = newSize;
} }
if (records.length < size) { if (records.length < this.pageSize) {
this.finished = true; // 没有更多数据 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; this.loading = false;
} }
}, },

@ -35,11 +35,25 @@
<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>
</div> <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> </div>
</li> </li>
</ul> </ul>
@ -58,7 +72,7 @@
</template> </template>
<script setup lang="js" name="PostDetail"> <script setup lang="js" name="PostDetail">
import { ref, onMounted } from 'vue'; import { ref, onMounted , onUnmounted} from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } 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';
@ -81,6 +95,25 @@ function formatTime(timeStr) {
return `${date.getFullYear()}${date.getMonth() + 1}${date.getDate()}${date.getHours()}:${date.getMinutes().toString().padStart(2, '0')}`; 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 = () => { const sendComment = () => {
if (!userStore.isLoggedIn) { if (!userStore.isLoggedIn) {
@ -89,7 +122,7 @@ const sendComment = () => {
} }
if (newComment.value.trim()) { if (newComment.value.trim()) {
postDetailStore.addComment({ postDetailStore.addComment({
userAvatar: userStore.userInfo.avatar || require('@/assets/default-avatar/boy_1.png'), userAvatar: userStore.userInfo.avatar,
userName: userStore.userInfo.username, userName: userStore.userInfo.username,
userId: userStore.userInfo.userid, userId: userStore.userInfo.userid,
content: newComment.value, content: newComment.value,
@ -102,6 +135,10 @@ const sendComment = () => {
onMounted(() => { onMounted(() => {
postDetailStore.fetchPostDetail(route.params.id); postDetailStore.fetchPostDetail(route.params.id);
window.addEventListener('scroll', handleScroll);
});
onUnmounted(() => {
window.removeEventListener('scroll', handleScroll);
}); });
</script> </script>
@ -334,4 +371,38 @@ onMounted(() => {
text-align: left; /* 设置居左 */ text-align: left; /* 设置居左 */
color: #333; 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> </style>
Loading…
Cancel
Save