110 lines
3.9 KiB
Vue
110 lines
3.9 KiB
Vue
|
|
<!-- src/components-mobile/stats/StatsPanelMobile.vue -->
|
|||
|
|
<!-- 手机端统计简化版:积分排行 + 群组数据 -->
|
|||
|
|
<script setup lang="ts">
|
|||
|
|
import { ref, onMounted, computed } from 'vue'
|
|||
|
|
import { getGroupMemberRanking } from '@/api/points'
|
|||
|
|
import { getGroupGames } from '@/api/games'
|
|||
|
|
import { useGroupStore } from '@/stores/group'
|
|||
|
|
|
|||
|
|
const props = defineProps<{ groupId: string }>()
|
|||
|
|
|
|||
|
|
const groupStore = useGroupStore()
|
|||
|
|
|
|||
|
|
const ranking = ref<{ userId: string; points: number; name?: string }[]>([])
|
|||
|
|
const gameCount = ref(0)
|
|||
|
|
const loading = ref(false)
|
|||
|
|
|
|||
|
|
const group = computed(() => groupStore.currentGroup)
|
|||
|
|
const members = computed(() => groupStore.currentMembers)
|
|||
|
|
|
|||
|
|
onMounted(async () => {
|
|||
|
|
loading.value = true
|
|||
|
|
try {
|
|||
|
|
const [rank, games] = await Promise.all([
|
|||
|
|
getGroupMemberRanking(props.groupId, 20),
|
|||
|
|
getGroupGames(props.groupId, { limit: 1 })
|
|||
|
|
])
|
|||
|
|
ranking.value = rank
|
|||
|
|
gameCount.value = games.total
|
|||
|
|
} catch (e) {
|
|||
|
|
console.error(e)
|
|||
|
|
} finally {
|
|||
|
|
loading.value = false
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 排名样式
|
|||
|
|
function rankColor(index: number): string {
|
|||
|
|
if (index === 0) return '#f59e0b'
|
|||
|
|
if (index === 1) return '#94a3b8'
|
|||
|
|
if (index === 2) return '#d97706'
|
|||
|
|
return 'var(--gg-text-muted)'
|
|||
|
|
}
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<template>
|
|||
|
|
<div class="stats-mobile">
|
|||
|
|
<div v-if="loading" class="loading-box">
|
|||
|
|
<van-loading size="24px">加载中...</van-loading>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<template v-else>
|
|||
|
|
<!-- 概览卡片 -->
|
|||
|
|
<div class="overview-card">
|
|||
|
|
<div class="overview-item">
|
|||
|
|
<div class="overview-num">{{ members.length }}</div>
|
|||
|
|
<div class="overview-label">成员</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="overview-item">
|
|||
|
|
<div class="overview-num">{{ gameCount }}</div>
|
|||
|
|
<div class="overview-label">游戏</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="overview-item">
|
|||
|
|
<div class="overview-num">{{ group?.maxMembers || '-' }}</div>
|
|||
|
|
<div class="overview-label">上限</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 积分排行 -->
|
|||
|
|
<div class="section">
|
|||
|
|
<div class="section-title">积分排行</div>
|
|||
|
|
<div v-if="ranking.length === 0" class="empty-row">暂无数据</div>
|
|||
|
|
<div class="rank-list">
|
|||
|
|
<div
|
|||
|
|
v-for="(item, idx) in ranking"
|
|||
|
|
:key="item.userId"
|
|||
|
|
class="rank-item"
|
|||
|
|
>
|
|||
|
|
<div class="rank-num" :style="{ color: rankColor(idx) }">{{ idx + 1 }}</div>
|
|||
|
|
<div class="rank-info">
|
|||
|
|
<div class="rank-name">{{ item.name || '玩家' }}</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="rank-points">{{ item.points }}</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</template>
|
|||
|
|
</div>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<style scoped>
|
|||
|
|
.stats-mobile { padding: 12px; }
|
|||
|
|
.loading-box { display: flex; justify-content: center; padding: 40px; }
|
|||
|
|
|
|||
|
|
.overview-card { display: flex; background: var(--gg-bg-card); border-radius: var(--gg-radius-lg); padding: 20px; box-shadow: var(--gg-shadow); margin-bottom: 16px; }
|
|||
|
|
.overview-item { flex: 1; display: flex; flex-direction: column; align-items: center; }
|
|||
|
|
.overview-num { font-size: 24px; font-weight: 700; color: var(--gg-primary); }
|
|||
|
|
.overview-label { font-size: 12px; color: var(--gg-text-muted); margin-top: 4px; }
|
|||
|
|
|
|||
|
|
.section { margin-bottom: 16px; }
|
|||
|
|
.section-title { font-size: 15px; font-weight: 600; color: var(--gg-text); margin-bottom: 10px; }
|
|||
|
|
.empty-row { text-align: center; padding: 20px; color: var(--gg-text-muted); font-size: 13px; }
|
|||
|
|
|
|||
|
|
.rank-list { display: flex; flex-direction: column; gap: 8px; }
|
|||
|
|
.rank-item { display: flex; align-items: center; gap: 12px; background: var(--gg-bg-card); padding: 12px 14px; border-radius: var(--gg-radius-sm); box-shadow: var(--gg-shadow); }
|
|||
|
|
.rank-num { font-size: 18px; font-weight: 700; width: 28px; text-align: center; }
|
|||
|
|
.rank-info { flex: 1; min-width: 0; }
|
|||
|
|
.rank-name { font-size: 14px; color: var(--gg-text); }
|
|||
|
|
.rank-points { font-size: 15px; font-weight: 600; color: var(--gg-primary); }
|
|||
|
|
</style>
|