<template> <view class="luckyDraw"> <image src="../../static/backImg.png" class="back" mode="widthFix"></image> <view class="luckyDrawContent"> <!-- 个人信息模块 --> <view class="luckyDrawTop"> <view class="left"> <view class="avatarView"> <open-data type="userAvatarUrl" class="avatar"></open-data> </view> <view class="userInfo"> <view class="account">{{mpName}}</view> <view class="ID">ID:{{userId}}</view> </view> </view> <view class="right" @click="jumpSign"> <text>签到有礼</text> </view> </view> <!-- 滚动通知 --> <view class="notice"> <image src="../../static/horn.png" class="horn" mode="widthFix"></image> <view class="txts"> <u-notice-bar mode="vertical" :volume-icon="false" color="#FFFFFF" type="none" :list="list"></u-notice-bar> </view> </view> <!-- 大转盘 --> <view class="turntable"> <view class="decorate"> <image src="../../static/decorate.png" class="decorateBack" mode="widthFix"></image> <image src="../../static/chassis.png" class="decorateChassis" mode="widthFix"></image> </view> <view class="turntableContent"> <almost-lottery :canvasId="canvasConfig.canvasId" :canvasWidth="canvasConfig.width" :canvasHeight="canvasConfig.height" :outerWidth="canvasConfig.outerWidth" :outerHeight="canvasConfig.outerHeight" :colors="canvasConfig.colors" :ringCount="8" :duration="1" :prizeList="prizeList" :prizeIndex="prizeIndex" @reset-index="prizeIndex = -1" @draw-start="handleDrawStart" @draw-end="handleDrawEnd" @finish="handleDrawFinish" v-if="prizeList.length" /> <view class="bottom"> <view class="cont">今日剩余抽奖次数:<text>{{freeNum}}</text></view> <navigator :url="'../checkInRecord/checkInRecord' + getSerialize({...mpData, openId})" class="record" hover-class="none"> <text>中奖记录</text> </navigator> </view> </view> </view> <!-- 我的奖品 --> <view class="myPrize"> <view class="title">我的奖品</view> <view class="chips"> <view class="chip" v-for="(item, index) in prizeCountDtoList" :key="index"> <image src="../../static/iPhone12Chip.png" mode="widthFix" v-if="item.prizeType === 3"></image> <image src="../../static/vipChip.png" mode="widthFix" v-else></image> <view class="text">{{item.prizeName}}</view> <view class="progress"> <view class="value" :style="{width: item.prizeCount && item.prizeCount > 0 ? (item.prizeCount/item.maxCount*100) + '%' : '0'}"></view> <text>{{item.prizeCount}}/{{item.maxCount}}</text> </view> </view> </view> <view class="clearTime" v-if="clearTime">碎片有效期至:{{clearTime | getTimerFilter}} </view> </view> <!-- 活动规则模块 --> <view class="activityRules"> <view class="top" @click="activityRulesHandle"> <text>活动规则</text> <image :src="!activityRules ? '../../static/unfold.png' : '../../static/retract.png'" mode="widthFix"></image> </view> <view class="bottom" v-if="activityRules"> <view>1、每个用户账户每天最多有10次抽奖机会,每天0点刷新。</view> <view>2、每天前2次可直接领取抽奖奖品,其他8次抽奖机会需要通过观看激励视频来获得。</view> <view>3、用户必须在对应公众号内登陆抽奖,否则书币将无法顺利领取。</view> <view>4、书币及7天书城VIP会员抽中后自动发放至书城账号,可在中奖记录中查看,或进入书城的“个人中心-充值记录-赠送”查看。</view> <view>5、用户可以通过完成本产品抽奖获得碎片,碎片种类由系统随机发放,兑换手机需集满50个碎片, 兑换书城VIP需集满30个碎片。碎片累计有效期为7天,在碎片超出有效期后或用户成功兑换奖品后,此前已获得的该奖品碎片都将清零。</view> <view>6、实物奖品请联系公众号内客服领取,实物奖品颜色随机寄送,以中奖用户收到的实物为准。</view> <view>7、若发现用户恶意违规行为,开发者有权取消其获奖资格。</view> <view>8、本活动解释权由开发者所有,有任何疑问请联系公众号内客服。</view> </view> </view> <!-- 弹窗 --> <view class="popup" v-if="popupShow"> <view class="popupContent"> <view class="content"> <image src="../../static/popupBack.png"></image> <view class="textCon"> <text class="ts">中奖了!</text> </br> <text class="reward">恭喜您获得{{luckyDay.name}}</text> </br> <image src="../../static/doubleBt.png" class="doubleBt" mode="widthFix" @click="lookVideo"></image> </br> <text class="bt" @click="receiveGold(false)">不了,单倍领取</text> </view> </view> <image src="../../static/close.png" class="close" mode="widthFix" @click="closeHandle"></image> </view> </view> </view> </view> </template> <script> let timer = null import AlmostLottery from '@/uni_modules/almost-lottery/components/almost-lottery/almost-lottery.vue' import { config, getHome, getOpenId, getBanner, setDrawGain, getPond, setReceive } from '@/api/api.js' import { clearCacheFile } from '@/uni_modules/almost-lottery/utils/almost-utils.js' export default { components: { AlmostLottery }, data() { return { title: 'Hello', list: [ '恭喜用户 “9016” 成功提现 2000书币', '恭喜用户 “9017” 成功提现 10书币', '恭喜用户 “9018” 成功提现 20书币', '恭喜用户 “9019” 成功提现 100书币' ], // canvas id、宽、高 canvasConfig: { canvasId: 'almostLotteryCanvas', width: 300, height: 300, outerWidth: 325, outerHeight: 325, colors: [ '#FEE3C6', '#FFFFFF' ] }, // 以下是奖品配置数据 // 奖品数据 prizeList: [], // 奖品是否设有库存 onStock: false, // 中奖下标 prizeIndex: -1, // 是否正在抽奖中,避免重复触发 prizeing: false, // 以下为中奖概率有关数据 // 是否由前端控制概率,默认不开启,强烈建议由后端控制 onFrontend: false, // 权重随机数的最大值 weightTotal: 0, // 权重数组 weightArr: [], // 以下为业务需求有关示例数据 // 金币余额 goldCoin: 50, // 当日免费抽奖次数余额 freeNum: 10, // 每次消耗的金币数 goldNum: 20, // 每天免费抽奖次数 freeNumDay: 2, // 以下是 // 活动规则开关控制 activityRules: false, // 弹窗控制 popupShow: false, openId: '', mpData: {}, prizeCountDtoList: [], // 碎片列表 mpName: null, // 渠道号 userId: null, // luckyDay: {}, // 中奖信息 doubleKey: "", // 领取key clearTime: "", // 碎片有效期 } }, computed: { isApple() { return uni.getSystemInfoSync().platform === 'ios' } }, onLoad(options) { // 请求奖品数据 this.mpData = options this.getData() }, onUnload() { uni.hideLoading() }, onShareAppMessage() {}, filters: { getTimerFilter(value) { // 获取年月日时分秒 if(!value) return "" let timestamp = Date.parse(new Date(value)) let date = new Date(timestamp) let Y = date.getFullYear() let M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) let D = date.getDate() < 10 ? '0' + date.getDate() : date.getDate() let H = date.getHours() < 10 ? '0' + date.getHours() : date.getHours() let minute = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes() let S = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds() return `${Y}-${M}-${D} ${H}:${minute}:${S}` } }, async onPullDownRefresh() { await this.handleInitCanvas() await this.getList() uni.stopPullDownRefresh(); }, methods: { // 看视频双倍 lookVideo() { this.receiveGold(true) }, // 领取金币 receiveGold(watchVideos = false) { if(this.doubleKey) { setReceive({appId: config.appid, doubleKey: this.doubleKey, openId: this.openId, watchVideos, ...this.mpData }).then(res => { this.popupShow = false this.doubleKey = "" this.getList() }) } }, // getData() { uni.login({ success: async res => { if (res.code) { let openIDInfo = await getOpenId({appId: config.appid, code: res.code}) this.openId = openIDInfo.data let response = await this.getList() if(response) { await this.getPrizeList() getBanner({appId: config.appid, openId: this.openId, mpAppId: this.mpData.mpAppId}).then(res => { this.list = res.data }) } else { this.prizeList = [{"prizeId":1,"name":"100书币","stock":100,"weight":10,"prizeImage":"/static/turntable_golds.png"},{"prizeId":2,"name":"书城VIP碎片","stock":100,"weight":10,"prizeImage":"/static/turntable_VIP.png"},{"prizeId":3,"name":"30书币","stock":100,"weight":10,"prizeImage":"/static/turntable_gold.png"},{"prizeId":4,"name":"书城VIP碎片","stock":100,"weight":10,"prizeImage":"/static/turntable_VIP.png"},{"prizeId":5,"name":"10书币","stock":100,"weight":10,"prizeImage":"/static/turntable_gold.png"},{"prizeId":6,"name":"50书币","stock":100,"weight":10,"prizeImage":"/static/turntable_golds.png"},{"prizeId":7,"name":"iPhone12碎片","stock":100,"weight":10,"prizeImage":"/static/turntable_iPhone12.png"},{"prizeId":8,"name":"20书币","stock":100,"weight":10,"prizeImage":"/static/turntable_gold.png"}] this.prizeCountDtoList = [{"prizeType":2,"prizeCount":0,"prizeName":"书城VIP碎片","maxCount":30},{"prizeType":3,"prizeCount":0,"prizeName":"iPhone12碎片","maxCount":50}] } } } }) }, // 跳转小程序 jumpSign() { let path = 'pages/index/index' if(Object.keys(this.mpData).length > 0) { path = path + this.getSerialize(this.mpData) } uni.navigateToMiniProgram({ appId: 'wxb21c3e030b8af425', path, success(res) { // 打开成功 } }) }, // 对象序列化成 a=1&b=2 getSerialize(value) { if(Object.prototype.toString.call(value) === "[object Object]") { let path = "" for (const key in value) { if (Object.prototype.hasOwnProperty.call(value, key)) { const element = value[key]; if (path === "") { path = `?${key}=${element}` }else { path = path + `&${key}=${element}` } } } return path } else { return "" } }, // 活动规则 activityRulesHandle() { this.activityRules = !this.activityRules }, // 关闭弹窗 closeHandle() { this.receiveGold(false) this.popupShow = false }, // 重新生成 handleInitCanvas() { clearCacheFile() this.prizeList = [] this.getPrizeList() }, // 获取首页信息 async getList() { let res = await getHome({appId: config.appid, openId: this.openId, userSource: 1, ...this.mpData}) let data = res.data if(data) { this.userId = data.userId this.clearTime = data.clearTime this.prizeCountDtoList = data.prizeCountDtoList this.mpName = data.mpName this.freeNum = data.luckDraw this.mpData = { mpAppId: data.mpAppId, mpName: data.mpName, mpOpenId: data.mpOpenId } return Promise.resolve(true) } else { return Promise.resolve(false) } }, // 获取奖品列表 async getPrizeList() { uni.showLoading({ title: '奖品准备中...' }) let res = await getPond({appId: config.appid, openId: this.openId, mpAppId: this.mpData.mpAppId}) if(res.data && res.data.length > 0) { this.prizeList = res.data.map(item => { let prizeImage = "" if (item.prizeType === 1) { prizeImage = '/static/turntable_gold.png' if(item.prizeCount > 30) { prizeImage = '/static/turntable_golds.png' } return { prizeId: item.id, name: item.prizeCount + item.prizeName, stock: 100, weight: 10, prizeImage } } else { prizeImage = '/static/turntable_iPhone12.png' if(item.prizeType === 2){ prizeImage = '/static/turntable_VIP.png' } return { prizeId: item.id, name: item.prizeName, stock: 100, weight: 10, prizeImage } } }) }else { uni.hideLoading() uni.showToast({ title: '获取奖品失败' }) } }, // 本次抽奖开始 handleDrawStart() { console.log('触发抽奖按钮') if (!this.mpName) { // 首次没有重公众号进入 uni.navigateTo({ url: '../officialAccount/officialAccount' }) return } if (this.prizeing) return this.prizeing = true // 还有免费数次或者剩余金币足够抽一次 if (this.freeNum > 0) { // 更新免费次数或金币余额 if (this.freeNum > 0) { this.freeNum-- } // 发起抽奖 this.remoteGetPrizeIndex() } else { this.prizeing = false uni.showToast({ title: '抽奖次数不足', icon:'none' }) } }, // 远程请求接口获取中奖下标 remoteGetPrizeIndex() { console.warn('###当前处于模拟的请求接口,并返回了中奖信息###') let { mpName, ...mp } = this.mpData setDrawGain({appId: config.appid, openId: this.openId, ...mp}).then(res => { let data = res.data this.doubleKey = data.doubleKey let prizeIndex = this.prizeList.findIndex(item => item.prizeId === data.id) if(prizeIndex !== -1){ this.prizeIndex = prizeIndex } else { uni.showToast({ title: '抽奖异常,请稍后重试' }) } }) }, // 本次抽奖结束 handleDrawEnd() { console.log('旋转结束,执行拿到结果后到逻辑') // 旋转结束后,开始处理拿到结果后的逻辑 this.prizeing = false let prizeName = this.prizeList[this.prizeIndex].name let tipContent = '' if (prizeName === '谢谢参与') { tipContent = '很遗憾,没有中奖,请再接再厉!' } else if (prizeName.indexOf('碎片') === -1 && this.freeNum >= 8) { this.popupShow = true this.luckyDay = this.prizeList[this.prizeIndex] return } else { // 如果奖品设有库存 if (this.onStock) { let prizeStock = this.prizeList[this.prizeIndex].stock tipContent = prizeStock ? `恭喜您,获得 ${prizeName}` : `很抱歉,您来晚了,当前奖品 ${prizeName} 已无库存` } else { tipContent = `恭喜您,获得 ${prizeName} !` } } uni.showModal({ content: tipContent, showCancel: false }) this.getList() }, // 抽奖转盘绘制完成 handleDrawFinish(res) { console.log('抽奖转盘绘制完成', res) uni.showToast({ title: res.msg, duration: 3000, mask: true, icon: 'none' }) } } } </script> <style lang="scss"> page{ background-color: #F0524C; } .luckyDraw { position: relative; &>.back { width: 100%; } } .luckyDrawContent { position: absolute; top: 0; width: 100%; padding: 40rpx 0; margin-bottom: 20rpx; font-family: PingFangSC-Medium, PingFang SC; // 用户信息 .luckyDrawTop { padding: 0 28rpx; height: 104rpx; display: flex; justify-content: space-between; align-items: center; .left{ display: flex; justify-content: flex-start; align-items: center; &>.avatarView { overflow: hidden; width: 104rpx; height: 104rpx; background: #D8D8D8; border-radius: 10px; } .userInfo{ margin-left: 28rpx; &>view{ font-size: 32rpx; font-weight: 500; color: #FFFFFF; &.account { display: flex; justify-content: flex-start; align-items: center; &>image { width: 36rpx; height: 36rpx; margin-right: 10rpx; } } &.ID{ margin-top: 4rpx; } } } } .right{ width: 212rpx; height: 76rpx; background: #FEC435; border-radius: 40rpx; display: flex; justify-content: center; align-items: center; &>text { font-size: 28rpx; font-weight: 400; color: #FFFFFF; margin-left: 18rpx; } } } // 滚动通知 .notice { width: 656rpx; height: 68rpx; background: #C7322C; box-shadow: 0px 4rpx 8rpx 0rpx rgba(49, 49, 49, 0.08); border-radius: 38rpx; opacity: 0.53; margin: 58rpx auto 0; padding: 0 30rpx; box-sizing: border-box; display: flex; justify-content: flex-start; align-items: center; .horn { width: 28rpx; height: 28rpx; } .txts { // flex: 1; height: 68rpx; width: 600rpx; } } // 大转盘 .turntable { position: relative; margin-top: 40rpx; .decorate{ position: relative; width: 100%; height: 890rpx; &>image { width: 100%; position: absolute; z-index: 10; &.decorateBack { top: 0; } &.decorateChassis{ position: absolute; z-index: 1; bottom: 0; } } } .turntableContent { position: absolute; top: 0; left: 0; right: 0; bottom: 0; width: 100%; height: 100%; z-index: 20; padding-top: 20rpx; .bottom { margin-top: 20rpx; text-align: center; position: absolute; bottom: 35rpx; left: 50%; transform: translateX(-50%); .cont{ font-size: 28rpx; font-weight: 400; color: #FFFFFF; margin-bottom: 22rpx; text { font-size: 36rpx; color: #FEED22; font-weight: 500; } } .record{ font-size: 28rpx; font-weight: 500; color: #FFFFFF; } } } } // 我的奖品 .myPrize { margin: 40rpx 26rpx 0; min-height: 352rpx; background: #F4E2CA; border-radius: 20rpx; position: relative; padding: 58rpx 0 20rpx; .title { position: absolute; width: 232rpx; height: 64rpx; background: linear-gradient(180deg, #FFEDD1 0%, #FFC954 100%); box-shadow: 0 4rpx 8rpx 0 rgba(0, 0, 0, 0.07); border-radius: 0 0 24rpx 24rpx; font-size: 32rpx; font-weight: 500; color: #F33938; text-align: center; line-height: 64rpx; left: 50%; transform: translateX(-50%); top: -12rpx; } .chips { display: flex; justify-content: space-between; } .chip { display: flex; flex-direction: column; align-items: center; width: 50%; &>image { width: 208rpx; height: 160rpx; } .text{ font-size: 32rpx; font-weight: 500; color: #F33938; line-height: 44rpx; margin-top: 12rpx; } .progress{ position: relative; width: 200rpx; height: 40rpx; margin-top: 12rpx; background: #FFFCF7; border-radius: 26rpx; overflow: hidden; .value{ position: absolute; width: 1%; height: 100%; top: 0; left: 0; z-index: 1; background: #F37938; } &>text { font-size: 28rpx; font-weight: 500; color: #333333; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 2; } } } .clearTime{ margin: 20rpx 0 0; text-align: center; font-size: 24rpx; font-weight: 400; color: #F33938; } } // 活动规则 .activityRules { margin: 40rpx 28rpx 0; background: #F4E2CA; border-radius: 20rpx; color: #F33938; font-size: 32rpx; font-weight: 400; &>.top { height: 92rpx; padding: 0 28rpx; box-sizing: border-box; display: flex; justify-content: space-between; align-items: center; &>image { width: 40rpx; } } &>.bottom { padding: 0 28rpx 30rpx; box-sizing: border-box; &> view { margin-bottom: 12rpx; font-size: 28rpx; font-weight: 400; color: #666666; line-height: 46rpx; } } } // 弹窗 .popup { position: fixed; width: 100%; height: 100vh; background: rgba(0, 0, 0, 0.6); top: 0; left: 0; right: 0; bottom: 0; z-index: 100; .popupContent { width: 632rpx; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); text-align: center; &>.content { position: relative; &>image { width: 100%; height: 652rpx; } &>.textCon { position: absolute; width: 100%; top: 0; left: 0; right: 0; bottom: 0; padding-top: 192rpx; .ts { font-size: 60rpx; font-weight: 500; color: #F5631D; } .reward { height: 60rpx; background: #FFD9A2; border-radius: 31rpx; font-size: 32rpx; font-weight: 400; display: inline-block; padding: 0 32rpx; color: #F5631E; line-height: 60rpx; margin-top: 20rpx; } .doubleBt { width: 424rpx; height: 84rpx; margin-top: 114rpx; } .bt { margin-top: 14rpx; font-size: 32rpx; font-weight: 400; color: #FCE8CD; } } } &>.close { width: 54rpx; height: 54rpx; margin-top: 76rpx; } } } } </style>