Files
rating/pages/index/index.vue
2026-06-22 18:07:44 +08:00

356 lines
8.6 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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"
/>
<view class="header-texts">
<text class="title">夯拉评分</text>
<text class="subtitle">看看全国网友怎么评分</text>
</view>
</view>
<!-- 顶部切换 tab -->
<scroll-view class="category-tabs" scroll-x :show-scrollbar="false">
<view class="tabs-wrapper">
<view
class="tab-item"
:class="{ active: currentCategoryId === category.id }"
v-for="category in categories"
:key="category.id"
@tap="switchCategory(category.id)"
>
<text class="tab-text">{{ category.title }}</text>
<view class="tab-line" v-if="currentCategoryId === category.id"></view>
</view>
</view>
</scroll-view>
</view>
<!-- 占位防止固定头部遮挡内容 (增加 tab 的高度) -->
<view :style="{ height: statusBarHeight + 104 + 'px' }"></view>
<!-- 列表内容 -->
<view class="topic-list">
<TopicCard
v-for="topic in topicList"
:key="topic.id"
:topic="topic"
@item-click="goToRating"
/>
<!-- 加载更多 -->
<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";
import { fetchTopicList, fetchTopicCategories } from "@/api/topic";
import { getShareToken, saveViewRequest } from "@/utils/common.js";
import LoginPopup from "@/components/LoginPopup/LoginPopup.vue";
import TopicCard from "@/components/TopicCard/TopicCard.vue";
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);
const currentCategoryId = ref('');
const categories = ref([]);
const currentPage = ref(1);
const loadCategories = async () => {
try {
const res = await fetchTopicCategories();
// 提取真实数据数组
const list = res?.data?.list || res?.list || res || [];
// 增加一个“全部”的默认选项id 置空
categories.value = [{ id: 'recommend', title: '推荐' }, ...list];
// 初始化选中的ID为“全部”
if (!currentCategoryId.value && categories.value.length > 0) {
currentCategoryId.value = categories.value[0].id;
}
// 加载初始列表
// loadTopicList(currentCategoryId.value, currentPage.value);
} 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 rawList =res?.list || [];
const list = Array.isArray(rawList) ? rawList : [];
// 合并数据
topicList.value = [...topicList.value, ...list];
hasMore.value = list.length >= 5;
} catch (error) {
console.error("获取主题列表失败:", error);
hasMore.value = false;
} finally {
loading.value = false;
}
};
const switchCategory = (categoryId) => {
if (currentCategoryId.value === categoryId) return;
currentCategoryId.value = categoryId;
uni.$trackRecord({
eventName: 'switch_category',
eventType: 'click',
elementId: `category_${categoryId}`,
elementContent: categories.value.find(c => c.id === categoryId)?.title || '未知分类',
});
// 切换分类时重新加载数据
currentPage.value = 1;
hasMore.value = true;
loadTopicList(categoryId, currentPage.value);
};
const topicList = ref([]);
const getCurrentCategoryTitle = () => {
return categories.value.find((category) => category.id === currentCategoryId.value)?.title || "推荐";
};
const goToRating = (topicId, itemId) => {
uni.$trackRecord({
eventName: 'jumpto_rating',
eventType: 'jump',
elementId: `topic_${topicId}`,
elementContent: `主题${topicId}`,
});
uni.navigateTo({
url: `/pages/rating/index?topicId=${topicId}&itemId=${itemId}`
});
};
const handleLogin = () => {
if (!userInfo.value.nickName) {
uni.$emit("show-login-popup");
}
};
let firstLoad = true;
const initData = () => {
if (adManager.isAdShowing) return; // 防止广告页返回时立即重新渲染引发 insertTextView:fail
};
onLoad(async (options) => {
uni.$trackRecord({
eventName: "index_page_visit",
eventType: `visit`,
})
if (options.shareToken) {
saveViewRequest(options.shareToken, "index");
}
await loadCategories(); // 初始化时加载分类
await loadTopicList(currentCategoryId.value); // 初始化时加载主题列表
initData();
});
onShow(() => {
if (firstLoad) {
firstLoad = false;
return;
}
initData();
});
const handleLoginSuccess = async () => {
currentPage.value = 1;
hasMore.value = true;
await loadCategories();
await loadTopicList(currentCategoryId.value, currentPage.value);
};
// 下拉刷新
onPullDownRefresh(async () => {
currentPage.value = 1;
hasMore.value = true;
await loadTopicList(currentCategoryId.value, currentPage.value);
uni.stopPullDownRefresh();
});
// 上拉加载更多
onReachBottom(() => {
if (!hasMore.value || loading.value) return;
currentPage.value += 1;
loadTopicList(currentCategoryId.value, currentPage.value);
});
onShareAppMessage(async () => {
const shareToken = await getShareToken("index");
getShareReward();
const categoryTitle = getCurrentCategoryTitle();
return {
title: `夯拉评分 | ${categoryTitle}榜单正在热议`,
path: "/pages/index/index?shareToken=" + shareToken,
};
});
onShareTimeline(async () => {
const shareToken = await getShareToken("index");
getShareReward();
const categoryTitle = getCurrentCategoryTitle();
return {
title: `${categoryTitle}圈都在玩夯拉评分,来看看谁最夯`,
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);
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;
}
.topic-list {
padding: 24rpx 32rpx;
}
.load-more {
text-align: center;
padding: 30rpx 0;
color: #999;
font-size: 24rpx;
}
</style>