404 lines
8.2 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="page-container">
<!-- 顶部装饰背景 - 漂浮光球 -->
<view class="bg-decoration"></view>
<view class="header-area">
<view class="page-title">邀请记录</view>
<view class="page-subtitle">Invitations</view>
</view>
<!-- 统计卡片 - 毛玻璃风格 -->
<view class="stats-card glass-card">
<view class="stat-item">
<text class="stat-num">{{ list.length }}</text>
<text class="stat-label">邀请人数</text>
</view>
<view class="stat-divider"></view>
<view class="stat-item">
<text class="stat-num">{{ getRewardsTotal() }}</text>
<text class="stat-label">累计奖励</text>
</view>
</view>
<!-- 内容区 -->
<scroll-view
scroll-y
class="content-scroll"
refresher-enabled
:refresher-triggered="isRefreshing"
@refresherrefresh="onRefresh"
@scrolltolower="loadMore"
>
<!-- 加载状态 -->
<view v-if="loading && list.length === 0" class="loading-state">
<view class="spinner"></view>
<text>加载中...</text>
</view>
<!-- 空状态 -->
<view v-else-if="list.length === 0" class="empty-state">
<text class="empty-icon">👥</text>
<text class="empty-text">暂无邀请记录</text>
<text class="empty-hint">分享给好友一起来玩吧</text>
</view>
<!-- 邀请列表 -->
<view v-else class="invite-list">
<view
v-for="(item, index) in list"
:key="item.id || index"
class="invite-item"
:style="{ animationDelay: `${index * 0.05}s` }"
>
<image class="invite-avatar" :src="item.avatar || '/static/logo.png'" mode="aspectFill"></image>
<view class="invite-info">
<text class="invite-name">{{ item.nickname || '用户' + item.id }}</text>
<text class="invite-time">{{ formatDate(item.created_at) }}</text>
</view>
<view class="invite-status">
<text class="status-text">已邀请</text>
</view>
</view>
</view>
<!-- 加载更多 -->
<view v-if="loading && list.length > 0" class="loading-more">
<view class="spinner"></view>
<text>加载更多...</text>
</view>
<view v-else-if="!hasMore && list.length > 0" class="no-more">- 到底啦 -</view>
</scroll-view>
</view>
</template>
<script setup>
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { getUserInvites } from '../../api/appUser'
const list = ref([])
const loading = ref(false)
const isRefreshing = ref(false)
const page = ref(1)
const pageSize = 20
const hasMore = ref(true)
// 获取用户ID
function getUserId() {
return uni.getStorageSync('user_id')
}
// 检查登录状态
function checkAuth() {
const token = uni.getStorageSync('token')
const userId = getUserId()
if (!token || !userId) {
uni.showModal({
title: '提示',
content: '请先登录',
confirmText: '去登录',
success: (res) => {
if (res.confirm) {
uni.navigateTo({ url: '/pages/login/index' })
}
}
})
return false
}
return true
}
// 格式化日期
function formatDate(t) {
if (!t) return ''
const d = new Date(t)
const y = d.getFullYear()
const m = String(d.getMonth() + 1).padStart(2, '0')
const day = String(d.getDate()).padStart(2, '0')
return `${y}-${m}-${day}`
}
// 计算累计奖励
function getRewardsTotal() {
// 根据实际业务逻辑计算,目前简单显示邀请人数 × 积分奖励
const rewardPerInvite = 10 // 每邀请一人奖励积分
return list.value.length * rewardPerInvite
}
// 下拉刷新
async function onRefresh() {
isRefreshing.value = true
page.value = 1
hasMore.value = true
await fetchData(false)
isRefreshing.value = false
}
// 加载更多
async function loadMore() {
if (loading.value || !hasMore.value) return
await fetchData(true)
}
// 获取数据
async function fetchData(append = false) {
if (!checkAuth()) return
if (loading.value) return
loading.value = true
try {
const userId = getUserId()
const res = await getUserInvites(userId, page.value, pageSize)
const items = res.list || res.data || []
if (append) {
list.value = [...list.value, ...items]
} else {
list.value = items
}
if (items.length < pageSize) {
hasMore.value = false
} else {
page.value++
}
} catch (e) {
console.error('获取邀请记录失败:', e)
hasMore.value = false
} finally {
loading.value = false
}
}
onLoad(() => {
fetchData()
})
</script>
<style lang="scss" scoped>
.page-container {
min-height: 100vh;
background: $bg-page;
position: relative;
overflow: hidden;
}
.header-area {
padding: $spacing-xl $spacing-lg;
padding-top: calc(env(safe-area-inset-top) + 20rpx);
position: relative;
z-index: 1;
}
.page-title {
font-size: 48rpx;
font-weight: 900;
color: $text-main;
margin-bottom: 8rpx;
letter-spacing: 1rpx;
text-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
}
.page-subtitle {
font-size: 24rpx;
color: $text-tertiary;
text-transform: uppercase;
letter-spacing: 2rpx;
font-weight: 600;
}
/* 统计卡片 */
.stats-card {
@extend .glass-card;
margin: 0 $spacing-lg $spacing-lg;
padding: 40rpx;
display: flex;
justify-content: center;
align-items: center;
}
.stat-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
}
.stat-num {
font-size: 56rpx;
font-weight: 900;
color: $brand-primary;
font-family: 'DIN Alternate', sans-serif;
line-height: 1;
margin-bottom: 12rpx;
}
.stat-label {
font-size: 24rpx;
color: $text-sub;
}
.stat-divider {
width: 1px;
height: 60rpx;
background: $border-color-light;
margin: 0 40rpx;
}
/* 内容滚动区 */
.content-scroll {
height: calc(100vh - 400rpx);
padding: 0 $spacing-lg $spacing-lg;
}
/* 加载状态 */
.loading-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 0;
color: $text-tertiary;
font-size: 26rpx;
gap: 16rpx;
}
/* 空状态 */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 0;
}
.empty-icon {
font-size: 80rpx;
margin-bottom: 20rpx;
}
.empty-text {
color: $text-tertiary;
font-size: 28rpx;
margin-bottom: 12rpx;
}
.empty-hint {
color: $text-tertiary;
font-size: 24rpx;
opacity: 0.6;
}
/* 邀请列表 */
.invite-list {
display: flex;
flex-direction: column;
gap: 20rpx;
}
.invite-item {
background: #fff;
border-radius: $radius-lg;
padding: 24rpx;
display: flex;
align-items: center;
box-shadow: $shadow-sm;
animation: fadeInUp 0.5s ease-out backwards;
&:active {
transform: scale(0.98);
background: rgba(255, 255, 255, 0.8);
}
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20rpx);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.invite-avatar {
width: 88rpx;
height: 88rpx;
border-radius: 50%;
background: $bg-secondary;
margin-right: 24rpx;
flex-shrink: 0;
}
.invite-info {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
}
.invite-name {
font-size: 30rpx;
font-weight: 700;
color: $text-main;
margin-bottom: 8rpx;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.invite-time {
font-size: 24rpx;
color: $text-tertiary;
}
.invite-status {
flex-shrink: 0;
}
.status-text {
font-size: 24rpx;
color: $uni-color-success;
background: rgba($uni-color-success, 0.1);
padding: 6rpx 16rpx;
border-radius: 100rpx;
}
/* 加载更多 */
.loading-more {
display: flex;
align-items: center;
justify-content: center;
padding: 30rpx 0;
color: $text-tertiary;
font-size: 24rpx;
gap: 12rpx;
}
.spinner {
width: 28rpx;
height: 28rpx;
border: 3rpx solid $bg-secondary;
border-top-color: $text-tertiary;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.no-more {
text-align: center;
padding: 40rpx 0;
color: $text-tertiary;
font-size: 24rpx;
opacity: 0.6;
}
</style>