hookehuyr

feat(积分收集): 添加积分收集完成后的总积分显示组件

新增TotalPointsDisplay组件用于展示积分收集完成后的总积分
修改PointsCollector组件在收集完成时触发collection-complete事件
在Dashboard页面根据收集状态切换显示PointsCollector或TotalPointsDisplay
......@@ -27,6 +27,7 @@ declare module 'vue' {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
TabBar: typeof import('./src/components/TabBar.vue')['default']
TotalPointsDisplay: typeof import('./src/components/TotalPointsDisplay.vue')['default']
WeRunAuth: typeof import('./src/components/WeRunAuth.vue')['default']
}
}
......
......@@ -38,10 +38,10 @@
</template>
<script setup>
import { ref, onMounted, defineProps, defineExpose } from 'vue'
import { ref, onMounted, defineProps, defineExpose, defineEmits } from 'vue'
import Taro, { useDidShow } from '@tarojs/taro'
// 定义props
const emit = defineEmits(['collection-complete'])
const props = defineProps({
height: {
type: String,
......@@ -179,6 +179,9 @@ const collectItem = (item) => {
totalPoints.value += totalToAdd;
floatingItems.value = floatingItems.value.filter(i => i.id !== item.id);
if (floatingItems.value.length === 0) {
emit('collection-complete', totalPoints.value);
}
}, 800); // 动画时长
}
......@@ -206,6 +209,7 @@ const collectAll = async () => {
floatingItems.value = [];
isCollecting.value = false;
emit('collection-complete', totalPoints.value);
}, totalAnimationTime);
}
......
<template>
<view class="total-points-display" :style="{ height: height }">
<!-- 中心圆形显示总积分 -->
<view class="center-circle1">
<view class="total-points" @tap="handleGoToRewards">
<text class="points-number">{{ animatedTotalPoints }}分</text>
<text class="points-label">去兑换</text>
</view>
</view>
</view>
</template>
<script setup>
import { ref, watch, onMounted, defineProps } from 'vue'
import Taro from '@tarojs/taro'
// 定义props
const props = defineProps({
totalPoints: {
type: Number,
required: true,
default: 0
},
height: {
type: String,
default: '30vh'
}
})
// 响应式数据
const animatedTotalPoints = ref(0)
/**
* 数字滚动动画
* @param {number} start - 动画开始值
* @param {number} end - 动画结束值
*/
const animateNumber = (start, end) => {
if (start === end) {
animatedTotalPoints.value = end;
return;
}
const duration = 800;
const startTime = Date.now();
const difference = end - start;
const animate = () => {
const elapsed = Date.now() - startTime;
const progress = Math.min(elapsed / duration, 1);
const easeOut = 1 - Math.pow(1 - progress, 3);
animatedTotalPoints.value = Math.floor(start + difference * easeOut);
if (progress < 1) {
requestAnimationFrame(animate);
} else {
animatedTotalPoints.value = end;
}
};
animate();
}
// 监听totalPoints变化,并执行动画
watch(() => props.totalPoints, (newValue, oldValue) => {
animateNumber(oldValue, newValue);
})
onMounted(() => {
animateNumber(0, props.totalPoints)
})
/**
* 处理去兑换点击事件
*/
const handleGoToRewards = () => {
Taro.navigateTo({
url: '/pages/RewardCategories/index',
})
}
</script>
<style lang="less">
.total-points-display {
position: relative;
width: 100vw;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
}
.center-circle1 {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 170rpx;
height: 170rpx;
background: linear-gradient(135deg, #4A90E2, #4d96ea);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 8rpx 32rpx rgba(53, 144, 255, 0.3);
z-index: 10;
}
.total-points {
text-align: center;
color: white;
}
.points-number {
display: block;
font-size: 48rpx;
font-weight: bold;
line-height: 1;
}
.points-label {
display: block;
font-size: 24rpx;
margin-top: 8rpx;
opacity: 0.9;
}
// 响应式适配
@media screen and (max-width: 750px) {
.center-circle {
width: 160rpx;
height: 160rpx;
}
.points-number {
font-size: 36rpx;
}
.points-label {
font-size: 20rpx;
}
}
</style>
......@@ -55,7 +55,20 @@
<!-- Points circles -->
<view class="flex justify-between px-5 py-6 my-4 bg-white rounded-xl shadow-md mx-4">
<PointsCollector ref="pointsCollectorRef" height="30vh" />
<template v-if="!showTotalPointsOnly">
<PointsCollector
ref="pointsCollectorRef"
height="30vh"
:total-points="finalTotalPoints"
@collection-complete="handleCollectionComplete"
/>
</template>
<template v-else>
<TotalPointsDisplay
:total-points="finalTotalPoints"
height="13vh"
/>
</template>
</view>
<!-- Photo button -->
......@@ -184,6 +197,7 @@ import { ref, computed, onMounted } from 'vue';
import Taro, { useDidShow } from '@tarojs/taro';
import { Setting, Photograph, Right, Close, Category } from '@nutui/icons-vue-taro';
import BottomNav from '../../components/BottomNav.vue';
import TotalPointsDisplay from '@/components/TotalPointsDisplay.vue';
import PointsCollector from '@/components/PointsCollector.vue'
import WeRunAuth from '@/components/WeRunAuth.vue'
import { useMediaPreview } from '@/composables/useMediaPreview';
......@@ -192,6 +206,8 @@ const todaySteps = ref(0);
const isWeRunAuthorized = ref(false);
const pointsCollectorRef = ref(null)
const weRunAuthRef = ref(null)
const showTotalPointsOnly = ref(false)
const finalTotalPoints = ref(0)
const familyName = ref('')
const familySlogn = ref('')
......@@ -240,6 +256,15 @@ const handleCollectAll = () => {
}
/**
* 处理积分收集完成事件
* @param {number} points - 最终的积分
*/
const handleCollectionComplete = (points) => {
finalTotalPoints.value = points;
showTotalPointsOnly.value = true;
};
/**
* 手动刷新微信步数
*/
const handleRefreshSteps = async () => {
......@@ -348,6 +373,11 @@ useDidShow(() => {
// Taro.redirectTo({ url: '/pages/Welcome/index' });
// }
initPageData();
// TODO: 真实情况下 需要重新获取积分列表, 测试随机积分获取状态
showTotalPointsOnly.value = Math.random() > 0.5 ? true : false;
// 总积分数量也要从接口获取传进去
finalTotalPoints.value = 10086;
})
</script>
......