hookehuyr

feat(排行榜): 添加活动排行榜图标并优化切换动画和样式

为活动排行榜按钮添加图标并优化家庭排行榜页面的交互体验
- 在活动排行榜按钮前添加分类图标
- 实现平滑的标签切换动画和指示器
- 优化排名卡片和交互元素的悬停效果
- 调整排行榜布局和视觉细节
...@@ -83,6 +83,7 @@ ...@@ -83,6 +83,7 @@
83 <view class="px-5 mb-4 mt-4"> 83 <view class="px-5 mb-4 mt-4">
84 <view @tap="openFamilyRank" class="w-full bg-blue-500 text-white py-3 rounded-lg flex flex-col items-center justify-center"> 84 <view @tap="openFamilyRank" class="w-full bg-blue-500 text-white py-3 rounded-lg flex flex-col items-center justify-center">
85 <view class="flex items-center justify-center"> 85 <view class="flex items-center justify-center">
86 + <Category size="16" class="mr-2" />
86 活动排行榜 87 活动排行榜
87 </view> 88 </view>
88 </view> 89 </view>
...@@ -172,7 +173,7 @@ ...@@ -172,7 +173,7 @@
172 <script setup> 173 <script setup>
173 import { ref, computed, onMounted } from 'vue'; 174 import { ref, computed, onMounted } from 'vue';
174 import Taro, { useDidShow } from '@tarojs/taro'; 175 import Taro, { useDidShow } from '@tarojs/taro';
175 -import { Setting, Photograph, Right, Close } from '@nutui/icons-vue-taro'; 176 +import { Setting, Photograph, Right, Close, Category } from '@nutui/icons-vue-taro';
176 import BottomNav from '../../components/BottomNav.vue'; 177 import BottomNav from '../../components/BottomNav.vue';
177 import PointsCollector from '@/components/PointsCollector.vue' 178 import PointsCollector from '@/components/PointsCollector.vue'
178 import WeRunAuth from '@/components/WeRunAuth.vue' 179 import WeRunAuth from '@/components/WeRunAuth.vue'
......
1 <!-- 1 <!--
2 * @Date: 2025-09-01 13:07:52 2 * @Date: 2025-09-01 13:07:52
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2025-09-01 14:46:59 4 + * @LastEditTime: 2025-09-01 16:05:41
5 * @FilePath: /lls_program/src/pages/FamilyRank/index.vue 5 * @FilePath: /lls_program/src/pages/FamilyRank/index.vue
6 * @Description: 文件描述 6 * @Description: 文件描述
7 --> 7 -->
...@@ -10,6 +10,11 @@ ...@@ -10,6 +10,11 @@
10 <!-- 顶部导航 --> 10 <!-- 顶部导航 -->
11 <view class="header"> 11 <view class="header">
12 <view class="nav-tabs"> 12 <view class="nav-tabs">
13 + <!-- 滑动指示器 -->
14 + <view
15 + class="tab-indicator"
16 + :class="{ 'indicator-shanghai': activeTab === 'shanghai' }"
17 + ></view>
13 <view 18 <view
14 class="tab-item" 19 class="tab-item"
15 :class="{ active: activeTab === 'huangpu' }" 20 :class="{ active: activeTab === 'huangpu' }"
...@@ -28,7 +33,7 @@ ...@@ -28,7 +33,7 @@
28 </view> 33 </view>
29 34
30 <!-- 排行榜内容 --> 35 <!-- 排行榜内容 -->
31 - <view class="rank-content"> 36 + <view class="rank-content" :class="{ 'content-switching': isContentSwitching }">
32 <!-- 前三名展示 --> 37 <!-- 前三名展示 -->
33 <view class="top-three"> 38 <view class="top-three">
34 <!-- 第二名 --> 39 <!-- 第二名 -->
...@@ -126,12 +131,28 @@ import { ref, computed } from 'vue' ...@@ -126,12 +131,28 @@ import { ref, computed } from 'vue'
126 // 当前激活的tab 131 // 当前激活的tab
127 const activeTab = ref('huangpu') 132 const activeTab = ref('huangpu')
128 133
134 +// 内容切换状态
135 +const isContentSwitching = ref(false)
136 +
129 /** 137 /**
130 * 切换tab 138 * 切换tab
131 * @param {string} tab - tab名称 139 * @param {string} tab - tab名称
132 */ 140 */
133 const switchTab = (tab) => { 141 const switchTab = (tab) => {
134 - activeTab.value = tab 142 + if (activeTab.value === tab) return
143 +
144 + // 开始切换动画
145 + isContentSwitching.value = true
146 +
147 + // 延迟切换内容,让淡出动画先执行
148 + setTimeout(() => {
149 + activeTab.value = tab
150 +
151 + // 内容切换后,结束切换状态,开始淡入动画
152 + setTimeout(() => {
153 + isContentSwitching.value = false
154 + }, 50)
155 + }, 200)
135 } 156 }
136 157
137 /** 158 /**
...@@ -250,6 +271,25 @@ const myRank = ref({ ...@@ -250,6 +271,25 @@ const myRank = ref({
250 border-radius: 60rpx; 271 border-radius: 60rpx;
251 padding: 8rpx; 272 padding: 8rpx;
252 margin: 0 80rpx; 273 margin: 0 80rpx;
274 + position: relative;
275 + overflow: hidden;
276 +
277 + .tab-indicator {
278 + position: absolute;
279 + top: 8rpx;
280 + left: 8rpx;
281 + width: calc(50% - 8rpx);
282 + height: calc(100% - 16rpx);
283 + background: rgba(255, 255, 255, 1);
284 + border-radius: 52rpx;
285 + transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
286 + box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
287 + z-index: 1;
288 +
289 + &.indicator-shanghai {
290 + transform: translateX(100%);
291 + }
292 + }
253 293
254 .tab-item { 294 .tab-item {
255 flex: 1; 295 flex: 1;
...@@ -260,12 +300,18 @@ const myRank = ref({ ...@@ -260,12 +300,18 @@ const myRank = ref({
260 font-size: 28rpx; 300 font-size: 28rpx;
261 font-weight: 500; 301 font-weight: 500;
262 transition: all 0.3s ease; 302 transition: all 0.3s ease;
303 + position: relative;
304 + z-index: 2;
305 + cursor: pointer;
263 306
264 &.active { 307 &.active {
265 - background: rgba(255, 255, 255, 1);
266 color: #4A90E2; 308 color: #4A90E2;
267 font-weight: 600; 309 font-weight: 600;
268 - box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1); 310 + transform: scale(1.02);
311 + }
312 +
313 + &:hover {
314 + color: rgba(255, 255, 255, 1);
269 } 315 }
270 } 316 }
271 } 317 }
...@@ -273,6 +319,14 @@ const myRank = ref({ ...@@ -273,6 +319,14 @@ const myRank = ref({
273 319
274 .rank-content { 320 .rank-content {
275 padding: 0 40rpx; 321 padding: 0 40rpx;
322 + transition: all 0.3s ease;
323 + transform: translateY(0);
324 + opacity: 1;
325 +
326 + &.content-switching {
327 + opacity: 0.3;
328 + transform: translateY(-20rpx);
329 + }
276 } 330 }
277 331
278 .top-three { 332 .top-three {
...@@ -281,7 +335,7 @@ const myRank = ref({ ...@@ -281,7 +335,7 @@ const myRank = ref({
281 justify-content: center; 335 justify-content: center;
282 align-items: flex-end; 336 align-items: flex-end;
283 margin: 180rpx 0 0 0; 337 margin: 180rpx 0 0 0;
284 - height: 400rpx; 338 + height: 360rpx;
285 gap: -40rpx; 339 gap: -40rpx;
286 340
287 .rank-item { 341 .rank-item {
...@@ -289,17 +343,22 @@ const myRank = ref({ ...@@ -289,17 +343,22 @@ const myRank = ref({
289 flex-direction: column; 343 flex-direction: column;
290 align-items: center; 344 align-items: center;
291 position: relative; 345 position: relative;
346 + transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
347 +
348 + &:hover {
349 + transform: translateY(-10rpx);
350 + }
292 351
293 .crown { 352 .crown {
294 font-size: 40rpx; 353 font-size: 40rpx;
295 margin-bottom: 10rpx; 354 margin-bottom: 10rpx;
296 355
297 &.crown-gold { 356 &.crown-gold {
298 - filter: hue-rotate(45deg) brightness(1.2); 357 + filter: grayscale(0.3) brightness(1.1);
299 } 358 }
300 359
301 &.crown-silver { 360 &.crown-silver {
302 - filter: grayscale(0.3) brightness(1.1); 361 + filter: hue-rotate(45deg) brightness(1.2);
303 } 362 }
304 363
305 &.crown-bronze { 364 &.crown-bronze {
...@@ -377,7 +436,7 @@ const myRank = ref({ ...@@ -377,7 +436,7 @@ const myRank = ref({
377 436
378 .rank-number { 437 .rank-number {
379 width: 220rpx; 438 width: 220rpx;
380 - height: 200rpx; 439 + height: 180rpx;
381 background: linear-gradient(135deg, #C0C0C0 0%, #A0A0A0 100%); 440 background: linear-gradient(135deg, #C0C0C0 0%, #A0A0A0 100%);
382 } 441 }
383 } 442 }
...@@ -395,7 +454,7 @@ const myRank = ref({ ...@@ -395,7 +454,7 @@ const myRank = ref({
395 454
396 .rank-number { 455 .rank-number {
397 width: 220rpx; 456 width: 220rpx;
398 - height: 260rpx; 457 + height: 220rpx;
399 background: linear-gradient(135deg, #FFD700 0%, #FFA500 100%); 458 background: linear-gradient(135deg, #FFD700 0%, #FFA500 100%);
400 } 459 }
401 } 460 }
...@@ -412,7 +471,7 @@ const myRank = ref({ ...@@ -412,7 +471,7 @@ const myRank = ref({
412 471
413 .rank-number { 472 .rank-number {
414 width: 220rpx; 473 width: 220rpx;
415 - height: 180rpx; 474 + height: 160rpx;
416 background: linear-gradient(135deg, #CD7F32 0%, #B8860B 100%); 475 background: linear-gradient(135deg, #CD7F32 0%, #B8860B 100%);
417 } 476 }
418 } 477 }
...@@ -433,6 +492,12 @@ const myRank = ref({ ...@@ -433,6 +492,12 @@ const myRank = ref({
433 justify-content: space-between; 492 justify-content: space-between;
434 padding: 24rpx 32rpx; 493 padding: 24rpx 32rpx;
435 border-bottom: 1rpx solid rgba(0, 0, 0, 0.05); 494 border-bottom: 1rpx solid rgba(0, 0, 0, 0.05);
495 + transition: all 0.3s ease;
496 +
497 + &:hover {
498 + background: rgba(74, 144, 226, 0.05);
499 + transform: translateX(8rpx);
500 + }
436 501
437 &:last-child { 502 &:last-child {
438 border-bottom: none; 503 border-bottom: none;
...@@ -505,6 +570,12 @@ const myRank = ref({ ...@@ -505,6 +570,12 @@ const myRank = ref({
505 backdrop-filter: blur(10px); 570 backdrop-filter: blur(10px);
506 box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.1); 571 box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.1);
507 z-index: 99; 572 z-index: 99;
573 + transition: all 0.3s ease;
574 +
575 + &:hover {
576 + transform: translateY(-4rpx);
577 + box-shadow: 0 12rpx 40rpx rgba(0, 0, 0, 0.15);
578 + }
508 579
509 .my-rank-content { 580 .my-rank-content {
510 display: flex; 581 display: flex;
......