hookehuyr

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

新增TotalPointsDisplay组件用于展示积分收集完成后的总积分
修改PointsCollector组件在收集完成时触发collection-complete事件
在Dashboard页面根据收集状态切换显示PointsCollector或TotalPointsDisplay
...@@ -27,6 +27,7 @@ declare module 'vue' { ...@@ -27,6 +27,7 @@ declare module 'vue' {
27 RouterLink: typeof import('vue-router')['RouterLink'] 27 RouterLink: typeof import('vue-router')['RouterLink']
28 RouterView: typeof import('vue-router')['RouterView'] 28 RouterView: typeof import('vue-router')['RouterView']
29 TabBar: typeof import('./src/components/TabBar.vue')['default'] 29 TabBar: typeof import('./src/components/TabBar.vue')['default']
30 + TotalPointsDisplay: typeof import('./src/components/TotalPointsDisplay.vue')['default']
30 WeRunAuth: typeof import('./src/components/WeRunAuth.vue')['default'] 31 WeRunAuth: typeof import('./src/components/WeRunAuth.vue')['default']
31 } 32 }
32 } 33 }
......
...@@ -38,10 +38,10 @@ ...@@ -38,10 +38,10 @@
38 </template> 38 </template>
39 39
40 <script setup> 40 <script setup>
41 -import { ref, onMounted, defineProps, defineExpose } from 'vue' 41 +import { ref, onMounted, defineProps, defineExpose, defineEmits } from 'vue'
42 import Taro, { useDidShow } from '@tarojs/taro' 42 import Taro, { useDidShow } from '@tarojs/taro'
43 43
44 -// 定义props 44 +const emit = defineEmits(['collection-complete'])
45 const props = defineProps({ 45 const props = defineProps({
46 height: { 46 height: {
47 type: String, 47 type: String,
...@@ -179,6 +179,9 @@ const collectItem = (item) => { ...@@ -179,6 +179,9 @@ const collectItem = (item) => {
179 totalPoints.value += totalToAdd; 179 totalPoints.value += totalToAdd;
180 180
181 floatingItems.value = floatingItems.value.filter(i => i.id !== item.id); 181 floatingItems.value = floatingItems.value.filter(i => i.id !== item.id);
182 + if (floatingItems.value.length === 0) {
183 + emit('collection-complete', totalPoints.value);
184 + }
182 }, 800); // 动画时长 185 }, 800); // 动画时长
183 } 186 }
184 187
...@@ -206,6 +209,7 @@ const collectAll = async () => { ...@@ -206,6 +209,7 @@ const collectAll = async () => {
206 209
207 floatingItems.value = []; 210 floatingItems.value = [];
208 isCollecting.value = false; 211 isCollecting.value = false;
212 + emit('collection-complete', totalPoints.value);
209 }, totalAnimationTime); 213 }, totalAnimationTime);
210 } 214 }
211 215
......
1 +<template>
2 + <view class="total-points-display" :style="{ height: height }">
3 + <!-- 中心圆形显示总积分 -->
4 + <view class="center-circle1">
5 + <view class="total-points" @tap="handleGoToRewards">
6 + <text class="points-number">{{ animatedTotalPoints }}分</text>
7 + <text class="points-label">去兑换</text>
8 + </view>
9 + </view>
10 + </view>
11 +</template>
12 +
13 +<script setup>
14 +import { ref, watch, onMounted, defineProps } from 'vue'
15 +import Taro from '@tarojs/taro'
16 +
17 +// 定义props
18 +const props = defineProps({
19 + totalPoints: {
20 + type: Number,
21 + required: true,
22 + default: 0
23 + },
24 + height: {
25 + type: String,
26 + default: '30vh'
27 + }
28 +})
29 +
30 +// 响应式数据
31 +const animatedTotalPoints = ref(0)
32 +
33 +/**
34 + * 数字滚动动画
35 + * @param {number} start - 动画开始值
36 + * @param {number} end - 动画结束值
37 + */
38 +const animateNumber = (start, end) => {
39 + if (start === end) {
40 + animatedTotalPoints.value = end;
41 + return;
42 + }
43 + const duration = 800;
44 + const startTime = Date.now();
45 + const difference = end - start;
46 +
47 + const animate = () => {
48 + const elapsed = Date.now() - startTime;
49 + const progress = Math.min(elapsed / duration, 1);
50 + const easeOut = 1 - Math.pow(1 - progress, 3);
51 + animatedTotalPoints.value = Math.floor(start + difference * easeOut);
52 +
53 + if (progress < 1) {
54 + requestAnimationFrame(animate);
55 + } else {
56 + animatedTotalPoints.value = end;
57 + }
58 + };
59 + animate();
60 +}
61 +
62 +// 监听totalPoints变化,并执行动画
63 +watch(() => props.totalPoints, (newValue, oldValue) => {
64 + animateNumber(oldValue, newValue);
65 +})
66 +
67 +onMounted(() => {
68 + animateNumber(0, props.totalPoints)
69 +})
70 +
71 +/**
72 + * 处理去兑换点击事件
73 + */
74 +const handleGoToRewards = () => {
75 + Taro.navigateTo({
76 + url: '/pages/RewardCategories/index',
77 + })
78 +}
79 +</script>
80 +
81 +<style lang="less">
82 +.total-points-display {
83 + position: relative;
84 + width: 100vw;
85 + overflow: hidden;
86 + display: flex;
87 + justify-content: center;
88 + align-items: center;
89 +}
90 +
91 +.center-circle1 {
92 + position: absolute;
93 + left: 50%;
94 + top: 50%;
95 + transform: translate(-50%, -50%);
96 + width: 170rpx;
97 + height: 170rpx;
98 + background: linear-gradient(135deg, #4A90E2, #4d96ea);
99 + border-radius: 50%;
100 + display: flex;
101 + align-items: center;
102 + justify-content: center;
103 + box-shadow: 0 8rpx 32rpx rgba(53, 144, 255, 0.3);
104 + z-index: 10;
105 +}
106 +
107 +.total-points {
108 + text-align: center;
109 + color: white;
110 +}
111 +
112 +.points-number {
113 + display: block;
114 + font-size: 48rpx;
115 + font-weight: bold;
116 + line-height: 1;
117 +}
118 +
119 +.points-label {
120 + display: block;
121 + font-size: 24rpx;
122 + margin-top: 8rpx;
123 + opacity: 0.9;
124 +}
125 +
126 +// 响应式适配
127 +@media screen and (max-width: 750px) {
128 + .center-circle {
129 + width: 160rpx;
130 + height: 160rpx;
131 + }
132 +
133 + .points-number {
134 + font-size: 36rpx;
135 + }
136 +
137 + .points-label {
138 + font-size: 20rpx;
139 + }
140 +}
141 +</style>
...@@ -55,7 +55,20 @@ ...@@ -55,7 +55,20 @@
55 55
56 <!-- Points circles --> 56 <!-- Points circles -->
57 <view class="flex justify-between px-5 py-6 my-4 bg-white rounded-xl shadow-md mx-4"> 57 <view class="flex justify-between px-5 py-6 my-4 bg-white rounded-xl shadow-md mx-4">
58 - <PointsCollector ref="pointsCollectorRef" height="30vh" /> 58 + <template v-if="!showTotalPointsOnly">
59 + <PointsCollector
60 + ref="pointsCollectorRef"
61 + height="30vh"
62 + :total-points="finalTotalPoints"
63 + @collection-complete="handleCollectionComplete"
64 + />
65 + </template>
66 + <template v-else>
67 + <TotalPointsDisplay
68 + :total-points="finalTotalPoints"
69 + height="13vh"
70 + />
71 + </template>
59 </view> 72 </view>
60 73
61 <!-- Photo button --> 74 <!-- Photo button -->
...@@ -184,6 +197,7 @@ import { ref, computed, onMounted } from 'vue'; ...@@ -184,6 +197,7 @@ import { ref, computed, onMounted } from 'vue';
184 import Taro, { useDidShow } from '@tarojs/taro'; 197 import Taro, { useDidShow } from '@tarojs/taro';
185 import { Setting, Photograph, Right, Close, Category } from '@nutui/icons-vue-taro'; 198 import { Setting, Photograph, Right, Close, Category } from '@nutui/icons-vue-taro';
186 import BottomNav from '../../components/BottomNav.vue'; 199 import BottomNav from '../../components/BottomNav.vue';
200 +import TotalPointsDisplay from '@/components/TotalPointsDisplay.vue';
187 import PointsCollector from '@/components/PointsCollector.vue' 201 import PointsCollector from '@/components/PointsCollector.vue'
188 import WeRunAuth from '@/components/WeRunAuth.vue' 202 import WeRunAuth from '@/components/WeRunAuth.vue'
189 import { useMediaPreview } from '@/composables/useMediaPreview'; 203 import { useMediaPreview } from '@/composables/useMediaPreview';
...@@ -192,6 +206,8 @@ const todaySteps = ref(0); ...@@ -192,6 +206,8 @@ const todaySteps = ref(0);
192 const isWeRunAuthorized = ref(false); 206 const isWeRunAuthorized = ref(false);
193 const pointsCollectorRef = ref(null) 207 const pointsCollectorRef = ref(null)
194 const weRunAuthRef = ref(null) 208 const weRunAuthRef = ref(null)
209 +const showTotalPointsOnly = ref(false)
210 +const finalTotalPoints = ref(0)
195 211
196 const familyName = ref('') 212 const familyName = ref('')
197 const familySlogn = ref('') 213 const familySlogn = ref('')
...@@ -240,6 +256,15 @@ const handleCollectAll = () => { ...@@ -240,6 +256,15 @@ const handleCollectAll = () => {
240 } 256 }
241 257
242 /** 258 /**
259 + * 处理积分收集完成事件
260 + * @param {number} points - 最终的积分
261 + */
262 +const handleCollectionComplete = (points) => {
263 + finalTotalPoints.value = points;
264 + showTotalPointsOnly.value = true;
265 +};
266 +
267 +/**
243 * 手动刷新微信步数 268 * 手动刷新微信步数
244 */ 269 */
245 const handleRefreshSteps = async () => { 270 const handleRefreshSteps = async () => {
...@@ -348,6 +373,11 @@ useDidShow(() => { ...@@ -348,6 +373,11 @@ useDidShow(() => {
348 // Taro.redirectTo({ url: '/pages/Welcome/index' }); 373 // Taro.redirectTo({ url: '/pages/Welcome/index' });
349 // } 374 // }
350 initPageData(); 375 initPageData();
376 +
377 + // TODO: 真实情况下 需要重新获取积分列表, 测试随机积分获取状态
378 + showTotalPointsOnly.value = Math.random() > 0.5 ? true : false;
379 + // 总积分数量也要从接口获取传进去
380 + finalTotalPoints.value = 10086;
351 }) 381 })
352 </script> 382 </script>
353 383
......