998 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			Vue
		
	
	
	
			
		
		
	
	
			998 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			Vue
		
	
	
	
| <template>
 | ||
|   <view>
 | ||
|     <!-- 直推人数排行榜弹窗 -->
 | ||
|     <u-popup
 | ||
|       class="ranking-popup"
 | ||
|       mode="center"
 | ||
|       :show="showPeopleRanking"
 | ||
|       :closeOnClickOverlay="false"
 | ||
|       border-radius="20"
 | ||
|     >
 | ||
|       <view style="width: 90vw; height: 85vh" class="popup-container">
 | ||
|         <!-- 弹窗头部 -->
 | ||
|         <view class="popup-header">
 | ||
|           <view class="header-title">直推人数排行榜</view>
 | ||
|         </view>
 | ||
| 
 | ||
|         <!-- 前三名特殊展示区域 -->
 | ||
|         <view class="top-three-section">
 | ||
|           <view class="podium-container">
 | ||
|             <!-- 第二名 -->
 | ||
|             <view class="podium-item second-place" v-if="peopleTopThree[1]">
 | ||
|               <view class="player-area">
 | ||
|                 <view class="rank-number">
 | ||
|                   <img src="@/static/images/rank-2.svg" alt="" />
 | ||
|                 </view>
 | ||
|                 <!-- <view class="member-code">{{
 | ||
|                   peopleTopThree[1].memberCode
 | ||
|                 }}</view> -->
 | ||
|                 <view
 | ||
|                   class="member-name"
 | ||
|                   :class="{ highlight: peopleTopThree[1].isLoginMember == 1 }"
 | ||
|                 >
 | ||
|                   {{ peopleTopThree[1].memberName | formatMemberName }}
 | ||
|                 </view>
 | ||
| 
 | ||
|                 <view class="score">{{ peopleTopThree[1].count }}人</view>
 | ||
|               </view>
 | ||
|             </view>
 | ||
| 
 | ||
|             <!-- 第一名 -->
 | ||
|             <view class="podium-item first-place" v-if="peopleTopThree[0]">
 | ||
|               <view class="player-area">
 | ||
|                 <view class="rank-number">
 | ||
|                   <img src="@/static/images/rank-1.svg" alt="" />
 | ||
|                 </view>
 | ||
| 
 | ||
|                 <!-- <view class="member-code">
 | ||
|                   {{ peopleTopThree[0].memberCode }}
 | ||
|                 </view> -->
 | ||
|                 <view
 | ||
|                   class="member-name"
 | ||
|                   :class="{ highlight: peopleTopThree[0].isLoginMember == 1 }"
 | ||
|                 >
 | ||
|                   {{ peopleTopThree[0].memberName | formatMemberName }}
 | ||
|                 </view>
 | ||
|                 <view class="score">{{ peopleTopThree[0].count }}人</view>
 | ||
|               </view>
 | ||
|             </view>
 | ||
| 
 | ||
|             <!-- 第三名 -->
 | ||
|             <view class="podium-item third-place" v-if="peopleTopThree[2]">
 | ||
|               <view class="player-area">
 | ||
|                 <view class="rank-number">
 | ||
|                   <img src="@/static/images/rank-3.svg" alt="" />
 | ||
|                 </view>
 | ||
|                 <!-- <view class="member-code">{{
 | ||
|                   peopleTopThree[2].memberCode
 | ||
|                 }}</view> -->
 | ||
|                 <view
 | ||
|                   class="member-name"
 | ||
|                   :class="{ highlight: peopleTopThree[2].isLoginMember == 1 }"
 | ||
|                 >
 | ||
|                   {{ peopleTopThree[2].memberName | formatMemberName }}
 | ||
|                 </view>
 | ||
| 
 | ||
|                 <view class="score">{{ peopleTopThree[2].count }}人</view>
 | ||
|               </view>
 | ||
|             </view>
 | ||
|           </view>
 | ||
|         </view>
 | ||
| 
 | ||
|         <!-- 4-30名滚动列表 -->
 | ||
|         <view class="ranking-list">
 | ||
|           <scroll-view
 | ||
|             class="scroll-container"
 | ||
|             scroll-y="true"
 | ||
|             :show-scrollbar="false"
 | ||
|           >
 | ||
|             <view
 | ||
|               class="list-item"
 | ||
|               v-for="(item, index) in peopleRemainingList"
 | ||
|               :key="index"
 | ||
|             >
 | ||
|               <view class="item-rank">{{ index + 4 }}</view>
 | ||
|               <view class="item-info">
 | ||
|                 <!-- <view class="item-code">{{ item.memberCode }}</view> -->
 | ||
|                 <view
 | ||
|                   class="item-name"
 | ||
|                   :class="{ highlight: item.isLoginMember == 1 }"
 | ||
|                 >
 | ||
|                   {{ item.memberName | formatMemberName }}
 | ||
|                 </view>
 | ||
|               </view>
 | ||
|               <view class="item-score">
 | ||
|                 <span>{{ item.count }}</span>
 | ||
|                 <span>人</span>
 | ||
|               </view>
 | ||
|             </view>
 | ||
|             <view class="list-footer" v-if="peopleRemainingList.length === 0">
 | ||
|               <text class="empty-text">暂无更多数据</text>
 | ||
|             </view>
 | ||
|           </scroll-view>
 | ||
|         </view>
 | ||
| 
 | ||
|         <!-- 底部关闭按钮 -->
 | ||
|         <view class="popup-footer">
 | ||
|           <view class="close-text-btn" @click="closePeopleRanking">
 | ||
|             关 闭
 | ||
|           </view>
 | ||
|         </view>
 | ||
|       </view>
 | ||
|     </u-popup>
 | ||
| 
 | ||
|     <!-- 直推金额排行榜弹窗 -->
 | ||
|     <u-popup
 | ||
|       class="ranking-popup"
 | ||
|       width="85%"
 | ||
|       height="85%"
 | ||
|       mode="center"
 | ||
|       :show="showAmountRanking"
 | ||
|       :closeOnClickOverlay="false"
 | ||
|       border-radius="20"
 | ||
|     >
 | ||
|       <view style="width: 90vw; height: 85vh" class="popup-container">
 | ||
|         <!-- 弹窗头部 -->
 | ||
|         <view class="popup-header">
 | ||
|           <view class="header-title">直推金额排行榜</view>
 | ||
|         </view>
 | ||
| 
 | ||
|         <!-- 前三名特殊展示区域 -->
 | ||
|         <view class="top-three-section">
 | ||
|           <view class="podium-container">
 | ||
|             <!-- 第二名 -->
 | ||
|             <view class="podium-item second-place" v-if="amountTopThree[1]">
 | ||
|               <view class="player-area">
 | ||
|                 <view class="rank-number">
 | ||
|                   <img src="@/static/images/rank-2.svg" alt="" />
 | ||
|                 </view>
 | ||
|                 <view
 | ||
|                   class="member-name"
 | ||
|                   :class="{ highlight: amountTopThree[1].isLoginMember == 1 }"
 | ||
|                 >
 | ||
|                   {{ amountTopThree[1].memberName | formatMemberName }}
 | ||
|                 </view>
 | ||
|                 <!-- <view class="member-code">{{
 | ||
|                   amountTopThree[1].memberCode
 | ||
|                 }}</view> -->
 | ||
|                 <view class="score">{{
 | ||
|                   formatAmount(amountTopThree[1].amount)
 | ||
|                 }}</view>
 | ||
|               </view>
 | ||
|             </view>
 | ||
| 
 | ||
|             <!-- 第一名 -->
 | ||
|             <view class="podium-item first-place" v-if="amountTopThree[0]">
 | ||
|               <view class="player-area">
 | ||
|                 <view class="rank-number">
 | ||
|                   <img src="@/static/images/rank-1.svg" alt="" />
 | ||
|                 </view>
 | ||
|                 <view
 | ||
|                   class="member-name"
 | ||
|                   :class="{ highlight: amountTopThree[0].isLoginMember == 1 }"
 | ||
|                 >
 | ||
|                   {{ amountTopThree[0].memberName | formatMemberName }}
 | ||
|                 </view>
 | ||
|                 <!-- <view class="member-code">{{
 | ||
|                   amountTopThree[0].memberCode
 | ||
|                 }}</view> -->
 | ||
|                 <view class="score">{{
 | ||
|                   formatAmount(amountTopThree[0].amount)
 | ||
|                 }}</view>
 | ||
|               </view>
 | ||
|             </view>
 | ||
| 
 | ||
|             <!-- 第三名 -->
 | ||
|             <view class="podium-item third-place" v-if="amountTopThree[2]">
 | ||
|               <view class="player-area">
 | ||
|                 <view class="rank-number">
 | ||
|                   <img src="@/static/images/rank-3.svg" alt="" />
 | ||
|                 </view>
 | ||
|                 <view
 | ||
|                   class="member-name"
 | ||
|                   :class="{ highlight: amountTopThree[2].isLoginMember == 1 }"
 | ||
|                 >
 | ||
|                   {{ amountTopThree[2].memberName | formatMemberName }}
 | ||
|                 </view>
 | ||
|                 <!-- <view class="member-code">{{
 | ||
|                   amountTopThree[2].memberCode
 | ||
|                 }}</view> -->
 | ||
|                 <view class="score">{{
 | ||
|                   formatAmount(amountTopThree[2].amount)
 | ||
|                 }}</view>
 | ||
|               </view>
 | ||
|             </view>
 | ||
|           </view>
 | ||
|         </view>
 | ||
| 
 | ||
|         <!-- 4-30名滚动列表 -->
 | ||
|         <view class="ranking-list">
 | ||
|           <!-- <view class="list-header">
 | ||
|             <text class="list-title">完整排行榜</text>
 | ||
|           </view> -->
 | ||
|           <scroll-view
 | ||
|             class="scroll-container"
 | ||
|             scroll-y="true"
 | ||
|             :show-scrollbar="false"
 | ||
|           >
 | ||
|             <view
 | ||
|               class="list-item"
 | ||
|               v-for="(item, index) in amountRemainingList"
 | ||
|               :key="index"
 | ||
|             >
 | ||
|               <view class="item-rank">{{ index + 4 }}</view>
 | ||
|               <view class="item-info">
 | ||
|                 <!-- <view class="item-code">{{ item.memberCode }}</view> -->
 | ||
|                 <view
 | ||
|                   class="item-name"
 | ||
|                   :class="{ highlight: item.isLoginMember == 1 }"
 | ||
|                 >
 | ||
|                   {{ item.memberName | formatMemberName }}
 | ||
|                 </view>
 | ||
|               </view>
 | ||
|               <view class="item-score">
 | ||
|                 <span>{{ item.amount }}</span>
 | ||
|               </view>
 | ||
|             </view>
 | ||
|             <view class="list-footer" v-if="amountRemainingList.length === 0">
 | ||
|               <text class="empty-text">暂无更多数据</text>
 | ||
|             </view>
 | ||
|           </scroll-view>
 | ||
|         </view>
 | ||
| 
 | ||
|         <!-- 底部关闭按钮 -->
 | ||
|         <view class="popup-footer">
 | ||
|           <view class="close-text-btn" @click="closeAmountRanking">
 | ||
|             关 闭
 | ||
|           </view>
 | ||
|         </view>
 | ||
|       </view>
 | ||
|     </u-popup>
 | ||
| 
 | ||
|     <!-- 加载状态 -->
 | ||
|     <u-loading-page
 | ||
|       :loading="loading"
 | ||
|       loading-text="加载中..."
 | ||
|     ></u-loading-page>
 | ||
|   </view>
 | ||
| </template>
 | ||
| 
 | ||
| <script>
 | ||
| import * as api from '@/config/index.js'
 | ||
| 
 | ||
| export default {
 | ||
|   name: 'RankingPopup',
 | ||
|   data() {
 | ||
|     return {
 | ||
|       // 控制弹窗显示
 | ||
|       showPeopleRanking: false,
 | ||
|       showAmountRanking: false,
 | ||
|       loading: false,
 | ||
| 
 | ||
|       // 人数排行数据
 | ||
|       peopleRankingList: [],
 | ||
|       peopleTopThree: [],
 | ||
|       peopleRemainingList: [],
 | ||
| 
 | ||
|       // 金额排行数据
 | ||
|       amountRankingList: [],
 | ||
|       amountTopThree: [],
 | ||
|       amountRemainingList: [],
 | ||
| 
 | ||
|       // 用户信息
 | ||
|       userInfo: uni.getStorageSync('User') || {},
 | ||
|     }
 | ||
|   },
 | ||
|   filters: {
 | ||
|     formatMemberName(val) {
 | ||
|       return val?.slice(0, 8) || ''
 | ||
|     },
 | ||
|   },
 | ||
|   methods: {
 | ||
|     // 显示排行榜弹窗(先显示人数排行)
 | ||
|     async showRankingPopups() {
 | ||
|       this.loading = true
 | ||
|       try {
 | ||
|         // 并行加载两个排行榜数据
 | ||
|         await Promise.all([this.loadPeopleRanking(), this.loadAmountRanking()])
 | ||
|         // 先显示人数排行榜
 | ||
|         if (this.peopleTopThree.length > 0) {
 | ||
|           this.showPeopleRanking = true
 | ||
|         } else if (this.amountTopThree.length > 0) {
 | ||
|           this.showAmountRanking = true
 | ||
|         } else {
 | ||
|           this.$emit('onRankingComplete')
 | ||
|         }
 | ||
|       } catch (error) {
 | ||
|         console.error('加载排行榜数据失败:', error)
 | ||
|         uni.showToast({
 | ||
|           title: '加载失败',
 | ||
|           icon: 'error',
 | ||
|         })
 | ||
|       } finally {
 | ||
|         this.loading = false
 | ||
|       }
 | ||
|     },
 | ||
| 
 | ||
|     // 获取当前年月参数
 | ||
|     getCurrentYearMonth() {
 | ||
|       const now = new Date()
 | ||
|       const year = now.getFullYear()
 | ||
|       const month = String(now.getMonth() + 1).padStart(2, '0') // 月份补0
 | ||
|       return { year, month }
 | ||
|     },
 | ||
| 
 | ||
|     // 加载人数排行榜数据
 | ||
|     async loadPeopleRanking() {
 | ||
|       try {
 | ||
|         const { year, month } = this.getCurrentYearMonth()
 | ||
|         const params = {
 | ||
|           year,
 | ||
|           month,
 | ||
|           faker: true,
 | ||
|         }
 | ||
|         const res = await api.getTopPeople(params)
 | ||
|         console.log(res.data)
 | ||
|         if (res.code === 200 && res.data) {
 | ||
|           // 数据字段映射
 | ||
|           const mappedData = res.data.map(item => ({
 | ||
|             ...item,
 | ||
|             memberName: item.memberName,
 | ||
|             memberCode: item.memberCode,
 | ||
|             count: item.numberOfPeople, // 人数字段映射
 | ||
|             isLoginMember: item.memberId === this.userInfo.memberId ? 1 : 0, // 判断是否为当前用户
 | ||
|           }))
 | ||
|           this.peopleRankingList = mappedData.slice(0, 30) // 取前30名
 | ||
|           this.peopleTopThree = this.peopleRankingList.slice(0, 3)
 | ||
|           this.peopleRemainingList = this.peopleRankingList.slice(3)
 | ||
|         }
 | ||
|       } catch (error) {
 | ||
|         console.error('加载人数排行榜失败:', error)
 | ||
|         throw error
 | ||
|       }
 | ||
|     },
 | ||
| 
 | ||
|     // 加载金额排行榜数据
 | ||
|     async loadAmountRanking() {
 | ||
|       try {
 | ||
|         const { year, month } = this.getCurrentYearMonth()
 | ||
|         const params = {
 | ||
|           year,
 | ||
|           month,
 | ||
|           faker: true,
 | ||
|         }
 | ||
|         const res = await api.getTopAmount(params)
 | ||
|         if (res.code === 200 && res.data) {
 | ||
|           // 数据字段映射
 | ||
|           const mappedData = res.data.map(item => ({
 | ||
|             ...item,
 | ||
|             memberName: item.memberName,
 | ||
|             memberCode: item.memberCode,
 | ||
|             amount: item.numberOfAmount, // 金额字段映射
 | ||
|             isLoginMember: item.memberId === this.userInfo.memberId ? 1 : 0, // 判断是否为当前用户
 | ||
|           }))
 | ||
|           this.amountRankingList = mappedData.slice(0, 30) // 取前30名
 | ||
|           this.amountTopThree = this.amountRankingList.slice(0, 3)
 | ||
|           this.amountRemainingList = this.amountRankingList.slice(3)
 | ||
|         }
 | ||
|       } catch (error) {
 | ||
|         console.error('加载金额排行榜失败:', error)
 | ||
|         throw error
 | ||
|       }
 | ||
|     },
 | ||
| 
 | ||
|     // 关闭人数排行榜,显示金额排行榜
 | ||
|     closePeopleRanking() {
 | ||
|       this.showPeopleRanking = false
 | ||
|       if (this.amountTopThree.length > 0) {
 | ||
|         setTimeout(() => {
 | ||
|           this.showAmountRanking = true
 | ||
|         }, 300)
 | ||
|       } else {
 | ||
|         this.$emit('onRankingComplete')
 | ||
|       }
 | ||
|     },
 | ||
| 
 | ||
|     // 关闭金额排行榜
 | ||
|     closeAmountRanking() {
 | ||
|       this.showAmountRanking = false
 | ||
|       // 触发完成事件
 | ||
|       this.$emit('onRankingComplete')
 | ||
|     },
 | ||
| 
 | ||
|     // 格式化金额显示
 | ||
|     formatAmount(amount) {
 | ||
|       return amount
 | ||
|     },
 | ||
|   },
 | ||
| }
 | ||
| </script>
 | ||
| 
 | ||
| <style lang="scss" scoped>
 | ||
| ::v-deep .u-popup__content {
 | ||
|   background-color: rgba(0, 0, 0, 0);
 | ||
| }
 | ||
| 
 | ||
| .popup-container {
 | ||
|   width: 100%;
 | ||
|   height: 100%;
 | ||
|   background: linear-gradient(
 | ||
|     135deg,
 | ||
|     #003d7a 0%,
 | ||
|     #005bac 30%,
 | ||
|     #0077cc 70%,
 | ||
|     #4a90e2 100%
 | ||
|   );
 | ||
|   border-radius: 20rpx;
 | ||
|   display: flex;
 | ||
|   flex-direction: column;
 | ||
|   overflow: hidden;
 | ||
|   position: relative;
 | ||
| 
 | ||
|   // 添加微妙的光效
 | ||
|   &::before {
 | ||
|     content: '';
 | ||
|     position: absolute;
 | ||
|     top: 0;
 | ||
|     left: 0;
 | ||
|     right: 0;
 | ||
|     bottom: 0;
 | ||
|     background: radial-gradient(
 | ||
|       ellipse at 30% 20%,
 | ||
|       rgba(255, 255, 255, 0.1) 0%,
 | ||
|       transparent 50%
 | ||
|     );
 | ||
|     pointer-events: none;
 | ||
|     z-index: 1;
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| @keyframes sparkle {
 | ||
|   0% {
 | ||
|     transform: translateY(0);
 | ||
|   }
 | ||
|   100% {
 | ||
|     transform: translateY(-100rpx);
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| .popup-header {
 | ||
|   padding: 30rpx 40rpx 20rpx;
 | ||
|   flex-shrink: 0;
 | ||
|   position: relative;
 | ||
|   z-index: 2;
 | ||
| 
 | ||
|   .header-title {
 | ||
|     font-size: 38rpx;
 | ||
|     font-weight: bold;
 | ||
|     color: #ffffff;
 | ||
|     text-shadow:
 | ||
|       0 2rpx 8rpx rgba(0, 0, 0, 0.6),
 | ||
|       0 0 15rpx rgba(74, 144, 226, 0.8),
 | ||
|       0 0 25rpx rgba(255, 255, 255, 0.4);
 | ||
|     text-align: center;
 | ||
|     position: relative;
 | ||
|     animation: title-bling 2s ease-in-out infinite alternate;
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| .top-three-section {
 | ||
|   padding: 20rpx 20rpx 40rpx;
 | ||
|   flex-shrink: 0;
 | ||
|   position: relative;
 | ||
|   z-index: 2;
 | ||
| }
 | ||
| 
 | ||
| .podium-container {
 | ||
|   display: flex;
 | ||
|   justify-content: center;
 | ||
|   align-items: flex-end;
 | ||
|   position: relative;
 | ||
|   margin: 0 10rpx;
 | ||
|   height: 290rpx;
 | ||
| 
 | ||
|   // 添加领奖台基座
 | ||
|   // &::after {
 | ||
|   //   content: '';
 | ||
|   //   position: absolute;
 | ||
|   //   bottom: 0;
 | ||
|   //   left: 50%;
 | ||
|   //   transform: translateX(-50%);
 | ||
|   //   width: 85%;
 | ||
|   //   height: 30rpx;
 | ||
|   //   background: linear-gradient(135deg, #424242 0%, #616161 50%, #424242 100%);
 | ||
|   //   border-radius: 15rpx 15rpx 0 0;
 | ||
|   //   box-shadow: 0 -6rpx 20rpx rgba(0, 0, 0, 0.3);
 | ||
|   // }
 | ||
| }
 | ||
| 
 | ||
| .podium-item {
 | ||
|   display: flex;
 | ||
|   flex-direction: column;
 | ||
|   align-items: center;
 | ||
|   position: relative;
 | ||
|   margin: 0 8rpx;
 | ||
| 
 | ||
|   .player-area {
 | ||
|     border-radius: 20rpx;
 | ||
|     min-width: 140rpx;
 | ||
|     text-align: center;
 | ||
|     transition: all 0.3s ease;
 | ||
|     display: flex;
 | ||
|     flex-direction: column;
 | ||
|     align-items: center;
 | ||
|     justify-content: flex-end;
 | ||
|     position: relative;
 | ||
|     z-index: 3;
 | ||
|   }
 | ||
| 
 | ||
|   // 创建领奖台台阶
 | ||
|   // &::after {
 | ||
|   //   content: '';
 | ||
|   //   position: absolute;
 | ||
|   //   bottom: 0;
 | ||
|   //   left: 50%;
 | ||
|   //   transform: translateX(-50%);
 | ||
|   //   border-radius: 12rpx 12rpx 0 0;
 | ||
|   //   box-shadow:
 | ||
|   //     0 -6rpx 15rpx rgba(0, 0, 0, 0.25),
 | ||
|   //     inset 0 3rpx 6rpx rgba(255, 255, 255, 0.15);
 | ||
|   // }
 | ||
| 
 | ||
|   .rank-number {
 | ||
|     border-radius: 50%;
 | ||
|     display: flex;
 | ||
|     align-items: center;
 | ||
|     justify-content: center;
 | ||
|     font-size: 26rpx;
 | ||
|     font-weight: bold;
 | ||
|     color: #fff;
 | ||
|     margin-bottom: 15rpx;
 | ||
|     position: relative;
 | ||
| 
 | ||
|     img {
 | ||
|       filter: drop-shadow(0 4rpx 8rpx rgba(0, 0, 0, 0.3));
 | ||
|       transition: all 0.3s ease;
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   .member-name {
 | ||
|     font-size: 24rpx;
 | ||
|     color: #fff;
 | ||
|     text-align: center;
 | ||
|     margin-bottom: 8rpx;
 | ||
|     font-weight: bold;
 | ||
|     max-width: 110rpx;
 | ||
|     overflow: hidden;
 | ||
|     text-overflow: ellipsis;
 | ||
|     white-space: nowrap;
 | ||
| 
 | ||
|     padding: 6rpx 10rpx;
 | ||
|     border-radius: 15rpx;
 | ||
|   }
 | ||
| 
 | ||
|   .member-code {
 | ||
|     color: #fff;
 | ||
|     text-align: center;
 | ||
|     margin-top: 8rpx;
 | ||
|     font-size: 24rpx;
 | ||
|     font-weight: 600;
 | ||
|     overflow: hidden;
 | ||
|     text-overflow: ellipsis;
 | ||
|     white-space: nowrap;
 | ||
| 
 | ||
|     padding: 4rpx 8rpx;
 | ||
|     border-radius: 12rpx;
 | ||
| 
 | ||
|     backdrop-filter: blur(5rpx);
 | ||
|   }
 | ||
| 
 | ||
|   .score {
 | ||
|     font-size: 22rpx;
 | ||
|     color: #fff;
 | ||
|     font-weight: bold;
 | ||
|     min-width: 60rpx;
 | ||
|     text-align: center;
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| // 第二名台阶
 | ||
| .second-place {
 | ||
|   .player-area {
 | ||
|     transform: translateY(10rpx);
 | ||
|   }
 | ||
|   &::after {
 | ||
|     width: 130rpx;
 | ||
|     height: 100rpx;
 | ||
|     background: linear-gradient(135deg, #c0c0c0 0%, #e8e8e8 50%, #c0c0c0 100%);
 | ||
|   }
 | ||
| 
 | ||
|   // .member-name {
 | ||
|   //   font-size: 26rpx;
 | ||
|   // }
 | ||
| 
 | ||
|   // .member-code {
 | ||
|   //   font-size: 26rpx;
 | ||
|   // }
 | ||
| 
 | ||
|   // .score {
 | ||
|   //   font-size: 26rpx;
 | ||
|   // }
 | ||
| 
 | ||
|   .rank-number img {
 | ||
|     height: 70rpx;
 | ||
|     width: 70rpx;
 | ||
|     animation: silver-glow 2s ease-in-out infinite alternate;
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| // 第一名台阶 - 最高
 | ||
| .first-place {
 | ||
|   .player-area {
 | ||
|     transform: translateY(-40rpx);
 | ||
|   }
 | ||
| 
 | ||
|   &::after {
 | ||
|     width: 150rpx;
 | ||
|     height: 140rpx;
 | ||
|     background: linear-gradient(135deg, #ffd700 0%, #ffed4e 50%, #ffd700 100%);
 | ||
|     box-shadow:
 | ||
|       0 -8rpx 20rpx rgba(0, 0, 0, 0.3),
 | ||
|       inset 0 3rpx 6rpx rgba(255, 255, 255, 0.3),
 | ||
|       0 0 25rpx rgba(255, 215, 0, 0.4);
 | ||
|   }
 | ||
| 
 | ||
|   .rank-number img {
 | ||
|     height: 80rpx;
 | ||
|     width: 80rpx;
 | ||
|     animation: champion-glow 2s ease-in-out infinite alternate;
 | ||
|   }
 | ||
| 
 | ||
|   .member-name {
 | ||
|     font-size: 28rpx;
 | ||
|     color: #ffd700;
 | ||
|     animation: champion-text-glow 2s ease-in-out infinite alternate;
 | ||
|   }
 | ||
| 
 | ||
|   .member-code {
 | ||
|     font-size: 28rpx;
 | ||
|     color: #ffd700;
 | ||
|     font-weight: 600;
 | ||
|   }
 | ||
| 
 | ||
|   .score {
 | ||
|     color: #ffd700;
 | ||
|     font-size: 28rpx;
 | ||
|     font-weight: 900;
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| // 第三名台阶
 | ||
| .third-place {
 | ||
|   .player-area {
 | ||
|     transform: translateY(10rpx);
 | ||
|   }
 | ||
| 
 | ||
|   .rank-number img {
 | ||
|     height: 65rpx;
 | ||
|     width: 65rpx;
 | ||
|     animation: bronze-glow 2s ease-in-out infinite alternate;
 | ||
|   }
 | ||
|   // .member-name {
 | ||
|   //   font-size: 22rpx;
 | ||
|   // }
 | ||
| 
 | ||
|   // .member-code {
 | ||
|   //   font-size: 22rpx;
 | ||
|   // }
 | ||
| 
 | ||
|   // .score {
 | ||
|   //   font-size: 22rpx;
 | ||
|   // }
 | ||
| }
 | ||
| 
 | ||
| @keyframes title-bling {
 | ||
|   0% {
 | ||
|     text-shadow:
 | ||
|       0 2rpx 8rpx rgba(0, 0, 0, 0.6),
 | ||
|       0 0 15rpx rgba(74, 144, 226, 0.8),
 | ||
|       0 0 25rpx rgba(255, 255, 255, 0.4);
 | ||
|   }
 | ||
|   100% {
 | ||
|     text-shadow:
 | ||
|       0 2rpx 8rpx rgba(0, 0, 0, 0.6),
 | ||
|       0 0 25rpx rgba(74, 144, 226, 1),
 | ||
|       0 0 35rpx rgba(255, 255, 255, 0.7),
 | ||
|       0 0 45rpx rgba(0, 119, 204, 0.5);
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| @keyframes golden-pulse {
 | ||
|   0% {
 | ||
|     background: radial-gradient(
 | ||
|       ellipse at center,
 | ||
|       rgba(255, 215, 0, 0.2) 0%,
 | ||
|       transparent 70%
 | ||
|     );
 | ||
|   }
 | ||
|   100% {
 | ||
|     background: radial-gradient(
 | ||
|       ellipse at center,
 | ||
|       rgba(255, 215, 0, 0.4) 0%,
 | ||
|       transparent 70%
 | ||
|     );
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| @keyframes champion-glow {
 | ||
|   0% {
 | ||
|     filter: drop-shadow(0 4rpx 8rpx rgba(0, 0, 0, 0.3))
 | ||
|       drop-shadow(0 0 10rpx rgba(255, 215, 0, 0.3));
 | ||
|     transform: scale(1);
 | ||
|   }
 | ||
|   100% {
 | ||
|     filter: drop-shadow(0 4rpx 8rpx rgba(0, 0, 0, 0.3))
 | ||
|       drop-shadow(0 0 20rpx rgba(255, 215, 0, 0.6));
 | ||
|     transform: scale(1.05);
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| @keyframes champion-text-glow {
 | ||
|   0% {
 | ||
|     text-shadow: 0 2rpx 8rpx rgba(255, 215, 0, 0.8);
 | ||
|   }
 | ||
|   100% {
 | ||
|     text-shadow:
 | ||
|       0 2rpx 8rpx rgba(255, 215, 0, 0.8),
 | ||
|       0 0 15rpx rgba(255, 215, 0, 0.5);
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| @keyframes silver-glow {
 | ||
|   0% {
 | ||
|     filter: drop-shadow(0 4rpx 8rpx rgba(0, 0, 0, 0.3))
 | ||
|       drop-shadow(0 0 8rpx rgba(192, 192, 192, 0.3));
 | ||
|   }
 | ||
|   100% {
 | ||
|     filter: drop-shadow(0 4rpx 8rpx rgba(0, 0, 0, 0.3))
 | ||
|       drop-shadow(0 0 15rpx rgba(192, 192, 192, 0.5));
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| @keyframes bronze-glow {
 | ||
|   0% {
 | ||
|     filter: drop-shadow(0 4rpx 8rpx rgba(0, 0, 0, 0.3))
 | ||
|       drop-shadow(0 0 8rpx rgba(205, 127, 50, 0.3));
 | ||
|   }
 | ||
|   100% {
 | ||
|     filter: drop-shadow(0 4rpx 8rpx rgba(0, 0, 0, 0.3))
 | ||
|       drop-shadow(0 0 15rpx rgba(205, 127, 50, 0.5));
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| .ranking-list {
 | ||
|   flex: 1;
 | ||
|   background: linear-gradient(
 | ||
|     135deg,
 | ||
|     rgba(255, 255, 255, 0.98) 0%,
 | ||
|     rgba(248, 250, 252, 0.95) 100%
 | ||
|   );
 | ||
|   margin: 20rpx 20rpx 0 20rpx;
 | ||
|   border-radius: 25rpx 25rpx 0 0;
 | ||
|   overflow: hidden;
 | ||
|   backdrop-filter: blur(15rpx);
 | ||
|   display: flex;
 | ||
|   flex-direction: column;
 | ||
|   position: relative;
 | ||
|   z-index: 2;
 | ||
|   box-shadow:
 | ||
|     0 -8rpx 25rpx rgba(0, 0, 0, 0.1),
 | ||
|     inset 0 1rpx 0 rgba(255, 255, 255, 0.8);
 | ||
| }
 | ||
| 
 | ||
| .list-header {
 | ||
|   padding: 30rpx 40rpx 20rpx;
 | ||
|   border-bottom: 2rpx solid rgba(139, 69, 19, 0.1);
 | ||
|   flex-shrink: 0;
 | ||
|   background: linear-gradient(
 | ||
|     135deg,
 | ||
|     rgba(255, 255, 255, 0.9) 0%,
 | ||
|     rgba(248, 250, 252, 0.8) 100%
 | ||
|   );
 | ||
| 
 | ||
|   .list-title {
 | ||
|     font-size: 28rpx;
 | ||
|     font-weight: bold;
 | ||
|     color: #2d3748;
 | ||
|     position: relative;
 | ||
|     text-align: center;
 | ||
| 
 | ||
|     &::after {
 | ||
|       content: '';
 | ||
|       position: absolute;
 | ||
|       bottom: -8rpx;
 | ||
|       left: 50%;
 | ||
|       transform: translateX(-50%);
 | ||
|       width: 60rpx;
 | ||
|       height: 3rpx;
 | ||
|       background: linear-gradient(90deg, transparent, #9c27b0, transparent);
 | ||
|       border-radius: 2rpx;
 | ||
|     }
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| .scroll-container {
 | ||
|   flex: 1;
 | ||
|   height: 0; /* 关键:在flex布局中,scroll-view需要明确高度 */
 | ||
| }
 | ||
| 
 | ||
| .list-item {
 | ||
|   display: flex;
 | ||
|   align-items: center;
 | ||
|   padding: 24rpx 30rpx;
 | ||
|   border-bottom: 1rpx solid rgba(226, 232, 240, 0.8);
 | ||
|   position: relative;
 | ||
|   transition: all 0.3s ease;
 | ||
| 
 | ||
|   &:last-child {
 | ||
|     border-bottom: none;
 | ||
|   }
 | ||
| 
 | ||
|   &:hover {
 | ||
|     background: linear-gradient(
 | ||
|       135deg,
 | ||
|       rgba(159, 122, 234, 0.05) 0%,
 | ||
|       rgba(139, 92, 246, 0.03) 100%
 | ||
|     );
 | ||
|     transform: translateX(5rpx);
 | ||
|   }
 | ||
| 
 | ||
|   .item-rank {
 | ||
|     font-size: 24rpx;
 | ||
|     font-weight: bold;
 | ||
|     color: #64748b;
 | ||
|     text-align: center;
 | ||
|     margin-right: 20rpx;
 | ||
|     background: linear-gradient(135deg, #f1f5f9 0%, #e2e8f0 100%);
 | ||
|     border-radius: 50%;
 | ||
|     width: 40rpx;
 | ||
|     height: 40rpx;
 | ||
|     display: flex;
 | ||
|     align-items: center;
 | ||
|     justify-content: center;
 | ||
|     box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.08);
 | ||
|   }
 | ||
| 
 | ||
|   .item-info {
 | ||
|     flex: 2;
 | ||
|     display: flex;
 | ||
|     align-items: center;
 | ||
|     justify-content: space-between;
 | ||
|   }
 | ||
| 
 | ||
|   .item-name {
 | ||
|     font-size: 28rpx;
 | ||
|     color: #1e293b;
 | ||
|     overflow: hidden;
 | ||
|     text-overflow: ellipsis;
 | ||
|     white-space: nowrap;
 | ||
|     font-weight: 600;
 | ||
| 
 | ||
|     &.highlight {
 | ||
|       color: #7c3aed;
 | ||
|       font-weight: bold;
 | ||
|       background: linear-gradient(135deg, #7c3aed, #a855f7);
 | ||
|       background-clip: text;
 | ||
|       -webkit-background-clip: text;
 | ||
|       -webkit-text-fill-color: transparent;
 | ||
|       position: relative;
 | ||
| 
 | ||
|       &::before {
 | ||
|         content: '';
 | ||
|         position: absolute;
 | ||
|         left: -8rpx;
 | ||
|         top: 50%;
 | ||
|         transform: translateY(-50%);
 | ||
|         width: 4rpx;
 | ||
|         height: 20rpx;
 | ||
|         background: linear-gradient(135deg, #7c3aed, #a855f7);
 | ||
|         border-radius: 2rpx;
 | ||
|       }
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   .item-code {
 | ||
|     font-size: 22rpx;
 | ||
|     color: #64748b;
 | ||
|     white-space: nowrap;
 | ||
|   }
 | ||
| 
 | ||
|   .item-score {
 | ||
|     flex: 1;
 | ||
|     font-size: 24rpx;
 | ||
|     color: #374151;
 | ||
|     font-weight: bold;
 | ||
|     min-width: 100rpx;
 | ||
|     text-align: center;
 | ||
|     border-radius: 20rpx;
 | ||
|     display: flex;
 | ||
|     justify-content: flex-end;
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| .list-footer {
 | ||
|   padding: 40rpx;
 | ||
|   text-align: center;
 | ||
| 
 | ||
|   .empty-text {
 | ||
|     font-size: 26rpx;
 | ||
|     color: #64748b;
 | ||
|     font-style: italic;
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| .popup-footer {
 | ||
|   padding: 20rpx;
 | ||
|   flex-shrink: 0;
 | ||
|   position: relative;
 | ||
|   z-index: 2;
 | ||
| 
 | ||
|   .close-text-btn {
 | ||
|     background: linear-gradient(
 | ||
|       135deg,
 | ||
|       rgba(255, 255, 255, 0.95) 0%,
 | ||
|       rgba(248, 250, 252, 0.9) 100%
 | ||
|     );
 | ||
|     color: #005bac;
 | ||
|     text-align: center;
 | ||
|     padding: 20rpx 50rpx;
 | ||
|     border-radius: 50rpx;
 | ||
|     font-size: 30rpx;
 | ||
|     font-weight: bold;
 | ||
|     box-shadow:
 | ||
|       0 8rpx 25rpx rgba(0, 0, 0, 0.15),
 | ||
|       inset 0 1rpx 0 rgba(255, 255, 255, 0.8),
 | ||
|       0 0 0 1rpx rgba(0, 91, 172, 0.3);
 | ||
|     transition: all 0.3s ease;
 | ||
|     position: relative;
 | ||
|     overflow: hidden;
 | ||
| 
 | ||
|     &::before {
 | ||
|       content: '';
 | ||
|       position: absolute;
 | ||
|       top: 0;
 | ||
|       left: -100%;
 | ||
|       width: 100%;
 | ||
|       height: 100%;
 | ||
|       background: linear-gradient(
 | ||
|         90deg,
 | ||
|         transparent,
 | ||
|         rgba(255, 255, 255, 0.4),
 | ||
|         transparent
 | ||
|       );
 | ||
|       transition: left 0.5s ease;
 | ||
|     }
 | ||
| 
 | ||
|     &:active {
 | ||
|       transform: scale(0.98);
 | ||
|       background: linear-gradient(
 | ||
|         135deg,
 | ||
|         rgba(248, 250, 252, 0.9) 0%,
 | ||
|         rgba(241, 245, 249, 0.85) 100%
 | ||
|       );
 | ||
|       box-shadow:
 | ||
|         0 4rpx 15rpx rgba(0, 0, 0, 0.1),
 | ||
|         inset 0 2rpx 4rpx rgba(0, 0, 0, 0.1);
 | ||
| 
 | ||
|       &::before {
 | ||
|         left: 100%;
 | ||
|       }
 | ||
|     }
 | ||
| 
 | ||
|     &:hover::before {
 | ||
|       left: 100%;
 | ||
|     }
 | ||
|   }
 | ||
| }
 | ||
| </style>
 |