feat(dashboard): CrystalOverview 双卡(水晶余额+今日收益)
This commit is contained in:
parent
6c8ecb8fda
commit
1328aadee9
119
frontend/pages/dashboard/components/CrystalOverview.vue
Normal file
119
frontend/pages/dashboard/components/CrystalOverview.vue
Normal file
@ -0,0 +1,119 @@
|
||||
<template>
|
||||
<view class="crystal-overview">
|
||||
<!-- 错误态 -->
|
||||
<view v-if="error" class="error-card" @tap="$emit('retry')">
|
||||
<text class="error-text">加载失败,点击重试</text>
|
||||
</view>
|
||||
|
||||
<!-- 骨架态 -->
|
||||
<view v-else-if="loading || !data" class="skeleton-row">
|
||||
<view v-for="i in 2" :key="i" class="skeleton-card"></view>
|
||||
</view>
|
||||
|
||||
<!-- 正常态 -->
|
||||
<view v-else class="card-row">
|
||||
<view class="data-card card-crystal">
|
||||
<text class="card-label">水晶余额</text>
|
||||
<text class="card-value">{{ data.crystal_balance }}</text>
|
||||
</view>
|
||||
<view class="data-card card-today">
|
||||
<text class="card-label">今日收益</text>
|
||||
<text class="card-value">+ {{ data.today_income }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
defineProps({
|
||||
data: { type: Object, default: null }, // { crystal_balance, today_income, week_rank? }
|
||||
loading: { type: Boolean, default: false },
|
||||
error: { type: String, default: null },
|
||||
})
|
||||
defineEmits(['retry'])
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.crystal-overview {
|
||||
margin: 24rpx 0;
|
||||
}
|
||||
|
||||
.card-row {
|
||||
display: flex;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.data-card {
|
||||
flex: 1;
|
||||
height: 200rpx;
|
||||
border-radius: 22rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card-crystal {
|
||||
background: linear-gradient(135deg, #FFDF77 0%, #8E95E2 40%, #F48CFF 100%);
|
||||
box-shadow: 0px 4px 4px rgba(189, 50, 50, 0.25);
|
||||
}
|
||||
|
||||
.card-today {
|
||||
background: linear-gradient(137deg, #FFDF77 0%, #8E95E2 40%, #F48CFF 100%);
|
||||
box-shadow: 0px 4px 4px rgba(189, 50, 50, 0.25);
|
||||
}
|
||||
|
||||
.card-label {
|
||||
font-size: 24rpx;
|
||||
color: #ffffff;
|
||||
text-shadow: 0px 4px 4px rgba(164, 60, 60, 0.55);
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.card-value {
|
||||
font-size: 70rpx;
|
||||
font-weight: 700;
|
||||
color: #FFFABD;
|
||||
text-shadow: -1px 1px 4px rgba(206, 9, 9, 0.84);
|
||||
font-family: 'Baloo Bhai', sans-serif;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
/* 骨架态 */
|
||||
.skeleton-row {
|
||||
display: flex;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.skeleton-card {
|
||||
flex: 1;
|
||||
height: 200rpx;
|
||||
border-radius: 22rpx;
|
||||
background: linear-gradient(90deg, rgba(255, 255, 255, 0.1) 25%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.1) 75%);
|
||||
background-size: 200% 100%;
|
||||
animation: skeleton-shimmer 1.5s infinite;
|
||||
}
|
||||
|
||||
@keyframes skeleton-shimmer {
|
||||
0% { background-position: 200% 0; }
|
||||
100% { background-position: -200% 0; }
|
||||
}
|
||||
|
||||
/* 错误态 */
|
||||
.error-card {
|
||||
height: 200rpx;
|
||||
border-radius: 22rpx;
|
||||
background: rgba(255, 100, 100, 0.15);
|
||||
border: 2rpx solid rgba(255, 100, 100, 0.4);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.error-text {
|
||||
color: #ff8080;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
</style>
|
||||
@ -7,9 +7,12 @@
|
||||
|
||||
<!-- Tab 1: 水晶相关 -->
|
||||
<view v-if="activeTab === 'crystal'" class="dashboard-content">
|
||||
<view class="placeholder-section">
|
||||
<text class="placeholder-text">骨架页(组件将在 Task 5-10 添加)</text>
|
||||
</view>
|
||||
<CrystalOverview
|
||||
:data="data.today"
|
||||
:loading="loading.today"
|
||||
:error="error.today"
|
||||
@retry="refresh('today')"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- Tab 2: 赛季总览(占位) -->
|
||||
@ -26,6 +29,7 @@
|
||||
<script setup>
|
||||
import { ref, onUnmounted } from 'vue'
|
||||
import DashboardHeader from './components/DashboardHeader.vue'
|
||||
import CrystalOverview from './components/CrystalOverview.vue'
|
||||
import { useDashboardData } from '@/composables/useDashboardData'
|
||||
|
||||
const pageBg = 'linear-gradient(153deg, #FF9597 0%, #80DFFF 33%, #B8B8B8 74%, #D9D9D9 100%)'
|
||||
@ -33,7 +37,7 @@ const pageBg = 'linear-gradient(153deg, #FF9597 0%, #80DFFF 33%, #B8B8B8 74%, #D
|
||||
const activeTab = ref('crystal')
|
||||
const starId = ref(uni.getStorageSync('star_id') || null)
|
||||
|
||||
const { loading, error, data, isReady, dispose } = useDashboardData({
|
||||
const { loading, error, data, refresh, isReady, dispose } = useDashboardData({
|
||||
starId: starId.value,
|
||||
})
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user