Files
rating/pages/index/index.vue

345 lines
8.1 KiB
Vue
Raw Normal View History

2026-05-07 11:05:30 +08:00
<template>
<view class="home-container">
<!-- 顶部固定栏 (使用 padding-top 避开状态栏) -->
<view class="header-section" :style="{ paddingTop: statusBarHeight + 'px' }">
<view class="header-content">
<image
class="user-avatar"
:src="userInfo.avatarUrl || '/static/images/default-avatar.png'"
mode="aspectFill"
@tap="handleLogin"
/>
2026-05-07 11:09:32 +08:00
<view class="header-texts">
2026-06-09 23:55:11 +08:00
<text class="title">夯拉评分</text>
2026-05-07 11:09:32 +08:00
<text class="subtitle">看看全国网友怎么评分</text>
</view>
2026-05-07 11:05:30 +08:00
</view>
2026-05-07 11:09:32 +08:00
<!-- 顶部切换 tab -->
<scroll-view class="category-tabs" scroll-x :show-scrollbar="false">
<view class="tabs-wrapper">
<view
class="tab-item"
2026-05-21 06:56:42 +08:00
:class="{ active: currentCategoryId === category.id }"
v-for="category in categories"
:key="category.id"
@tap="switchCategory(category.id)"
2026-05-07 11:09:32 +08:00
>
2026-05-21 06:56:42 +08:00
<text class="tab-text">{{ category.title }}</text>
<view class="tab-line" v-if="currentCategoryId === category.id"></view>
2026-05-07 11:09:32 +08:00
</view>
</view>
</scroll-view>
2026-05-07 11:05:30 +08:00
</view>
2026-05-07 11:09:32 +08:00
<!-- 占位防止固定头部遮挡内容 (增加 tab 的高度) -->
<view :style="{ height: statusBarHeight + 104 + 'px' }"></view>
2026-05-07 11:05:30 +08:00
<!-- 列表内容 -->
<view class="topic-list">
2026-05-21 18:00:07 +08:00
<TopicCard
v-for="topic in topicList"
:key="topic.id"
:topic="topic"
@item-click="goToRating"
/>
2026-05-07 11:05:30 +08:00
<!-- 加载更多 -->
<view class="load-more">
<text>{{ loading ? '加载中...' : (hasMore ? '上拉加载更多' : '没有更多了') }}</text>
</view>
</view>
<!-- 登录弹窗 -->
<LoginPopup @logind="handleLoginSuccess" />
</view>
</template>
<script setup>
import { ref, computed } from "vue";
import { getStatusBarHeight } from "@/utils/system";
import {
onShareAppMessage,
onShareTimeline,
onShow,
onPullDownRefresh,
onReachBottom,
onLoad,
} from "@dcloudio/uni-app";
import { useUserStore } from "@/stores/user";
import { getShareReward } from "@/api/system";
2026-05-21 06:56:42 +08:00
import { fetchTopicList, fetchTopicCategories } from "@/api/topic";
2026-05-07 11:05:30 +08:00
import { getShareToken, saveViewRequest } from "@/utils/common.js";
import LoginPopup from "@/components/LoginPopup/LoginPopup.vue";
2026-05-21 18:00:07 +08:00
import TopicCard from "@/components/TopicCard/TopicCard.vue";
2026-05-07 11:05:30 +08:00
import adManager from "@/utils/adManager.js";
const userStore = useUserStore();
const statusBarHeight = ref(getStatusBarHeight());
const userInfo = computed(() => userStore?.userInfo || {});
const loading = ref(false);
const hasMore = ref(true);
2026-05-21 06:56:42 +08:00
const currentCategoryId = ref('');
const categories = ref([]);
const currentPage = ref(1);
2026-05-07 11:09:32 +08:00
2026-05-21 06:56:42 +08:00
const loadCategories = async () => {
try {
const res = await fetchTopicCategories();
// 提取真实数据数组
const list = res?.data?.list || res?.list || res || [];
// 增加一个“全部”的默认选项id 置空
2026-05-21 18:00:07 +08:00
categories.value = [{ id: 'recommend', title: '推荐' }, ...list];
2026-05-21 06:56:42 +08:00
// 初始化选中的ID为“全部”
if (!currentCategoryId.value && categories.value.length > 0) {
currentCategoryId.value = categories.value[0].id;
}
// 加载初始列表
2026-05-21 18:00:07 +08:00
// loadTopicList(currentCategoryId.value, currentPage.value);
2026-05-21 06:56:42 +08:00
} catch (error) {
console.error("获取分类失败:", error);
// 兜底数据
categories.value = [
{ id: '', title: '全部' },
{ id: 1, title: '历史' },
{ id: 2, title: '数码' },
];
// 使用兜底也尝试加载一次
loadTopicList(currentCategoryId.value, currentPage.value);
}
};
const loadTopicList = async (categoryId, page = 1) => {
if (page === 1) {
topicList.value = [];
}
try {
loading.value = true;
const res = await fetchTopicList(categoryId, page);
// 提取真实数据数组
const list = res?.list || [];
// 合并数据
topicList.value = [...topicList.value, ...list];
hasMore.value = list.length >= 5;
} catch (error) {
console.error("获取主题列表失败:", error);
hasMore.value = false;
} finally {
loading.value = false;
}
};
2026-05-07 11:09:32 +08:00
2026-05-21 06:56:42 +08:00
const switchCategory = (categoryId) => {
if (currentCategoryId.value === categoryId) return;
currentCategoryId.value = categoryId;
2026-06-09 23:55:11 +08:00
uni.$trackRecord({
eventName: 'switch_category',
eventType: 'click',
elementId: `category_${categoryId}`,
elementContent: categories.value.find(c => c.id === categoryId)?.title || '未知分类',
});
// 切换分类时重新加载数据
2026-05-21 06:56:42 +08:00
currentPage.value = 1;
2026-05-07 11:09:32 +08:00
hasMore.value = true;
2026-05-21 06:56:42 +08:00
loadTopicList(categoryId, currentPage.value);
2026-05-07 11:09:32 +08:00
};
2026-05-07 11:05:30 +08:00
2026-06-09 23:55:11 +08:00
const topicList = ref([]);
2026-05-07 11:05:30 +08:00
2026-05-07 15:57:39 +08:00
const goToRating = (topicId, itemId) => {
2026-06-09 23:55:11 +08:00
uni.$trackRecord({
eventName: 'jumpto_rating',
eventType: 'jump',
elementId: `topic_${topicId}`,
elementContent: `主题${topicId}`,
});
2026-05-07 15:57:39 +08:00
uni.navigateTo({
url: `/pages/rating/index?topicId=${topicId}&itemId=${itemId}`
});
};
2026-05-07 11:05:30 +08:00
const handleLogin = () => {
if (!userInfo.value.nickName) {
uni.$emit("show-login-popup");
}
};
let firstLoad = true;
const initData = () => {
if (adManager.isAdShowing) return; // 防止广告页返回时立即重新渲染引发 insertTextView:fail
};
2026-05-21 06:56:42 +08:00
onLoad(async (options) => {
2026-05-07 11:05:30 +08:00
uni.$trackRecord({
eventName: "index_page_visit",
eventType: `visit`,
})
if (options.shareToken) {
saveViewRequest(options.shareToken, "index");
}
2026-05-21 06:56:42 +08:00
await loadCategories(); // 初始化时加载分类
await loadTopicList(currentCategoryId.value); // 初始化时加载主题列表
2026-05-07 11:05:30 +08:00
initData();
});
onShow(() => {
if (firstLoad) {
firstLoad = false;
return;
}
initData();
});
const handleLoginSuccess = () => {
};
// 下拉刷新
2026-05-21 06:56:42 +08:00
onPullDownRefresh(async () => {
currentPage.value = 1;
hasMore.value = true;
await loadTopicList(currentCategoryId.value, currentPage.value);
uni.stopPullDownRefresh();
2026-05-07 11:05:30 +08:00
});
// 上拉加载更多
onReachBottom(() => {
if (!hasMore.value || loading.value) return;
2026-05-21 06:56:42 +08:00
currentPage.value += 1;
loadTopicList(currentCategoryId.value, currentPage.value);
2026-05-07 11:05:30 +08:00
});
onShareAppMessage(async () => {
const shareToken = await getShareToken("index");
getShareReward();
return {
2026-06-09 23:55:11 +08:00
title: "夯拉评分",
2026-05-07 11:05:30 +08:00
path: "/pages/index/index?shareToken=" + shareToken,
};
});
onShareTimeline(async () => {
const shareToken = await getShareToken("index");
getShareReward();
return {
2026-06-09 23:55:11 +08:00
title: "夯拉评分",
2026-05-07 11:05:30 +08:00
query: `shareToken=${shareToken}`,
};
});
</script>
<style lang="scss" scoped>
.home-container {
min-height: 100vh;
background-color: #f5f6fa;
box-sizing: border-box;
}
.header-section {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 999;
background-color: rgba(245, 246, 250, 0.95);
backdrop-filter: blur(20rpx);
padding-bottom: 20rpx;
}
.header-content {
display: flex;
align-items: center;
padding: 0 32rpx;
height: 120rpx;
}
.header-texts {
display: flex;
flex-direction: column;
justify-content: center;
}
.title {
font-size: 44rpx;
font-weight: 800;
color: #111;
margin-bottom: 6rpx;
}
.subtitle {
font-size: 24rpx;
color: #666;
}
.user-avatar {
width: 72rpx;
height: 72rpx;
border-radius: 50%;
border: 2rpx solid #fff;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
2026-05-07 11:09:32 +08:00
margin-right: 24rpx; /* 头像在左边时,增加右侧间距 */
}
/* Tab 样式 */
.category-tabs {
width: 100%;
white-space: nowrap;
height: 88rpx;
}
.tabs-wrapper {
display: inline-flex;
padding: 0 16rpx;
height: 100%;
align-items: center;
}
.tab-item {
display: inline-flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 0 24rpx;
height: 100%;
position: relative;
}
.tab-text {
font-size: 30rpx;
color: #666;
transition: all 0.3s ease;
}
.tab-item.active .tab-text {
font-size: 34rpx;
font-weight: bold;
color: #111;
}
.tab-line {
position: absolute;
bottom: 12rpx;
width: 32rpx;
height: 6rpx;
background-color: #2953ff;
border-radius: 4rpx;
transition: all 0.3s ease;
2026-05-07 11:05:30 +08:00
}
.topic-list {
padding: 24rpx 32rpx;
}
.load-more {
text-align: center;
padding: 30rpx 0;
color: #999;
font-size: 24rpx;
}
</style>