1
0

index.vue 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665
  1. <template>
  2. <view class="app-layout">
  3. <app-layout :haveBackground="false">
  4. <view class="scratch">
  5. <view class="bg" :style="{'background-image': `url(${setting.bg_pic})`}"></view>
  6. <view class="scratch-head">
  7. <view class="s-h" @click="rules">规则</view>
  8. <view class="s-h" @click="share">分享</view>
  9. <app-share-qr-code-poster v-model="shareShow" :url="poster" title="生成海报"></app-share-qr-code-poster>
  10. </view>
  11. <view class="scratch-oppty">
  12. <view>您还有
  13. <text>{{oppty}}</text>
  14. 次抽奖机会
  15. </view>
  16. </view>
  17. <view class="container">
  18. <image class="image-bg-a" src="./../image/scratch_border_bg.png" load-lazy></image>
  19. <image id='frame' class="image-bg-b" src="./../image/scratch_border.png"
  20. load-lazy></image>
  21. <view v-if="is_integral">
  22. <image class="image-bg-b" src="./../image/scratch_hide.png" load-lazy></image>
  23. <view @click="register" class="image-bg-c">{{setting.deplete_integral_num}}积分刮一次</view>
  24. </view>
  25. <view v-else class="integral-show">
  26. <view class="scratch-award main-center" :hidden="shareShow || userLoginShow">
  27. <!--头条小程序不支持action -->
  28. <canvas v-if="is_start"
  29. class="scratch-canvas"
  30. id="scratch"
  31. canvas-id="scratch"
  32. @touchstart="touchStart"
  33. @touchmove="touchMove"
  34. @touchend="touchEnd"
  35. disable-scroll></canvas>
  36. <view v-if="is_name" class="award-name">
  37. <text>{{list.name}}</text>
  38. <app-button v-if="oppty > 0" @click="onStart" background="#f05525" height="80"
  39. width="400"
  40. color="#FFFFFF" font-size="32" round>再刮一次
  41. </app-button>
  42. <app-button v-if="oppty == 0" @click="onStart" background="#cdcdcd" height="80"
  43. width="400"
  44. color="#FFFFFF" font-size="32" round>再刮一次
  45. </app-button>
  46. </view>
  47. </view>
  48. </view>
  49. </view>
  50. <view class="dir-left-nowrap main-center cross-center end">
  51. <view @click="home" class="dir-left-nowrap cross-center box-grow-0 left">
  52. <icon class="end-icon home"></icon>
  53. <view>回到首页</view>
  54. </view>
  55. <view @click="prize" class="dir-left-nowrap cross-center box-grow-0">
  56. <view>我的中奖记录</view>
  57. <icon class="end-icon price"></icon>
  58. </view>
  59. </view>
  60. <view class="prize">
  61. <view class="dir-left-nowrap cross-center main-center">
  62. <view class="line"></view>
  63. <view class="text">中奖名单</view>
  64. <view class="line"></view>
  65. </view>
  66. <swiper duration="500" vertical="true" autoplay="false" class="scratch-swiper">
  67. <swiper-item>
  68. <block v-for="v in logs" :key="v.id">
  69. <view class="scratch-item">{{v.create_time}} {{v.user}} {{v.name}}</view>
  70. </block>
  71. </swiper-item>
  72. </swiper>
  73. </view>
  74. </view>
  75. <!-- 弹窗 -->
  76. <view v-if="is_award">
  77. <view class="act-modal">
  78. <view class="act-modal-bg"></view>
  79. <view class="act-modal-pic cross-center main-center">
  80. <view class="success main-center">
  81. <icon class="image-bg" :style="{'background-image': `url(${appImg.scratch_win})`}">></icon>
  82. <view class="dir-top-nowrap name main-center">
  83. <view>恭喜获得</view>
  84. <text class="main-center">{{list.name}}</text>
  85. </view>
  86. </view>
  87. <view class="act-modal-end dir-top-nowrap cross-center main-center">
  88. <app-button @click="onStart" height="72" width="320" color="#FFFFFF" background="#f05525"
  89. round>再刮一次
  90. </app-button>
  91. <view class="act-zh">奖品已放入您的账号</view>
  92. </view>
  93. </view>
  94. </view>
  95. </view>
  96. </app-layout>
  97. </view>
  98. </template>
  99. <script>
  100. import appShareQrCodePoster from '../../../components/page-component/app-share-qr-code-poster/app-share-qr-code-poster';
  101. import {mapState} from 'vuex';
  102. export default {
  103. name: "index",
  104. components: {appShareQrCodePoster},
  105. computed: {
  106. ...mapState({
  107. appImg: state => state.mallConfig.plugin.scratch.app_image,
  108. userInfo: state => state.user.info,
  109. }),
  110. },
  111. data() {
  112. let ctx, interval, minX, minY, maxX, maxY, lastX, lastY;
  113. return {
  114. ctx,
  115. interval,
  116. minX,
  117. minY,
  118. maxX,
  119. maxY,
  120. lastX,
  121. lastY,
  122. logs: [],
  123. setting: null,
  124. list: null,
  125. oppty: 0,
  126. msg: '', //错误信息
  127. is_start: true,
  128. is_award: false,
  129. is_name: false,
  130. extra_list: null,
  131. touch_end: true,
  132. poster: this.$api.scratch.poster,
  133. poster_status: false, //hide model
  134. shareShow: false,
  135. userLoginShow: false,
  136. r: 16,
  137. is_integral: false,
  138. }
  139. },
  140. watch: {
  141. shareShow(val, oldVal) {
  142. if (val === false && oldVal === true) {
  143. this.init();
  144. }
  145. }
  146. },
  147. onUnload: function () {
  148. clearInterval(this.interval);
  149. },
  150. onHide: function () {
  151. clearInterval(this.interval);
  152. },
  153. onShareAppMessage: function () {
  154. return this.$shareAppMessage({
  155. title: this.$children[0].navigationBarTitle,
  156. path: '/plugins/scratch/index/index',
  157. params: {}
  158. });
  159. },
  160. onShow() {
  161. const self = this;
  162. self.$showLoading({title: '加载中'});
  163. self.$request({
  164. url: self.$api.scratch.index,
  165. }).then(info => {
  166. self.$hideLoading();
  167. self.is_award = false;
  168. if (info.data) {
  169. [self.msg, self.setting, self.oppty, self.list] = [info.code === 1 ? info.msg : '', info.data.setting, info.data.oppty, info.data.list];
  170. self.is_integral = self.setting.deplete_integral_num > 0;
  171. } else {
  172. uni.showToast({'title': info.msg, icon: 'none'});
  173. }
  174. self.init();
  175. });
  176. self.prizeList();
  177. self.interval = setInterval(() => {
  178. self.prizeList();
  179. }, 10000)
  180. },
  181. methods: {
  182. register() {
  183. if (this.msg) {
  184. uni.showToast({icon: `none`, title: this.msg});
  185. return;
  186. }
  187. this.is_integral = false;
  188. this.init();
  189. },
  190. share() {
  191. this.shareShow = true;
  192. },
  193. home() {
  194. uni.redirectTo({url: `/pages/index/index`});
  195. },
  196. prize() {
  197. uni.navigateTo({url: `/plugins/scratch/prize/prize`});
  198. },
  199. rules() {
  200. // uni.navigateTo({url: `/plugins/scratch/rule/rule`});
  201. uni.navigateTo({
  202. url: `/pages/rules/index?url=${encodeURIComponent(this.$api.scratch.setting)}&keys=${JSON.stringify(['setting', 'rule'])}`,
  203. });
  204. },
  205. prizeList() {
  206. const self = this;
  207. self.$request({
  208. url: self.$api.scratch.record,
  209. }).then(info => {
  210. if (info.code === 0) {
  211. self.logs = info.data;
  212. }
  213. })
  214. },
  215. onStart() {
  216. this.init();
  217. [this.is_integral, this.list, this.is_award] = [this.setting.deplete_integral_num > 0, this.extra_list, false];
  218. },
  219. init() {
  220. const self = this;
  221. [self.is_start, self.is_name] = [true, false];
  222. //Promise
  223. setTimeout(() => {
  224. let query = uni.createSelectorQuery();
  225. query.select('#frame').boundingClientRect();
  226. query.exec((res) => {
  227. const canvasWidth = res[0].width;
  228. const canvasHeight = res[0].height;
  229. let imageResource = './../image/scratch_hide.png';
  230. [self.lastX, self.lastY, self.minX, self.minY, self.maxX, self.maxY] = ['', '', '', '', '', ''];
  231. [self.r, self.canvasWidth, self.canvasHeight] = [16, canvasWidth, canvasHeight]
  232. let ctx = uni.createCanvasContext('scratch');
  233. ctx.drawImage(imageResource, 0, 0, canvasWidth, canvasHeight);
  234. self.ctx = ctx;
  235. ctx.draw(true, (e) => {
  236. self.is_name = true;
  237. })
  238. })
  239. })
  240. },
  241. //
  242. drawRect(x, y) {
  243. const self = this;
  244. const r = self.r / 2;
  245. const x1 = x - r > 0 ? x - r : 0
  246. const y1 = y - r > 0 ? y - r : 0
  247. if ('' !== self.minX) {
  248. self.minX = self.minX > x1 ? x1 : self.minX;
  249. self.minY = self.minY > y1 ? y1 : self.minY;
  250. self.maxX = self.maxX > x1 ? self.maxX : x1;
  251. self.maxY = self.maxY > y1 ? self.maxY : y1;
  252. } else {
  253. [self.minX, self.minY, self.maxX, self.maxY] = [x1, y1, x1, y1];
  254. }
  255. [self.lastX, self.lastY] = [x1, y1];
  256. return [x1, y1, 2 * r]
  257. },
  258. // 绘图
  259. clearArc(x, y, stepClear) {
  260. let r = this.r;
  261. let ctx = this.ctx;
  262. let calcWidth = r - stepClear;
  263. let calcHeight = Math.sqrt(r * r - calcWidth * calcWidth);
  264. let posX = x - calcWidth;
  265. let posY = y - calcHeight;
  266. let widthX = 2 * calcWidth;
  267. let heightY = 2 * calcHeight;
  268. if (stepClear <= r) {
  269. ctx.clearRect(posX, posY, widthX, heightY);
  270. stepClear += 2;
  271. this.clearArc(x, y, stepClear);
  272. }
  273. },
  274. //action
  275. touchStart(e) {
  276. const self = this;
  277. if (self.msg === 'User ID不能为空。') {
  278. this.userLoginShow = true;
  279. self.$store.dispatch('user/info');
  280. } else if (self.msg) {
  281. uni.showToast({icon: 'none', title: self.msg})
  282. }
  283. },
  284. touchMove(e) {
  285. if (!this.is_start || this.msg) return;
  286. let stepClear = 2;
  287. this.drawRect(e.touches[0].x, e.touches[0].y)
  288. this.clearArc(e.touches[0].x, e.touches[0].y, stepClear)
  289. this.ctx.draw(true)
  290. },
  291. touchEnd(e) {
  292. const self = this;
  293. if (!self.is_start || self.msg) return;
  294. //自动清楚采用点范围值方式判断
  295. let canvasWidth = self.canvasWidth;
  296. let canvasHeight = self.canvasHeight;
  297. let minX = self.minX;
  298. let minY = self.minY;
  299. let maxX = self.maxX;
  300. let maxY = self.maxY;
  301. if (maxX - minX > .4 * canvasWidth && maxY - minY > .4 * canvasHeight && self.touch_end) {
  302. self.touch_end = false;
  303. self.$request({
  304. url: self.$api.scratch.receive,
  305. data: {
  306. id: self.list.id
  307. }
  308. }).then(info => {
  309. if (info.code === 0) {
  310. self.ctx.draw();
  311. [self.msg, self.setting, self.oppty, self.extra_list] = [info.msg, info.data.setting, info.data.oppty, info.data.list];
  312. [self.is_start, self.is_award] = [false, self.list.type != 5];
  313. } else {
  314. uni.showToast({icon: 'none', content: info.msg});
  315. self.onStart();
  316. }
  317. self.touch_end = true;
  318. });
  319. }
  320. },
  321. }
  322. }
  323. </script>
  324. <style scoped lang="scss">
  325. .app-layout /deep/ .app-layout {
  326. background: #5e00b8;
  327. min-height: calc(100vh - var(--window-top));
  328. }
  329. .scratch-canvas {
  330. width: #{600rpx};
  331. height: #{320rpx};
  332. position: absolute;
  333. left: 0;
  334. z-index: 1;
  335. }
  336. .scratch {
  337. text-align: center;
  338. padding-bottom: #{1rpx};
  339. .bg {
  340. position: absolute;
  341. left: 0;
  342. top: 0;
  343. height: calc(100vh - var(--window-top));
  344. width: #{100%};
  345. display: block;
  346. background-repeat: no-repeat;
  347. background-size: 100% 100%;
  348. }
  349. .scratch-head {
  350. .s-h {
  351. color: #FFFFFF;
  352. font-size: $uni-font-size-weak-one;
  353. background: #000000;
  354. text-align: center;
  355. background-color: rgba(0, 0, 0, 0.3);
  356. line-height: #{48rpx};
  357. width: #{92rpx};
  358. border-radius: #{24rpx} 0 0 #{24rpx};
  359. position: absolute;
  360. right: 0;
  361. z-index: 1;
  362. top: #{40rpx};
  363. }
  364. .s-h:nth-child(2) {
  365. top: #{112rpx}
  366. }
  367. }
  368. .scratch-oppty {
  369. line-height: #{56rpx};
  370. font-size: #{28rpx};
  371. padding-top: #{380rpx};
  372. position: relative;
  373. view {
  374. color: #ffffff;
  375. padding: 0 #{30rpx};
  376. display: inline-block;
  377. background: rgba(0, 0, 0, 0.3);;
  378. border-radius: #{28rpx};
  379. }
  380. text {
  381. color: #ffb92a;
  382. }
  383. }
  384. .container {
  385. position: relative;
  386. margin-top: #{40rpx};
  387. margin-bottom: #{48rpx};
  388. height: #{360rpx};
  389. width: 100%;
  390. .image-bg-a {
  391. height: #{360rpx};
  392. width: #{654rpx};
  393. }
  394. .image-bg-b {
  395. height: #{320rpx};
  396. width: #{600rpx};
  397. position: absolute;
  398. top: #{20rpx};
  399. left: 50%;
  400. -webkit-transform: translate(-50%, -50%);
  401. transform: translate(-50%, 0);
  402. }
  403. .image-bg-c {
  404. position: absolute;
  405. top: #{150rpx};
  406. left: 50%;
  407. -webkit-transform: translate(-50%, -50%);
  408. transform: translate(-50%, 0);
  409. line-height: #{80rpx};
  410. background: #f05525;
  411. border-radius: #{40rpx};
  412. padding: 0 #{48rpx};
  413. color: #ffffff;
  414. }
  415. }
  416. .end {
  417. position: relative;
  418. color: #FFFFFF;
  419. font-size: #{28rpx};
  420. .left {
  421. margin-right: #{160rpx};
  422. }
  423. .end-icon {
  424. background-repeat: no-repeat;
  425. background-size: 100% 100%;
  426. margin: 0 #{16rpx};
  427. }
  428. .end-icon.home {
  429. height: #{30rpx};
  430. width: #{30rpx};
  431. background-image: url('../../../static/image/icon/icon-home-white.png');
  432. }
  433. .end-icon.price {
  434. height: #{22rpx};
  435. width: #{12rpx};
  436. background-image: url('../../../static/image/icon/arrow-right-white.png');
  437. }
  438. }
  439. .prize {
  440. position: relative;
  441. height: #{186rpx};
  442. width: #{654rpx};
  443. background: #420080;
  444. display: inline-block;
  445. border-radius: #{16rpx};
  446. text-align: left;
  447. padding-top: #{26rpx};
  448. margin-top: #{48rpx};
  449. .line {
  450. height: #{1px};
  451. width: #{120rpx};
  452. background: #FFFFFF;
  453. }
  454. .text {
  455. padding: 0 #{40rpx};
  456. color: #FFFFFF;
  457. font-size: #{28rpx};
  458. padding-bottom:#{12rpx};
  459. }
  460. .scratch-swiper {
  461. height: #{110rpx};
  462. font-size: #{24rpx};
  463. color: #ffffff;
  464. }
  465. .scratch-item {
  466. line-height: #{40rpx};
  467. padding: 0 #{50rpx};
  468. display: inline-block;
  469. white-space: nowrap;
  470. width: 100%;
  471. overflow: hidden;
  472. text-overflow: ellipsis;
  473. }
  474. }
  475. .integral-show {
  476. position: absolute;
  477. top: #{20rpx};
  478. left: 50%;
  479. -webkit-transform: translate(-50%, -50%);
  480. transform: translate(-50%, 0);
  481. width: #{600rpx};
  482. height: #{320rpx};
  483. .scratch-award {
  484. width: 100%;
  485. height: 100%;
  486. canvas {
  487. }
  488. .award-name {
  489. text {
  490. width: #{410rpx};
  491. color: #f05525;
  492. font-size: #{36rpx};
  493. margin-bottom: #{40rpx};
  494. margin-top: #{88rpx};
  495. overflow: hidden;
  496. text-overflow: ellipsis;
  497. display: -webkit-box;
  498. -webkit-line-clamp: 1;
  499. -webkit-box-orient: vertical;
  500. }
  501. view {
  502. }
  503. }
  504. }
  505. }
  506. .scraxtch-award {
  507. view {
  508. width: #{400rpx};
  509. line-height: #{80rpx};
  510. color: #ffffff;
  511. margin-top: #{40rpx};
  512. border-radius: #{40rpx};
  513. }
  514. .next {
  515. background: #f05525;
  516. }
  517. .none {
  518. background: #cdcdcd;
  519. }
  520. }
  521. }
  522. .act-modal {
  523. position: fixed;
  524. left: 0;
  525. top: 0;
  526. width: 100%;
  527. height: 100%;
  528. z-index: 2001;
  529. transition: 200ms;
  530. .show {
  531. visibility: visible;
  532. opacity: 1;
  533. }
  534. .act-modal-bg {
  535. background: rgba(0, 0, 0, 0.5);
  536. position: fixed;
  537. left: 0;
  538. top: 0;
  539. width: 100%;
  540. height: 100%;
  541. z-index: 1;
  542. }
  543. .act-modal-pic {
  544. background: rgba(0, 0, 0, 0.25);
  545. position: fixed;
  546. left: 0;
  547. top: 0;
  548. width: 100%;
  549. height: 100%;
  550. z-index: 1;
  551. }
  552. .success {
  553. position: absolute;
  554. top: 0;
  555. width: 100%;
  556. left: 0;
  557. .image-bg {
  558. height: #{731rpx};
  559. width: 100%;
  560. background-repeat: no-repeat;
  561. background-size: 100% 100%;
  562. }
  563. .name {
  564. position: absolute;
  565. top: #{540rpx};
  566. font-size: #{32rpx};
  567. color: #f05525;
  568. left: 50%;
  569. right: 0;
  570. text-align: center;
  571. -webkit-transform: translate(-50%, 0);
  572. transform: translate(-50%, 0);
  573. view {
  574. padding-bottom: #{10rpx};
  575. }
  576. text {
  577. //text-align: left;
  578. }
  579. }
  580. }
  581. .act-modal-end {
  582. position: absolute;
  583. top: #{730rpx};
  584. border-bottom: #{10rpx} solid #ffb947;
  585. border-radius: 0 0 #{24rpx} #{24rpx};
  586. border-left: #{10rpx} solid #ffb947;
  587. border-right: #{10rpx} solid #ffb947;
  588. width: #{490rpx};
  589. background: #ffffff;
  590. height: #{160rpx};
  591. left: 0;
  592. right: 0;
  593. margin: 0 auto;
  594. .act-zh {
  595. margin-top: #{24rpx};
  596. font-size: #{24rpx};
  597. color: #999999;
  598. }
  599. }
  600. }
  601. </style>