Files
rating/pages/index/index.vue
2026-05-07 11:05:30 +08:00

439 lines
11 KiB
Vue
Raw 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">
<view class="header-texts">
<text class="title">全民评分</text>
<text class="subtitle">看看全国网友怎么评分</text>
</view>
<image
class="user-avatar"
:src="userInfo.avatarUrl || '/static/images/default-avatar.png'"
mode="aspectFill"
@tap="handleLogin"
/>
</view>
</view>
<!-- 占位防止固定头部遮挡内容 -->
<view :style="{ height: statusBarHeight + 60 + 'px' }"></view>
<!-- 列表内容 -->
<view class="topic-list">
<view class="topic-card" v-for="topic in topicList" :key="topic.id">
<!-- 卡片头部标题与统计信息 -->
<view class="topic-header">
<text class="topic-title">{{ topic.title }}</text>
<view class="topic-stats">
<view class="stat-item">
<text class="stat-icon icon-group"></text>
<text>{{ topic.participants }}人参与</text>
</view>
<view class="stat-item heat">
<text class="stat-icon icon-fire"></text>
<text>热度 {{ topic.heat }}</text>
</view>
</view>
</view>
<!-- 排行榜列表 -->
<view class="rank-list">
<view class="rank-item" v-for="(item, index) in topic.items" :key="item.id">
<text class="rank-num" :class="'rank-' + (index + 1)">{{ index + 1 }}</text>
<image class="item-avatar" :src="item.avatar" mode="aspectFill"></image>
<text class="item-name">{{ item.name }}</text>
<text class="item-score" :class="'score-' + (index + 1)">{{ item.score }}</text>
</view>
</view>
<!-- AI 总结 -->
<view class="ai-summary">
<text class="ai-icon icon-robot"></text>
<view class="summary-text">
<text class="summary-label">AI总结</text>
<text class="summary-content">{{ topic.aiSummary }}</text>
</view>
</view>
<!-- 标签列表 -->
<view class="tag-list">
<view class="tag" v-for="(tag, tIndex) in topic.tags" :key="tIndex">
<text>#{{ tag }}</text>
</view>
</view>
</view>
<!-- 加载更多 -->
<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 { getShareToken, saveViewRequest } from "@/utils/common.js";
import LoginPopup from "@/components/LoginPopup/LoginPopup.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 mockData = [
{
id: 1,
title: '你心中的千古一帝',
participants: '23.5万',
heat: '98k',
items: [
{ id: 1, name: '李世民', avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=1', score: '9.8' },
{ id: 2, name: '秦始皇', avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=2', score: '9.5' },
{ id: 3, name: '康熙', avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=3', score: '9.2' },
],
aiSummary: '多数用户认为李世民综合能力最强,兼具文治武功。',
tags: ['历史', '皇帝', '争议']
},
{
id: 2,
title: '国产手机系统谁最强',
participants: '15万',
heat: '76k',
items: [
{ id: 1, name: 'iOS', avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=4', score: '8.5' },
{ id: 2, name: 'HyperOS', avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=5', score: '8.2' },
{ id: 3, name: 'OriginOS', avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=6', score: '8.0' },
],
aiSummary: '系统流畅度与动效是用户关注的核心,底层优化成为评分关键。',
tags: ['数码', '手机', '测评']
},
{
id: 3,
title: '最值得定居的城市',
participants: '12.8万',
heat: '62k',
items: [
{ id: 1, name: '成都', avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=7', score: '9.2' },
{ id: 2, name: '杭州', avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=8', score: '8.9' },
{ id: 3, name: '厦门', avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=9', score: '8.7' },
],
aiSummary: '宜居性与就业机会是主要博弈点,慢节奏生活深受青睐。',
tags: ['城市', '生活', '推荐']
}
];
const topicList = ref([...mockData]);
const handleLogin = () => {
if (!userInfo.value.nickName) {
uni.$emit("show-login-popup");
}
};
let firstLoad = true;
const initData = () => {
if (adManager.isAdShowing) return; // 防止广告页返回时立即重新渲染引发 insertTextView:fail
};
onLoad((options) => {
uni.$trackRecord({
eventName: "index_page_visit",
eventType: `visit`,
})
if (options.shareToken) {
saveViewRequest(options.shareToken, "index");
}
initData();
});
onShow(() => {
if (firstLoad) {
firstLoad = false;
return;
}
initData();
});
const handleLoginSuccess = () => {
};
// 下拉刷新
onPullDownRefresh(() => {
setTimeout(() => {
topicList.value = [...mockData];
hasMore.value = true;
uni.stopPullDownRefresh();
}, 1000);
});
// 上拉加载更多
onReachBottom(() => {
if (!hasMore.value || loading.value) return;
loading.value = true;
setTimeout(() => {
const newData = mockData.map(item => ({ ...item, id: item.id + topicList.value.length }));
topicList.value.push(...newData);
loading.value = false;
if (topicList.value.length >= 10) {
hasMore.value = false;
}
}, 1000);
});
onShareAppMessage(async () => {
const shareToken = await getShareToken("index");
getShareReward();
return {
title: "全民评分",
path: "/pages/index/index?shareToken=" + shareToken,
};
});
onShareTimeline(async () => {
const shareToken = await getShareToken("index");
getShareReward();
return {
title: "全民评分",
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;
justify-content: space-between;
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);
}
.topic-list {
padding: 24rpx 32rpx;
}
.topic-card {
background-color: #ffffff;
border-radius: 32rpx;
padding: 32rpx;
margin-bottom: 32rpx;
box-shadow: 0 4rpx 24rpx rgba(0, 0, 0, 0.02);
}
.topic-header {
margin-bottom: 32rpx;
}
.topic-title {
font-size: 36rpx;
font-weight: bold;
color: #222;
margin-bottom: 16rpx;
display: block;
}
.topic-stats {
display: flex;
align-items: center;
}
.stat-item {
display: flex;
align-items: center;
font-size: 24rpx;
color: #666;
margin-right: 32rpx;
}
.stat-icon {
width: 24rpx;
height: 24rpx;
margin-right: 8rpx;
display: inline-block;
background-size: cover;
}
.icon-group {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23999999'%3E%3Cpath d='M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5C6.34 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z'/%3E%3C/svg%3E");
}
.icon-fire {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23ff4d4f'%3E%3Cpath d='M19.48 13.03c-.02-.19-.13-.36-.29-.46-.17-.11-.38-.13-.56-.05-.98.42-2.12.3-2.92-.31-1.07-.81-1.55-2.22-1.28-3.75.12-.66.1-1.32-.06-1.97-.24-.95-1.1-1.6-2.09-1.58-1 .02-1.83.74-2 1.72-.25 1.55-.95 2.94-2 4.02-1.38 1.41-2.05 3.39-1.85 5.4.2 2.05 1.42 3.86 3.25 4.82 1.37.72 2.94 1.01 4.5.83 2.74-.32 5.06-2.28 5.86-4.95.23-.75.29-1.52.19-2.28-.06-.5-.21-.99-.41-1.45l-.34-.99zm-4.98 6.47c-1.37 0-2.5-1.13-2.5-2.5s1.13-2.5 2.5-2.5 2.5 1.13 2.5 2.5-1.13 2.5-2.5 2.5z'/%3E%3C/svg%3E");
}
.rank-list {
margin-bottom: 24rpx;
}
.rank-item {
display: flex;
align-items: center;
margin-bottom: 24rpx;
}
.rank-num {
width: 40rpx;
font-size: 32rpx;
font-weight: bold;
text-align: center;
margin-right: 16rpx;
}
.rank-1 {
color: #2953ff;
}
.rank-2, .rank-3 {
color: #333;
}
.item-avatar {
width: 72rpx;
height: 72rpx;
border-radius: 50%;
margin-right: 24rpx;
background-color: #f0f0f0;
}
.item-name {
flex: 1;
font-size: 30rpx;
color: #333;
}
.item-score {
font-size: 34rpx;
font-weight: bold;
}
.score-1 {
color: #2953ff;
}
.score-2, .score-3 {
color: #333;
}
.ai-summary {
display: flex;
align-items: flex-start;
padding: 20rpx 24rpx;
background-color: #ffffff;
border: 2rpx solid #e0d6ff;
border-radius: 20rpx;
margin-bottom: 24rpx;
}
.ai-icon {
width: 32rpx;
height: 32rpx;
margin-right: 12rpx;
margin-top: 4rpx;
flex-shrink: 0;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%234d44f1'%3E%3Cpath d='M12 2a2 2 0 0 1 2 2c0 .74-.4 1.39-1 1.73V7h5a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V9a2 2 0 0 1 2-2h5V5.73A2 2 0 0 1 10 4a2 2 0 0 1 2-2zm-3 8a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3zm6 0a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3z'/%3E%3C/svg%3E");
}
.summary-text {
flex: 1;
font-size: 26rpx;
line-height: 1.5;
}
.summary-label {
color: #4d44f1;
font-weight: bold;
}
.summary-content {
color: #555;
}
.tag-list {
display: flex;
flex-wrap: wrap;
gap: 16rpx;
}
.tag {
background-color: #f4eaff;
padding: 6rpx 20rpx;
border-radius: 100rpx;
}
.tag text {
color: #9d5bfe;
font-size: 22rpx;
}
.load-more {
text-align: center;
padding: 30rpx 0;
color: #999;
font-size: 24rpx;
}
</style>