luckDraw.vue 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. <template>
  2. <view class="content">
  3. <view class="luckwheel">
  4. <almost-lottery
  5. :prize-list="prizeList"
  6. :prize-index="prizeIndex"
  7. @reset-index="prizeIndex = -1"
  8. @draw-start="handleDrawStart"
  9. @draw-end="handleDrawEnd"
  10. @finish="handleDrawFinish"
  11. v-if="prizeList.length"
  12. />
  13. <view class="synums">
  14. 抽奖剩余次数:
  15. <text>2</text>
  16. </view>
  17. </view>
  18. <!-- 获奖人员滚动列表 -->
  19. <view class="hjList">
  20. <view>
  21. 恭喜
  22. <text>185****5252</text>
  23. 获得
  24. <text>7</text>
  25. 乐豆
  26. </view>
  27. <view>
  28. 恭喜
  29. <text>185****5252</text>
  30. 获得
  31. <text>7</text>
  32. 乐豆
  33. </view>
  34. <view>
  35. 恭喜
  36. <text>185****5252</text>
  37. 获得
  38. <text>7</text>
  39. 乐豆
  40. </view>
  41. <view>
  42. 恭喜
  43. <text>185****5252</text>
  44. 获得
  45. <text>7</text>
  46. 乐豆
  47. </view>
  48. <view>
  49. 恭喜
  50. <text>185****5252</text>
  51. 获得
  52. <text>7</text>
  53. 乐豆
  54. </view>
  55. <view>
  56. 恭喜
  57. <text>185****5252</text>
  58. 获得
  59. <text>7</text>
  60. 乐豆
  61. </view>
  62. <view>
  63. 恭喜
  64. <text>185****5252</text>
  65. 获得
  66. <text>7</text>
  67. 乐豆
  68. </view>
  69. <view>
  70. 恭喜
  71. <text>185****5252</text>
  72. 获得
  73. <text>7</text>
  74. 乐豆
  75. </view>
  76. <view>
  77. 恭喜
  78. <text>185****5252</text>
  79. 获得
  80. <text>7</text>
  81. 乐豆
  82. </view>
  83. </view>
  84. <!-- 抽奖规则 -->
  85. <view class="guize">
  86. <view>抽奖规则</view>
  87. <view>
  88. <text>1.活动时间:</text>
  89. <text>2020.12.10 至 2020.12.31</text>
  90. </view>
  91. <view>
  92. <text>2.奖品:</text>
  93. <view>
  94. <view>一等奖 华为P40</view>
  95. <view>二等奖 小米电动牙刷</view>
  96. <view>三等奖 瑞士军刀</view>
  97. <view>四等奖 20乐豆</view>
  98. <view>五等奖 7乐豆</view>
  99. </view>
  100. </view>
  101. <view><text>3.抽奖条件:</text></view>
  102. <view>①首次注册乐色管家小程序即可获得一次抽奖机会;</view>
  103. <view>②分享给好友或发送到朋友圈可获得1次抽奖机会;</view>
  104. <view><text>4.抽奖说明:</text></view>
  105. <view>本活动最终解释权归XXXXXX所有。</view>
  106. </view>
  107. </view>
  108. </template>
  109. <script>
  110. import AlmostLottery from '@/components/almost-lottery/almost-lottery.vue';
  111. import { clearCacheFile } from '@/almost-utils/almost-utils.js';
  112. export default {
  113. name: 'Home',
  114. components: {
  115. AlmostLottery
  116. },
  117. data() {
  118. return {
  119. // canvas 宽高
  120. canvasData: {
  121. width: 200,
  122. height: 200
  123. },
  124. // 奖品数据
  125. prizeList: [],
  126. // 中奖下标
  127. prizeIndex: -1,
  128. // 中奖类目名称
  129. targetName: '',
  130. // 奖品是否设有库存
  131. onStock: true,
  132. // 是否由前端控制概率,默认不开启
  133. onFrontend: false,
  134. // 权重随机数的最大值
  135. weightTotal: 0,
  136. // 权重数组
  137. weightArr: []
  138. };
  139. },
  140. methods: {
  141. // 重新生成
  142. handleInitCanvas() {
  143. clearCacheFile();
  144. this.targetName = '';
  145. this.prizeList = [];
  146. this.getPrizeList();
  147. },
  148. // 获取奖品列表
  149. async getPrizeList() {
  150. uni.showLoading({
  151. title: '奖品准备中...'
  152. });
  153. let res = await this.requestPrizeList();
  154. console.log('获取奖品列表', res);
  155. if (res.ok) {
  156. let data = res.data;
  157. if (data.length) {
  158. // stock 奖品库存
  159. // weight 中奖概率,数值越大中奖概率越高
  160. this.prizeList = data;
  161. // 如果开启了前端控制概率
  162. // 计算出权重的总和并生成权重数组
  163. if (this.onFrontend) {
  164. this.prizeList.forEach(item => (this.weightTotal += item.weight));
  165. this.weightArr = this.prizeList.map(item => item.weight);
  166. }
  167. }
  168. } else {
  169. uni.hideLoading();
  170. uni.showToast({
  171. title: '获取奖品失败'
  172. });
  173. }
  174. },
  175. // 模拟请求奖品列表接口
  176. requestPrizeList() {
  177. return new Promise((resolve, reject) => {
  178. let requestTimer = setTimeout(() => {
  179. clearTimeout(requestTimer);
  180. requestTimer = null;
  181. resolve({
  182. ok: true,
  183. data: [
  184. {
  185. prizeId: 1,
  186. name: '0.1元现金',
  187. stock: 10,
  188. weight: 0,
  189. prizeImage: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/56f085e0-bcfe-11ea-b244-a9f5e5565f30.png'
  190. },
  191. {
  192. prizeId: 2,
  193. name: '10元现金',
  194. stock: 0,
  195. weight: 0,
  196. prizeImage: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/56f085e0-bcfe-11ea-b244-a9f5e5565f30.png'
  197. },
  198. { prizeId: 3, name: '5元话费', stock: 1, weight: 0 },
  199. { prizeId: 4, name: '50元现金', stock: 0, weight: 0 },
  200. { prizeId: 5, name: '1卷抽纸', stock: 3, weight: 0 },
  201. { prizeId: 6, name: '0.2元现金', stock: 8, weight: 0 },
  202. { prizeId: 7, name: '谢谢参与', stock: 100, weight: 0 },
  203. { prizeId: 8, name: '100金币', stock: 100, weight: 0 }
  204. ]
  205. });
  206. }, 2000);
  207. });
  208. },
  209. // 本次抽奖开始
  210. handleDrawStart() {
  211. this.targetName = '';
  212. let list = [...this.prizeList];
  213. // 模拟请求
  214. let stoTimer = setTimeout(() => {
  215. clearTimeout(stoTimer);
  216. stoTimer = null;
  217. // 判断是否由前端控制概率
  218. // 前端控制概率的情况下,需要拿到最接近随机权重且大于随机权重的值
  219. // 后端控制概率的情况下,通常会直接返回 prizeId
  220. if (this.onFrontend) {
  221. if (!this.weightTotal) {
  222. console.warn('###当前已开启前端控制中奖概率,但是奖品数据列表中的 weight 参数似乎配置不正确###');
  223. return;
  224. }
  225. console.warn('###当前处于前端控制中奖概率,安全起见,强烈建议由后端控制###');
  226. console.log('当前权重总和为 =>', this.weightTotal);
  227. // 注意这里使用了 Math.ceil,如果某个权重的值为 0,则始终无法中奖
  228. let weight = Math.ceil(Math.random() * this.weightTotal);
  229. console.log('本次权重随机数 =>', weight);
  230. // 生成大于等于随机权重的数组
  231. let tempMaxArrs = [];
  232. list.forEach(item => {
  233. if (item.weight >= weight) {
  234. tempMaxArrs.push(item.weight);
  235. }
  236. });
  237. // 如果大于随机权重的数组有值,先对这个数组排序然后取值
  238. // 反之新建一个临时的包含所有权重的已排序数组,然后取值
  239. if (tempMaxArrs.length) {
  240. tempMaxArrs.sort((a, b) => a - b);
  241. this.prizeIndex = this.weightArr.indexOf(tempMaxArrs[0]);
  242. } else {
  243. let tempWeightArr = [...this.weightArr];
  244. tempWeightArr.sort((a, b) => a - b);
  245. this.prizeIndex = this.weightArr.indexOf(tempWeightArr[tempWeightArr.length - 1]);
  246. }
  247. } else {
  248. console.warn('###当前处于模拟的随机中奖概率,实际场景中,中奖概率应由后端控制###');
  249. // 这里随机产生的 prizeId 是模拟后端返回的 prizeId
  250. let prizeId = Math.floor(Math.random() * list.length + 1);
  251. list.forEach((item, index) => {
  252. if (item.prizeId === prizeId) {
  253. // 中奖下标
  254. this.prizeIndex = index;
  255. }
  256. });
  257. }
  258. console.log('本次抽中奖品 =>', this.prizeList[this.prizeIndex].name);
  259. if (this.onStock) {
  260. console.log('本次奖品库存 =>', this.prizeList[this.prizeIndex].stock);
  261. }
  262. }, 200);
  263. },
  264. // 本次抽奖结束
  265. handleDrawEnd() {
  266. // 旋转结束后,可以执行拿到结果后的逻辑
  267. let prizeName = this.prizeList[this.prizeIndex].name;
  268. if (this.onStock) {
  269. let prizeStock = this.prizeList[this.prizeIndex].stock;
  270. this.targetName = prizeName === '谢谢参与' ? prizeName : prizeStock ? `恭喜您,获得 ${prizeName}` : '很抱歉,您来晚了,当前奖品已无库存';
  271. }else{
  272. this.targetName = prizeName === '谢谢参与' ? prizeName : `恭喜您,获得 ${prizeName}`;
  273. }
  274. console.log(this.targetName)
  275. },
  276. // 抽奖转盘绘制完成
  277. handleDrawFinish(res) {
  278. console.log('抽奖转盘绘制完成', res);
  279. uni.showToast({
  280. title: res.msg,
  281. duration: 2000,
  282. mask: true,
  283. icon: 'none'
  284. });
  285. }
  286. },
  287. onLoad() {
  288. // 模拟请求奖品数据
  289. this.getPrizeList();
  290. },
  291. onUnload() {
  292. uni.hideLoading();
  293. }
  294. };
  295. </script>
  296. <style lang="less">
  297. .content {
  298. width: 100%;
  299. background: linear-gradient(#00287a, #002268);
  300. .luckwheel {
  301. padding: 30rpx 0;
  302. .synums {
  303. font-size: 28rpx;
  304. color: #fff;
  305. padding-top: 20rpx;
  306. text-align: center;
  307. text {
  308. color: #ffff00;
  309. margin-left: 6rpx;
  310. }
  311. }
  312. }
  313. .hjList {
  314. height: 30vh;
  315. overflow: auto;
  316. > view {
  317. width: 70%;
  318. margin: 10rpx auto;
  319. background: #acdbff;
  320. padding: 6rpx;
  321. border-radius: 50rpx;
  322. text-align: center;
  323. font-size: 24rpx;
  324. }
  325. }
  326. .guize {
  327. color: #ffffff;
  328. padding: 30rpx 0;
  329. width: 70%;
  330. margin: 0 auto;
  331. align-items: left;
  332. font-size: 24rpx;
  333. > view {
  334. display: flex;
  335. align-items: center;
  336. padding: 10rpx 0;
  337. width: 100%;
  338. }
  339. }
  340. }
  341. </style>