|
|
@ -13,8 +13,20 @@ |
|
|
|
<text class="subtitle">转一转,好运来!</text> |
|
|
|
</view> |
|
|
|
|
|
|
|
<!-- 积分余额显示 --> |
|
|
|
<view class="points-display"> |
|
|
|
<view class="points-info"> |
|
|
|
<text class="points-label">当前积分:</text> |
|
|
|
<text class="points-value">{{ userPoints }}</text> |
|
|
|
</view> |
|
|
|
<view class="cost-info"> |
|
|
|
<text class="cost-label">每次消耗:</text> |
|
|
|
<text class="cost-value">{{ drawCost }}积分</text> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
|
|
|
|
<!-- 转盘区域 --> |
|
|
|
<view class="turntable-wrapper"> |
|
|
|
<view class="turntable-wrapper" v-if="prizes.length > 0"> |
|
|
|
<view class="turntable" :class="{ 'spinning': isSpinning }" :style="{ transform: `rotate(${rotateAngle}deg)` }"> |
|
|
|
<!-- 使用纯CSS创建转盘 --> |
|
|
|
<view class="wheel-bg"> |
|
|
@ -24,8 +36,8 @@ |
|
|
|
:key="index" |
|
|
|
class="wheel-sector" |
|
|
|
:style="{ |
|
|
|
backgroundColor: sectorColors[index], |
|
|
|
transform: `rotate(${index * 45}deg)` |
|
|
|
backgroundColor: sectorColors[index % sectorColors.length], |
|
|
|
transform: `rotate(${index * sectorAngle}deg)` |
|
|
|
}" |
|
|
|
> |
|
|
|
</view> |
|
|
@ -40,9 +52,9 @@ |
|
|
|
:style="prizeStyles[index]" |
|
|
|
> |
|
|
|
<view class="prize-content"> |
|
|
|
<text class="prize-icon">{{ prize.icon }}</text> |
|
|
|
<text class="prize-name">{{ prize.name }}</text> |
|
|
|
<text class="prize-value">{{ prize.value }}</text> |
|
|
|
<text class="prize-icon">{{ getIcon(prize.type) }}</text> |
|
|
|
<text class="prize-name">{{ prize.title }}</text> |
|
|
|
<text class="prize-value" v-if="prize.price">¥{{ prize.price }}</text> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
</view> |
|
|
@ -54,19 +66,31 @@ |
|
|
|
</view> |
|
|
|
|
|
|
|
<!-- 中心按钮 --> |
|
|
|
<view class="center-button" @click="startSpin" :class="{ disabled: isSpinning }"> |
|
|
|
<text class="button-text">{{ isSpinning ? '抽奖中...' : '开始抽奖' }}</text> |
|
|
|
<view class="center-button" @click="startSpin" :class="{ disabled: isSpinning || userPoints < drawCost }"> |
|
|
|
<text class="button-text">{{ getButtonText() }}</text> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
|
|
|
|
<!-- 加载中状态 --> |
|
|
|
<view v-else class="loading-wrapper"> |
|
|
|
<uv-loading-icon mode="spinner" color="#fff" size="60"></uv-loading-icon> |
|
|
|
<text class="loading-text">加载中...</text> |
|
|
|
</view> |
|
|
|
|
|
|
|
<!-- 抽奖结果弹窗 --> |
|
|
|
<view class="result-modal" v-if="showResult" @click="closeResult"> |
|
|
|
<view class="modal-content" @click.stop> |
|
|
|
<text class="result-title">🎉 恭喜您 🎉</text> |
|
|
|
<view class="result-prize"> |
|
|
|
<text class="result-icon">{{ currentPrize.icon }}</text> |
|
|
|
<text class="result-name">{{ currentPrize.name }}</text> |
|
|
|
<text class="result-value">{{ currentPrize.value }}</text> |
|
|
|
<!-- <text class="result-icon">{{ getIcon(currentPrize.type) }}</text> --> |
|
|
|
|
|
|
|
<image :src="currentPrize.img" mode="widthFix" style="width: 100%;"></image> |
|
|
|
|
|
|
|
<text class="result-name">{{ currentPrize.title }}</text> |
|
|
|
<text class="result-value" v-if="currentPrize.price">¥{{ currentPrize.price }}</text> |
|
|
|
</view> |
|
|
|
<view class="points-change"> |
|
|
|
<text class="change-text">{{ getPointsChangeText() }}</text> |
|
|
|
</view> |
|
|
|
<view class="result-actions"> |
|
|
|
<button class="confirm-btn" @click="closeResult">确定</button> |
|
|
@ -74,29 +98,28 @@ |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
|
|
|
|
<!-- 抽奖次数提示 --> |
|
|
|
<!-- 积分提示 --> |
|
|
|
<view class="spin-info"> |
|
|
|
<text>今日剩余抽奖次数: {{ remainingSpins }}</text> |
|
|
|
<text>消耗{{ drawCost }}积分即可抽奖,快来试试手气吧!</text> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
</template> |
|
|
|
|
|
|
|
<script> |
|
|
|
import { mapState } from 'vuex' |
|
|
|
export default { |
|
|
|
computed: { |
|
|
|
...mapState(['userInfo']), |
|
|
|
// 获取用户当前积分 |
|
|
|
userPoints() { |
|
|
|
return this.userInfo?.integerPrice || 0 |
|
|
|
} |
|
|
|
}, |
|
|
|
data() { |
|
|
|
return { |
|
|
|
// 奖品配置 |
|
|
|
prizes: [ |
|
|
|
{ name: '现金奖励', value: '¥10', icon: '💰', type: 'money', amount: 10 }, |
|
|
|
{ name: '精美礼品', value: '小熊', icon: '🧸', type: 'gift', item: 'teddy_bear' }, |
|
|
|
{ name: '现金奖励', value: '¥5', icon: '💰', type: 'money', amount: 5 }, |
|
|
|
{ name: '精美礼品', value: '花束', icon: '💐', type: 'gift', item: 'flowers' }, |
|
|
|
{ name: '现金奖励', value: '¥20', icon: '💰', type: 'money', amount: 20 }, |
|
|
|
{ name: '精美礼品', value: '巧克力', icon: '🍫', type: 'gift', item: 'chocolate' }, |
|
|
|
{ name: '现金奖励', value: '¥2', icon: '💰', type: 'money', amount: 2 }, |
|
|
|
{ name: '精美礼品', value: '香水', icon: '🌸', type: 'gift', item: 'perfume' } |
|
|
|
], |
|
|
|
// 奖品配置(从接口获取) |
|
|
|
prizes: [], |
|
|
|
sectorColors: [ |
|
|
|
'#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', |
|
|
|
'#FFEAA7', '#DDA0DD', '#98D8C8', '#F7DC6F' |
|
|
@ -105,21 +128,71 @@ |
|
|
|
rotateAngle: 0, // 旋转角度 |
|
|
|
showResult: false, // 显示结果弹窗 |
|
|
|
currentPrize: null, // 当前中奖奖品 |
|
|
|
remainingSpins: 3, // 剩余抽奖次数 |
|
|
|
sectorAngle: 45, // 每个扇形的角度 (360/8) |
|
|
|
sectorAngle: 0, // 每个扇形的角度 |
|
|
|
drawCost: 5, // 抽奖消耗积分 |
|
|
|
pointsChange: null, // 积分变化 |
|
|
|
// 预计算奖品位置样式 |
|
|
|
prizeStyles: [] |
|
|
|
} |
|
|
|
}, |
|
|
|
onLoad() { |
|
|
|
this.calculatePrizeStyles() |
|
|
|
this.getLuckDrawList() |
|
|
|
}, |
|
|
|
onShow() { |
|
|
|
// 刷新用户信息以获取最新积分 |
|
|
|
if (uni.getStorageSync('token')) { |
|
|
|
this.$store.commit('getUserInfo') |
|
|
|
} |
|
|
|
}, |
|
|
|
methods: { |
|
|
|
// 获取抽奖列表 |
|
|
|
getLuckDrawList() { |
|
|
|
this.$api('getLuckDrawList', {}, res => { |
|
|
|
if (res.code == 200) { |
|
|
|
this.prizes = res.result || [] |
|
|
|
|
|
|
|
// 计算每个扇形的角度 |
|
|
|
if (this.prizes.length > 0) { |
|
|
|
this.sectorAngle = 360 / this.prizes.length |
|
|
|
this.calculatePrizeStyles() |
|
|
|
} |
|
|
|
} else { |
|
|
|
uni.showToast({ |
|
|
|
title: res.message || '获取抽奖信息失败', |
|
|
|
icon: 'none' |
|
|
|
}) |
|
|
|
} |
|
|
|
}) |
|
|
|
}, |
|
|
|
|
|
|
|
// 获取按钮文字 |
|
|
|
getButtonText() { |
|
|
|
if (this.isSpinning) { |
|
|
|
return '抽奖中...' |
|
|
|
} |
|
|
|
if (this.userPoints < this.drawCost) { |
|
|
|
return '积分不足' |
|
|
|
} |
|
|
|
return '开始抽奖' |
|
|
|
}, |
|
|
|
|
|
|
|
// 根据奖品类型获取图标 |
|
|
|
getIcon(type) { |
|
|
|
const iconMap = { |
|
|
|
'0': '💰', |
|
|
|
'points': '⭐', |
|
|
|
'gift': '🎁', |
|
|
|
'coupon': '🎫', |
|
|
|
'thanks': '🤝' |
|
|
|
} |
|
|
|
return iconMap[type] || '🎁' |
|
|
|
}, |
|
|
|
|
|
|
|
// 计算奖品位置样式 |
|
|
|
calculatePrizeStyles() { |
|
|
|
this.prizeStyles = this.prizes.map((prize, index) => { |
|
|
|
// 计算奖品在圆形中的位置 |
|
|
|
const angle = (index * 45 + 22.5) * Math.PI / 180; // 转换为弧度,22.5是偏移到扇形中心 |
|
|
|
const angle = (index * this.sectorAngle + this.sectorAngle / 2) * Math.PI / 180; // 转换为弧度 |
|
|
|
const radius = 150; // 奖品距离中心的距离 |
|
|
|
const x = Math.cos(angle - Math.PI/2) * radius; // 减去90度,因为我们希望0度在顶部 |
|
|
|
const y = Math.sin(angle - Math.PI/2) * radius; |
|
|
@ -127,59 +200,118 @@ |
|
|
|
return `left: calc(50% + ${x}rpx); top: calc(50% + ${y}rpx); transform: translate(-50%, -50%);` |
|
|
|
}) |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
// 开始抽奖 |
|
|
|
startSpin() { |
|
|
|
if (this.isSpinning || this.remainingSpins <= 0) { |
|
|
|
if (this.remainingSpins <= 0) { |
|
|
|
uni.showToast({ |
|
|
|
title: '今日抽奖次数已用完', |
|
|
|
icon: 'none' |
|
|
|
}) |
|
|
|
} |
|
|
|
// 检查是否可以抽奖 |
|
|
|
if (this.isSpinning) { |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
// 检查积分是否足够 |
|
|
|
if (this.userPoints < this.drawCost) { |
|
|
|
uni.showModal({ |
|
|
|
title: '积分不足', |
|
|
|
content: `抽奖需要消耗${this.drawCost}积分,您当前积分为${this.userPoints}`, |
|
|
|
showCancel: true, |
|
|
|
cancelText: '取消', |
|
|
|
confirmText: '去赚积分', |
|
|
|
success: (res) => { |
|
|
|
if (res.confirm) { |
|
|
|
// 跳转到积分获取页面或任务页面 |
|
|
|
uni.navigateBack() |
|
|
|
} |
|
|
|
} |
|
|
|
}) |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
this.isSpinning = true |
|
|
|
this.remainingSpins-- |
|
|
|
|
|
|
|
// 随机选择奖品 |
|
|
|
const prizeIndex = Math.floor(Math.random() * this.prizes.length) |
|
|
|
this.currentPrize = this.prizes[prizeIndex] |
|
|
|
|
|
|
|
// 计算旋转角度 |
|
|
|
// 每个扇形45度,指针指向扇形中心需要额外旋转22.5度 |
|
|
|
const targetAngle = 360 - (prizeIndex * this.sectorAngle + this.sectorAngle / 2) |
|
|
|
const spinRounds = 5 // 转5圈 |
|
|
|
const finalAngle = this.rotateAngle + spinRounds * 360 + targetAngle |
|
|
|
|
|
|
|
this.rotateAngle = finalAngle |
|
|
|
|
|
|
|
// 动画结束后显示结果 |
|
|
|
setTimeout(() => { |
|
|
|
this.isSpinning = false |
|
|
|
this.showResult = true |
|
|
|
this.handlePrizeResult() |
|
|
|
}, 3000) |
|
|
|
|
|
|
|
// 调用抽奖接口 |
|
|
|
this.$api('luckDraw', {}, res => { |
|
|
|
if (res.code == 200) { |
|
|
|
const result = res.result |
|
|
|
this.currentPrize = result.gift |
|
|
|
this.pointsChange = result.remainingIntegral || null |
|
|
|
|
|
|
|
// 更新用户信息(积分变化) |
|
|
|
this.$store.commit('getUserInfo') |
|
|
|
|
|
|
|
// 找到中奖奖品的索引 |
|
|
|
const prizeIndex = this.prizes.findIndex(prize => prize.id == result.gift.id) |
|
|
|
|
|
|
|
if (prizeIndex !== -1) { |
|
|
|
// 计算旋转角度 |
|
|
|
const targetAngle = 360 - (prizeIndex * this.sectorAngle + this.sectorAngle / 2) |
|
|
|
const spinRounds = 5 // 转5圈 |
|
|
|
const finalAngle = this.rotateAngle + spinRounds * 360 + targetAngle |
|
|
|
|
|
|
|
this.rotateAngle = finalAngle |
|
|
|
|
|
|
|
// 动画结束后显示结果 |
|
|
|
setTimeout(() => { |
|
|
|
this.isSpinning = false |
|
|
|
this.showResult = true |
|
|
|
this.handlePrizeResult() |
|
|
|
}, 3000) |
|
|
|
} else { |
|
|
|
this.isSpinning = false |
|
|
|
uni.showToast({ |
|
|
|
title: '抽奖异常,请重试', |
|
|
|
icon: 'none' |
|
|
|
}) |
|
|
|
} |
|
|
|
} else { |
|
|
|
this.isSpinning = false |
|
|
|
uni.showToast({ |
|
|
|
title: res.message || '抽奖失败,请重试', |
|
|
|
icon: 'none' |
|
|
|
}) |
|
|
|
} |
|
|
|
}) |
|
|
|
}, |
|
|
|
|
|
|
|
// 处理中奖结果 |
|
|
|
handlePrizeResult() { |
|
|
|
if (this.currentPrize.type === 'money') { |
|
|
|
// 处理现金奖励 |
|
|
|
console.log(`获得现金奖励: ${this.currentPrize.amount}元`) |
|
|
|
// 这里可以调用API增加用户余额 |
|
|
|
} else if (this.currentPrize.type === 'gift') { |
|
|
|
// 处理礼品奖励 |
|
|
|
console.log(`获得礼品: ${this.currentPrize.item}`) |
|
|
|
// 这里可以调用API添加礼品到用户背包 |
|
|
|
if (this.currentPrize) { |
|
|
|
console.log(`中奖信息:`, this.currentPrize) |
|
|
|
// 根据奖品类型显示不同提示 |
|
|
|
if (this.currentPrize.type === 'money') { |
|
|
|
console.log(`获得现金奖励: ${this.currentPrize.prizeValue}`) |
|
|
|
} else if (this.currentPrize.type === 'points') { |
|
|
|
console.log(`获得积分奖励: ${this.currentPrize.prizeValue}`) |
|
|
|
} else if (this.currentPrize.type === 'gift') { |
|
|
|
console.log(`获得礼品: ${this.currentPrize.prizeName}`) |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
// 关闭结果弹窗 |
|
|
|
closeResult() { |
|
|
|
this.showResult = false |
|
|
|
this.pointsChange = null |
|
|
|
// 刷新抽奖信息 |
|
|
|
this.getLuckDrawList() |
|
|
|
}, |
|
|
|
|
|
|
|
// 积分变化提示文字 |
|
|
|
getPointsChangeText() { |
|
|
|
let text = `消耗 ${this.drawCost} 积分` |
|
|
|
|
|
|
|
if (this.currentPrize && this.currentPrize.type === 'points') { |
|
|
|
const gained = parseInt(this.currentPrize.prizeValue) |
|
|
|
const net = gained - this.drawCost |
|
|
|
if (net > 0) { |
|
|
|
text += `,获得 ${gained} 积分,净收益 +${net} 积分` |
|
|
|
} else if (net < 0) { |
|
|
|
text += `,获得 ${gained} 积分,净损失 ${Math.abs(net)} 积分` |
|
|
|
} else { |
|
|
|
text += `,获得 ${gained} 积分,收支平衡` |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return text |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -197,7 +329,7 @@ |
|
|
|
|
|
|
|
.header { |
|
|
|
text-align: center; |
|
|
|
margin-bottom: 60rpx; |
|
|
|
margin-bottom: 40rpx; |
|
|
|
color: white; |
|
|
|
|
|
|
|
.title { |
|
|
@ -213,6 +345,42 @@ |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.points-display { |
|
|
|
background: rgba(255, 255, 255, 0.2); |
|
|
|
border-radius: 20rpx; |
|
|
|
padding: 30rpx; |
|
|
|
margin-bottom: 40rpx; |
|
|
|
backdrop-filter: blur(10rpx); |
|
|
|
display: flex; |
|
|
|
justify-content: space-between; |
|
|
|
align-items: center; |
|
|
|
width: 100%; |
|
|
|
max-width: 600rpx; |
|
|
|
|
|
|
|
.points-info, .cost-info { |
|
|
|
display: flex; |
|
|
|
align-items: center; |
|
|
|
color: white; |
|
|
|
|
|
|
|
.points-label, .cost-label { |
|
|
|
font-size: 28rpx; |
|
|
|
margin-right: 10rpx; |
|
|
|
} |
|
|
|
|
|
|
|
.points-value { |
|
|
|
font-size: 32rpx; |
|
|
|
font-weight: bold; |
|
|
|
color: #FFD700; |
|
|
|
} |
|
|
|
|
|
|
|
.cost-value { |
|
|
|
font-size: 28rpx; |
|
|
|
font-weight: bold; |
|
|
|
color: #FF6B6B; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.turntable-wrapper { |
|
|
|
position: relative; |
|
|
|
width: 600rpx; |
|
|
@ -220,6 +388,20 @@ |
|
|
|
margin-bottom: 60rpx; |
|
|
|
} |
|
|
|
|
|
|
|
.loading-wrapper { |
|
|
|
display: flex; |
|
|
|
flex-direction: column; |
|
|
|
align-items: center; |
|
|
|
justify-content: center; |
|
|
|
height: 600rpx; |
|
|
|
|
|
|
|
.loading-text { |
|
|
|
color: white; |
|
|
|
font-size: 28rpx; |
|
|
|
margin-top: 20rpx; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.turntable { |
|
|
|
width: 100%; |
|
|
|
height: 100%; |
|
|
@ -363,6 +545,7 @@ |
|
|
|
&.disabled { |
|
|
|
opacity: 0.6; |
|
|
|
cursor: not-allowed; |
|
|
|
background: linear-gradient(145deg, #999, #777); |
|
|
|
} |
|
|
|
|
|
|
|
.button-text { |
|
|
@ -401,7 +584,7 @@ |
|
|
|
} |
|
|
|
|
|
|
|
.result-prize { |
|
|
|
margin-bottom: 40rpx; |
|
|
|
margin-bottom: 30rpx; |
|
|
|
|
|
|
|
.result-icon { |
|
|
|
font-size: 60rpx; |
|
|
@ -423,6 +606,19 @@ |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.points-change { |
|
|
|
margin-bottom: 40rpx; |
|
|
|
padding: 20rpx; |
|
|
|
background: #f5f5f5; |
|
|
|
border-radius: 10rpx; |
|
|
|
|
|
|
|
.change-text { |
|
|
|
font-size: 24rpx; |
|
|
|
color: #666; |
|
|
|
line-height: 1.4; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.confirm-btn { |
|
|
|
background: linear-gradient(45deg, #FF6B6B, #FF4757); |
|
|
|
color: white; |
|
|
|