fix: youhua

This commit is contained in:
zzc
2026-06-10 01:24:41 +08:00
parent 1e5bbbbf9e
commit aa88eef08e
3 changed files with 164 additions and 26 deletions

View File

@@ -20,10 +20,13 @@
class="comment-input"
type="text"
v-model="commentText"
:disabled="sending"
placeholder="说点什么吧..."
placeholder-class="input-placeholder"
/>
<button class="send-btn" @tap="handleSend">发送</button>
<button class="send-btn" :class="{ 'is-disabled': sending }" :disabled="sending" @tap="handleSend">
{{ sending ? '发送中...' : '发送' }}
</button>
</view>
</view>
</template>
@@ -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
});
</script>
<style lang="scss" scoped>
@@ -162,7 +178,11 @@ const handleSend = () => {
margin: 0;
}
.send-btn.is-disabled {
opacity: 0.7;
}
.send-btn::after {
border: none;
}
</style>
</style>

View File

@@ -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;

View File

@@ -47,7 +47,9 @@
<!-- 你的态度 -->
<AttitudePanel
ref="attitudePanelRef"
:initial-action="currentAction"
:sending="isSendingComment"
@action-change="handleActionChange"
@send-comment="handleSendComment"
/>
@@ -89,18 +91,32 @@
<!-- <text class="filter-icon"></text> -->
</view>
<view class="comment-list" style="min-height: 400rpx;">
<view class="comment-list">
<view v-if="commentLoading && !comments.length" class="comment-state">
<text class="comment-state-title">评论加载中...</text>
<text class="comment-state-desc">正在拉取大家的锐评</text>
</view>
<view v-else-if="commentError && !comments.length" class="comment-state">
<text class="comment-state-title">评论加载失败</text>
<text class="comment-state-desc">{{ commentError }}</text>
<button class="comment-state-btn" @tap="retryLoadComments">重新加载</button>
</view>
<view v-else-if="!comments.length" class="comment-state">
<text class="comment-state-title">还没有评论</text>
<text class="comment-state-desc">来发第一条锐评带动一下讨论气氛</text>
</view>
<CommentItem
v-for="(comment, index) in comments"
:key="comment.id"
:comment="comment"
@need-login="openLoginPopup"
@like-change="handleCommentLikeChange"
/>
</view>
<!-- 加载更多 -->
<view class="load-more">
<text>{{ loading ? '加载中...' : (hasMore ? '上拉加载更多' : '没有更多了') }}</text>
<view v-if="comments.length" class="load-more">
<text>{{ commentLoadText }}</text>
</view>
</view>
</view>
@@ -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 {