| | |
| | | |
| | | <view class="group-progress"> |
| | | <view class="progress-header"> |
| | | <text class="progress-title">团购进度</text> |
| | | <text class="progress-percent">{{ Math.round((detail.actual_people / detail.groupbuy_num) * 100) }}%</text> |
| | | <view class="progress-left"> |
| | | <text class="progress-title">团购进度</text> |
| | | <text class="progress-tips">邀请好友加速成团</text> |
| | | </view> |
| | | <view class="progress-right"> |
| | | <text class="progress-percent">{{ Math.round((detail.actual_people / detail.groupbuy_num) * 100) }}%</text> |
| | | </view> |
| | | </view> |
| | | <view class="progress-bar"> |
| | | <view class="progress-fill" :style="{ width: (detail.actual_people / detail.groupbuy_num) * 100 + '%' }"></view> |
| | | <view class="progress-shine"></view> |
| | | </view> |
| | | <view class="progress-desc"> |
| | | <text>{{ detail.actual_people }}/{{ detail.groupbuy_num }}人</text> |
| | | <text>还差{{ detail.groupbuy_num - detail.actual_people }}人成团</text> |
| | | <view class="people-count"> |
| | | <text class="current">{{ detail.actual_people }}</text> |
| | | <text class="separator">/</text> |
| | | <text class="target">{{ detail.groupbuy_num }}人</text> |
| | | </view> |
| | | <view class="countdown"> |
| | | <text class="countdown-label">还剩</text> |
| | | <text class="countdown-time">{{ countdownText }}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | |
| | | detail: {}, |
| | | participants: [], |
| | | selectedSkuIndex: 0, |
| | | showInvite: false |
| | | showInvite: false, |
| | | countdownTimer: null, |
| | | countdownTime: 0 |
| | | }; |
| | | }, |
| | | computed: { |
| | | countdownText() { |
| | | if (this.countdownTime <= 0) return '00:00:00'; |
| | | const hours = Math.floor(this.countdownTime / 3600); |
| | | const minutes = Math.floor((this.countdownTime % 3600) / 60); |
| | | const seconds = this.countdownTime % 60; |
| | | return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`; |
| | | } |
| | | }, |
| | | onLoad(options) { |
| | | this.productId = options.id; |
| | | this.loadData(); |
| | | }, |
| | | onUnload() { |
| | | if (this.countdownTimer) { |
| | | clearInterval(this.countdownTimer); |
| | | } |
| | | }, |
| | | methods: { |
| | | loadData() { |
| | |
| | | groupbuy_product_id: this.productId |
| | | }, (res) => { |
| | | this.detail = res.data.detail; |
| | | // 计算团购倒计时 |
| | | this.calculateCountdown(); |
| | | }); |
| | | |
| | | // 模拟加载参团用户 |
| | | |
| | | // 加载参团用户 |
| | | this.loadParticipants(); |
| | | }, |
| | | calculateCountdown() { |
| | | if (this.detail.active && this.detail.active.end_time) { |
| | | const endTime = new Date(this.detail.active.end_time).getTime() / 1000; |
| | | const now = Math.floor(Date.now() / 1000); |
| | | const diff = endTime - now; |
| | | this.countdownTime = diff > 0 ? diff : 0; |
| | | this.startCountdown(); |
| | | } |
| | | }, |
| | | startCountdown() { |
| | | if (this.countdownTimer) { |
| | | clearInterval(this.countdownTimer); |
| | | } |
| | | this.countdownTimer = setInterval(() => { |
| | | if (this.countdownTime > 0) { |
| | | this.countdownTime--; |
| | | } else { |
| | | clearInterval(this.countdownTimer); |
| | | } |
| | | }, 1000); |
| | | }, |
| | | loadParticipants() { |
| | | // 模拟参团用户数据 |
| | |
| | | background: #fff; |
| | | padding: 30rpx; |
| | | margin-bottom: 20rpx; |
| | | |
| | | border-radius: 16rpx; |
| | | |
| | | .progress-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 20rpx; |
| | | |
| | | .progress-title { |
| | | font-size: 28rpx; |
| | | color: #333; |
| | | font-weight: bold; |
| | | |
| | | .progress-left { |
| | | .progress-title { |
| | | font-size: 32rpx; |
| | | color: #333; |
| | | font-weight: bold; |
| | | display: block; |
| | | margin-bottom: 8rpx; |
| | | } |
| | | |
| | | .progress-tips { |
| | | font-size: 24rpx; |
| | | color: #999; |
| | | } |
| | | } |
| | | |
| | | .progress-percent { |
| | | font-size: 24rpx; |
| | | color: #e74748; |
| | | |
| | | .progress-right { |
| | | .progress-percent { |
| | | font-size: 48rpx; |
| | | color: #e74748; |
| | | font-weight: bold; |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | .progress-bar { |
| | | height: 20rpx; |
| | | height: 24rpx; |
| | | background: #f0f0f0; |
| | | border-radius: 10rpx; |
| | | border-radius: 12rpx; |
| | | overflow: hidden; |
| | | margin-bottom: 20rpx; |
| | | |
| | | position: relative; |
| | | |
| | | .progress-fill { |
| | | height: 100%; |
| | | background: linear-gradient(to right, #e74748, #f17a5f); |
| | | border-radius: 10rpx; |
| | | transition: width 0.3s ease; |
| | | border-radius: 12rpx; |
| | | transition: width 0.5s ease; |
| | | position: relative; |
| | | z-index: 1; |
| | | } |
| | | |
| | | .progress-shine { |
| | | position: absolute; |
| | | top: 0; |
| | | left: 0; |
| | | width: 100%; |
| | | height: 100%; |
| | | background: linear-gradient(90deg, |
| | | transparent 0%, |
| | | rgba(255, 255, 255, 0.3) 50%, |
| | | transparent 100%); |
| | | animation: shine 2s infinite; |
| | | z-index: 2; |
| | | } |
| | | } |
| | | |
| | | |
| | | .progress-desc { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | font-size: 24rpx; |
| | | color: #999; |
| | | |
| | | .people-count { |
| | | color: #333; |
| | | |
| | | .current { |
| | | font-size: 36rpx; |
| | | color: #e74748; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .separator { |
| | | margin: 0 5rpx; |
| | | color: #999; |
| | | } |
| | | |
| | | .target { |
| | | color: #999; |
| | | } |
| | | } |
| | | |
| | | .countdown { |
| | | color: #666; |
| | | background: #fef5f5; |
| | | padding: 8rpx 16rpx; |
| | | border-radius: 20rpx; |
| | | |
| | | .countdown-label { |
| | | font-size: 24rpx; |
| | | } |
| | | |
| | | .countdown-time { |
| | | font-size: 28rpx; |
| | | color: #e74748; |
| | | font-weight: bold; |
| | | margin-left: 5rpx; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | @keyframes shine { |
| | | 0% { |
| | | transform: translateX(-100%); |
| | | } |
| | | 100% { |
| | | transform: translateX(100%); |
| | | } |
| | | } |
| | | |