Files
rating/pages/mine/topics.vue
2026-06-08 10:57:20 +08:00

237 lines
5.7 KiB
Vue

<template>
<view class="page-container">
<!-- 顶部导航栏 -->
<view class="nav-bar" :style="{ paddingTop: statusBarHeight + 'px' }">
<view class="nav-content">
<view class="nav-left" @tap="goBack">
<text class="back-icon"></text>
<text class="nav-title">我参与的话题</text>
</view>
</view>
</view>
<!-- 占位防止内容被导航栏遮挡 -->
<view :style="{ height: statusBarHeight + 44 + 'px' }"></view>
<!-- 话题列表 -->
<view class="topic-list-wrap">
<view v-if="topics.length > 0">
<TopicCard
v-for="(item, index) in topics"
:key="item.id || index"
:topic="item"
@card-click="goToTopicDetail(item.id)"
/>
</view>
<!-- 空状态 -->
<view class="empty-state" v-if="!loading && topics.length === 0">
<text class="empty-icon">📝</text>
<text class="empty-text">你还没有参与任何话题</text>
<view class="empty-btn" @tap="goToHome">去逛逛</view>
</view>
</view>
<!-- 加载更多指示器 -->
<view class="load-more" v-if="topics.length > 0">
<text>{{ loading ? '加载中...' : (hasMore ? '上拉加载更多' : '没有更多了') }}</text>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue';
import { getStatusBarHeight } from "@/utils/system";
import { onLoad, onPullDownRefresh, onReachBottom, onShow } from "@dcloudio/uni-app";
import { fetchUserTopics } from "@/api/topic";
import TopicCard from "@/components/TopicCard/TopicCard.vue";
const statusBarHeight = ref(getStatusBarHeight() || 44);
const topics = ref([]);
const loading = ref(false);
const hasMore = ref(true);
const currentPage = ref(1);
const pageSize = 5; // 每次获取5个
const goBack = () => {
uni.navigateBack();
};
const goToHome = () => {
uni.switchTab({
url: '/pages/index/index'
});
};
const goToTopicDetail = (topicId) => {
uni.navigateTo({
url: `/pages/rating/index?topicId=${topicId}`
});
};
const loadData = async (page = 1) => {
if (loading.value) return;
try {
loading.value = true;
const res = await fetchUserTopics(page);
const list = res?.data?.records || res?.records || res?.list || [];
// 如果是第一页,先清空原有数据
if (page === 1) {
topics.value = [];
}
// 构造适合 TopicCard 渲染的数据格式
const formattedList = list.map(item => ({
id: item.topicId || item.id,
title: item.topicName || item.title || '未知话题',
image: item.image || item.coverUrl || 'https://api.dicebear.com/7.x/shapes/svg?seed=' + (item.topicId || item.id), // 给个默认图防止报错
views: item.participantCount || item.views || 0,
items: item.items || item.ratingItems || []
}));
topics.value = [...topics.value, ...formattedList];
// 判断是否有下一页
hasMore.value = list.length >= pageSize;
} catch (error) {
console.error("获取参与话题列表失败:", error);
uni.showToast({
title: '获取列表失败',
icon: 'none'
});
if (page === 1) {
hasMore.value = false;
}
} finally {
loading.value = false;
}
};
onLoad(() => {
// onLoad 只触发一次
});
onShow(() => {
// 每次进入页面都刷新,确保数据是最新的(如果刚才在详情页做了评分)
loadData(1);
});
onPullDownRefresh(async () => {
currentPage.value = 1;
hasMore.value = true;
await loadData(1);
uni.stopPullDownRefresh();
});
onReachBottom(() => {
if (!hasMore.value || loading.value) return;
currentPage.value += 1;
loadData(currentPage.value);
});
</script>
<style lang="scss" scoped>
.page-container {
min-height: 100vh;
background-color: #f5f6fa;
padding-bottom: 60rpx;
}
/* 导航栏 */
.nav-bar {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 100;
background-color: #f5f6fa;
backdrop-filter: blur(20rpx);
}
.nav-content {
height: 44px;
display: flex;
align-items: center;
padding: 0 32rpx;
}
.nav-left {
display: flex;
align-items: center;
}
.back-icon {
width: 40rpx;
height: 40rpx;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23333' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='19' y1='12' x2='5' y2='12'%3E%3C/line%3E%3Cpolyline points='12 19 5 12 12 5'%3E%3C/polyline%3E%3C/svg%3E");
background-size: cover;
margin-right: 16rpx;
}
.nav-title {
font-size: 34rpx;
font-weight: 700;
color: #111;
font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Helvetica, Segoe UI, Arial, Roboto, 'PingFang SC', 'miui', 'Hiragino Sans GB', 'Microsoft Yahei', sans-serif;
}
/* 列表区 */
.topic-list-wrap {
padding: 24rpx 32rpx;
display: flex;
flex-direction: column;
gap: 24rpx; /* 组件之间的间距 */
}
/* 覆盖 TopicCard 的外边距,让外层控制间距 */
:deep(.topic-card) {
margin-bottom: 24rpx !important;
}
/* 空状态 */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 160rpx 0;
}
.empty-icon {
font-size: 100rpx;
margin-bottom: 24rpx;
opacity: 0.8;
}
.empty-text {
font-size: 28rpx;
color: #888;
margin-bottom: 48rpx;
}
.empty-btn {
background: linear-gradient(135deg, #4d44f1, #2953ff);
color: #fff;
font-size: 28rpx;
font-weight: 600;
padding: 16rpx 64rpx;
border-radius: 100rpx;
box-shadow: 0 8rpx 24rpx rgba(41, 83, 255, 0.25);
}
.empty-btn:active {
transform: scale(0.95);
transition: transform 0.2s;
}
/* 加载更多 */
.load-more {
text-align: center;
padding: 20rpx 0 40rpx;
color: #999;
font-size: 24rpx;
}
</style>