From aa88eef08ee84152800712eee0cd2d6be2bd5de0 Mon Sep 17 00:00:00 2001
From: zzc <1761997216@qq.com>
Date: Wed, 10 Jun 2026 01:24:41 +0800
Subject: [PATCH] fix: youhua
---
components/AttitudePanel/AttitudePanel.vue | 30 ++++-
components/CommentItem/CommentItem.vue | 13 +-
pages/rating/detail.vue | 147 ++++++++++++++++++---
3 files changed, 164 insertions(+), 26 deletions(-)
diff --git a/components/AttitudePanel/AttitudePanel.vue b/components/AttitudePanel/AttitudePanel.vue
index 5e91f5c..22137d5 100644
--- a/components/AttitudePanel/AttitudePanel.vue
+++ b/components/AttitudePanel/AttitudePanel.vue
@@ -20,10 +20,13 @@
class="comment-input"
type="text"
v-model="commentText"
+ :disabled="sending"
placeholder="说点什么吧..."
placeholder-class="input-placeholder"
/>
-
+
@@ -35,6 +38,10 @@ const props = defineProps({
initialAction: {
type: String,
default: ''
+ },
+ sending: {
+ type: Boolean,
+ default: false
}
});
@@ -62,19 +69,28 @@ const handleAction = (action) => {
};
const handleSend = () => {
- if (!commentText.value.trim()) {
+ const content = commentText.value.trim();
+ if (!content) {
uni.showToast({
title: '请输入评论内容',
icon: 'none'
});
return;
}
+ if (props.sending) return;
emit('send-comment', {
action: currentAction.value,
- content: commentText.value
+ content
});
- commentText.value = ''; // 发送后清空
};
+
+const resetComment = () => {
+ commentText.value = '';
+};
+
+defineExpose({
+ resetComment
+});
\ No newline at end of file
+
diff --git a/components/CommentItem/CommentItem.vue b/components/CommentItem/CommentItem.vue
index 46d6721..0c586f6 100644
--- a/components/CommentItem/CommentItem.vue
+++ b/components/CommentItem/CommentItem.vue
@@ -37,7 +37,7 @@ const props = defineProps({
}
});
-const emit = defineEmits(['need-login']);
+const emit = defineEmits(['need-login', 'like-change']);
const userStore = useUserStore();
const isLoggedIn = computed(() => !!userStore.token || !!userStore.userInfo?.nickName);
@@ -70,6 +70,11 @@ const handleLike = async () => {
} else {
currentLikes.value -= 1;
}
+ emit('like-change', {
+ id: props.comment.id,
+ isLiked: isLiked.value,
+ likes: currentLikes.value
+ });
try {
// 调用点赞接口 (点赞和取消点赞为同一个接口)
@@ -85,7 +90,6 @@ const handleLike = async () => {
page: 'rating_detail'
}
});
- uni.showToast({ title: '操作成功', icon: 'success' });
} catch (error) {
// 接口调用失败,回滚 UI 状态
isLiked.value = !isLiked.value;
@@ -94,6 +98,11 @@ const handleLike = async () => {
} else {
currentLikes.value -= 1;
}
+ emit('like-change', {
+ id: props.comment.id,
+ isLiked: isLiked.value,
+ likes: currentLikes.value
+ });
uni.showToast({ title: '操作失败', icon: 'none' });
} finally {
isLiking = false;
diff --git a/pages/rating/detail.vue b/pages/rating/detail.vue
index 823d18a..880b377 100644
--- a/pages/rating/detail.vue
+++ b/pages/rating/detail.vue
@@ -47,7 +47,9 @@
@@ -89,18 +91,32 @@
-
@@ -127,14 +143,22 @@ import { getShareReward } from "@/api/system";
import { useUserStore } from "@/stores/user";
const userStore = useUserStore();
const statusBarHeight = ref(getStatusBarHeight() || 44);
-const loading = ref(false);
const hasMore = ref(true);
const currentPage = ref(1);
const currentAction = ref('');
const successPopupRef = ref(null);
const loginPopupRef = ref(null);
+const attitudePanelRef = ref(null);
const currentTab = ref('hot'); // 'hot' 或 'new'
const isLoggedIn = computed(() => !!userStore.token || !!userStore.userInfo?.nickName);
+const commentLoading = ref(false);
+const isSendingComment = ref(false);
+const commentError = ref('');
+const commentLoadText = computed(() => {
+ if (commentLoading.value && comments.value.length) return '加载中...';
+ if (!hasMore.value) return '没有更多了';
+ return '上拉加载更多';
+});
const goBack = () => {
uni.navigateBack();
@@ -215,9 +239,10 @@ const handleSendComment = async (payload) => {
return;
}
- if (!detailData.value.topicId || !itemId.value) return;
+ if (!detailData.value.topicId || !itemId.value || isSendingComment.value) return;
try {
+ isSendingComment.value = true;
uni.showLoading({ title: '发送中...' });
await topicItemComment({
topicId: detailData.value.topicId,
@@ -237,7 +262,6 @@ const handleSendComment = async (payload) => {
// 刷新页面数据以获取最新评论
currentPage.value = 1;
hasMore.value = true;
- comments.value = [];
uni.$trackRecord({
eventName: 'comment',
eventType: 'comment',
@@ -248,13 +272,16 @@ const handleSendComment = async (payload) => {
page: 'rating_detail'
}
});
- getCommentsData();
+ attitudePanelRef.value?.resetComment?.();
+ await getCommentsData({ replace: true });
} catch (error) {
uni.hideLoading();
uni.showToast({
title: '发送失败',
icon: 'none'
});
+ } finally {
+ isSendingComment.value = false;
}
};
@@ -263,16 +290,34 @@ const switchTab = (tab) => {
currentTab.value = tab;
currentPage.value = 1;
hasMore.value = true;
- // comments.value = []; // 移除这行,避免数据清空导致高度塌陷
- getCommentsData(true); // 传入标识表示是切换榜单
+ commentError.value = '';
+ getCommentsData({ replace: true });
};
-const getCommentsData = async (isSwitchTab = false) => {
- if (!detailData.value.topicId || !itemId.value || loading.value) return;
+const resolveCommentHasMore = (res, list) => {
+ const data = res?.data || res || {};
+ const hasNext = data?.hasNext ?? res?.hasNext;
+ if (typeof hasNext === 'boolean') return hasNext;
+
+ const pages = Number(data?.pages ?? res?.pages ?? 0);
+ if (pages > 0) return currentPage.value < pages;
+
+ const total = Number(data?.total ?? res?.total ?? 0);
+ if (total > 0) {
+ const size = Number(data?.size ?? data?.pageSize ?? res?.size ?? res?.pageSize ?? list.length || 1);
+ return currentPage.value * size < total;
+ }
+
+ return list.length > 0;
+};
+
+const getCommentsData = async ({ replace = false } = {}) => {
+ if (!detailData.value.topicId || !itemId.value || commentLoading.value) return;
try {
- loading.value = true;
- const orderBy = currentTab.value;
+ commentLoading.value = true;
+ commentError.value = '';
+ const orderBy = currentTab.value === 'hot' ? 1 : 2;
const res = await topicItemCommentList(detailData.value.topicId, itemId.value, currentPage.value, orderBy);
const rawList =
res?.data?.records ||
@@ -297,21 +342,42 @@ const getCommentsData = async (isSwitchTab = false) => {
userAction: item.userAction || "" // 夯/顶级/人上人/NPC/拉
}));
- if (currentPage.value === 1 || isSwitchTab) {
+ if (currentPage.value === 1 || replace) {
comments.value = formattedList;
} else {
comments.value = [...comments.value, ...formattedList];
}
- // 假设每页10条,如果返回的少于10条则没有更多了
- hasMore.value = list.length >= 6;
+ hasMore.value = resolveCommentHasMore(res, list);
} catch (error) {
console.error('获取评论列表失败:', error);
+ commentError.value = '请检查网络后重试';
+ if (currentPage.value > 1) {
+ currentPage.value -= 1;
+ }
} finally {
- loading.value = false;
+ commentLoading.value = false;
}
};
+const retryLoadComments = () => {
+ if (!comments.value.length) {
+ currentPage.value = 1;
+ }
+ getCommentsData({ replace: currentPage.value === 1 });
+};
+
+const handleCommentLikeChange = ({ id, isLiked, likes }) => {
+ comments.value = comments.value.map((item) => {
+ if (item.id !== id) return item;
+ return {
+ ...item,
+ isLiked,
+ likes
+ };
+ });
+};
+
const getDetailData = async () => {
if (!itemId.value) return;
try {
@@ -344,7 +410,7 @@ const getDetailData = async () => {
// 拉取真实的评论列表
if (currentPage.value === 1) {
- getCommentsData();
+ getCommentsData({ replace: true });
}
} catch (error) {
console.error('获取详情失败:', error);
@@ -377,6 +443,7 @@ const handleLoginSuccess = () => {
currentPage.value = 1;
hasMore.value = true;
comments.value = [];
+ commentError.value = '';
getDetailData();
};
@@ -405,11 +472,12 @@ onPullDownRefresh(() => {
currentPage.value = 1;
hasMore.value = true;
comments.value = [];
+ commentError.value = '';
getDetailData();
});
onReachBottom(() => {
- if (!hasMore.value || loading.value) return;
+ if (!hasMore.value || commentLoading.value) return;
currentPage.value += 1;
getCommentsData();
});
@@ -749,6 +817,47 @@ onReachBottom(() => {
.comment-list {
display: flex;
flex-direction: column;
+ min-height: 400rpx;
+}
+
+.comment-state {
+ min-height: 400rpx;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ text-align: center;
+ color: #999;
+ padding: 40rpx 32rpx;
+ box-sizing: border-box;
+}
+
+.comment-state-title {
+ font-size: 30rpx;
+ color: #222;
+ font-weight: 700;
+ margin-bottom: 12rpx;
+}
+
+.comment-state-desc {
+ font-size: 24rpx;
+ color: #9aa0ae;
+ line-height: 1.6;
+}
+
+.comment-state-btn {
+ margin-top: 24rpx;
+ height: 72rpx;
+ line-height: 72rpx;
+ padding: 0 32rpx;
+ border-radius: 999rpx;
+ background: #4d44f1;
+ color: #fff;
+ font-size: 26rpx;
+}
+
+.comment-state-btn::after {
+ border: none;
}
.load-more {