敢为人鲜小程序前端代码仓库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

712 lines
17 KiB

10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
9 months ago
10 months ago
10 months ago
9 months ago
10 months ago
10 months ago
9 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
9 months ago
10 months ago
9 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
  1. <template>
  2. <view class="receive-gift">
  3. <template v-if="isOpeningEnd">
  4. <navbar title="礼品领取" leftClick @leftClick="$utils.navigateBack" />
  5. <!-- 主图片展示区 -->
  6. <view class="main-image">
  7. <image :src="giftInfo.image" mode="aspectFill"></image>
  8. </view>
  9. <!-- 礼品信息区域 -->
  10. <view class="gift-info">
  11. <view class="gift-name">{{giftInfo.title}}</view>
  12. <view class="gift-value">
  13. <text>礼品价值</text>
  14. <text class="price">{{giftInfo.price}}</text>
  15. </view>
  16. </view>
  17. <!-- 祝福语区域 -->
  18. <view class="blessing-area">
  19. <view class="sender-info">
  20. <text class="label">来自</text>
  21. <text class="sender">{{giftInfo.name}}</text>
  22. <text class="label">的祝福</text>
  23. </view>
  24. <view class="blessing-content">
  25. {{ giftInfo.giveTitle }}
  26. </view>
  27. </view>
  28. <!-- 底部区域 -->
  29. <view class="bottom-area">
  30. <!-- <view class="countdown">距离礼包失效{{countdownText}}</view> -->
  31. <!-- <button class="receive-btn"
  32. @click="receiveGift"
  33. :disabled="giftInfo.giveStatus !== 0">
  34. {{giftInfo.giveStatus === 0 ? '立即领取' : '已领取'}}
  35. </button> -->
  36. <button class="receive-btn"
  37. v-if="giftInfo.giveStatus != 1"
  38. @click="openAddress">
  39. 立即领取
  40. </button>
  41. </view>
  42. </template>
  43. <!-- 礼物开启动画弹窗 -->
  44. <uv-popup ref="giftPopup"
  45. mode="center"
  46. :round="20"
  47. :closeable="false"
  48. :closeOnClickOverlay="false"
  49. :maskClick="false"
  50. :customStyle="{backgroundColor: 'transparent', padding: 0}"
  51. overlayOpacity="0.8">
  52. <view class="gift-card" :class="{ 'open': isOpening }">
  53. <view class="gift-icon" :class="{ 'bounce': !isOpening }">
  54. <uv-icon name="gift-fill" size="120" color="#FFD700"></uv-icon>
  55. </view>
  56. <view class="gift-content">
  57. <view class="gift-title" :class="{ 'fade': isOpening }">
  58. {{ giftInfo.name }}送你一份礼物
  59. </view>
  60. <view class="gift-open-btn"
  61. @click="openGift"
  62. :class="{ 'pulse': !isOpening }">
  63. <text></text>
  64. <view class="btn-sparkle"></view>
  65. </view>
  66. <view class="gift-tip" :class="{ 'fade': isOpening }">
  67. {{ giftInfo.giveTitle }}
  68. </view>
  69. </view>
  70. </view>
  71. </uv-popup>
  72. <!-- 抽奖弹窗 -->
  73. <uv-popup ref="lotteryPopup"
  74. mode="center"
  75. :round="20"
  76. :closeable="false"
  77. :maskClick="false"
  78. :closeOnClickOverlay="false"
  79. :customStyle="{backgroundColor: 'transparent', padding: 0}"
  80. overlayOpacity="0.8">
  81. <view class="lottery-card">
  82. <view class="lottery-title">{{ giftInfo.name }}为您精选了一份抽奖礼包</view>
  83. <view class="gift-tip">
  84. {{ giftInfo.giveTitle }}
  85. </view>
  86. <view class="lottery-content">
  87. <view class="product-image">
  88. <image :src="giftInfo.image" mode="aspectFill"></image>
  89. </view>
  90. <view class="product-info">
  91. <view class="product-name">{{giftInfo.title}}</view>
  92. <view class="product-price">{{giftInfo.price}}</view>
  93. </view>
  94. </view>
  95. <view class="lottery-btn" @click="startLottery" v-if="!isSpinning">
  96. 试试手气
  97. </view>
  98. <view class="lottery-result" v-if="showResult">
  99. <text v-if="isWinner">恭喜您中奖了</text>
  100. <text v-else>很遗憾下次再来</text>
  101. </view>
  102. <view class="lottery-btn"
  103. @click="openAddress"
  104. v-if="isWinner && giftInfo.giveStatus != 1">
  105. 立即领取
  106. </view>
  107. </view>
  108. </uv-popup>
  109. <!-- 地址选择 -->
  110. <uv-popup ref="addressPopup" :round="30" style="padding-bottom: 90rpx;">
  111. <view class="addressPopupTitle">
  112. 填写地址就能收礼了
  113. </view>
  114. <!-- <view class="tabs">
  115. <uv-tabs :list="tabs"
  116. :activeStyle="{color : 'rgb(235, 51, 0)', fontWeight : 600}"
  117. lineColor="#fff"
  118. :inactiveStyle="{color: 'rgba(235, 51, 0, 0.8)'}"
  119. lineHeight="8rpx"
  120. lineWidth="50rpx"
  121. :current="addressCurrent"
  122. @click="clickTabs"></uv-tabs>
  123. </view> -->
  124. <addressList
  125. ref="addressList"
  126. height="60vh"
  127. v-if="addressCurrent == 1"
  128. @select="receiveGift" />
  129. <view class="redactAddressForm"
  130. v-else>
  131. <redactAddressForm
  132. ref="redactAddressForm"
  133. @saveOrUpdate="receiveGift"
  134. />
  135. <view
  136. @click="$refs.redactAddressForm.onSubmit"
  137. class="save">确认收礼</view>
  138. </view>
  139. </uv-popup>
  140. <loginPopup ref="loginPopup" @login="getGiftInfo"/>
  141. </view>
  142. </template>
  143. <script>
  144. import addressList from '../components/address/addressList.vue'
  145. import redactAddressForm from '../components/address/redactAddressForm.vue'
  146. import loginPopup from '@/components/config/loginPopup.vue'
  147. export default {
  148. components : {
  149. addressList,
  150. redactAddressForm,
  151. loginPopup,
  152. },
  153. data() {
  154. return {
  155. giftId: '',
  156. giftInfo: {
  157. },
  158. countdown: 24 * 60 * 60, // 倒计时秒数
  159. countdownTimer: null,
  160. // 领奖
  161. isOpening: false,
  162. isOpeningEnd : false,
  163. // 抽奖
  164. isSpinning: false,
  165. showResult: false,
  166. isWinner: false,
  167. // 地址
  168. addressTotal: 0,
  169. tabs: [
  170. {
  171. name: '新地址'
  172. },
  173. {
  174. name: '现有地址'
  175. }
  176. ],
  177. addressCurrent : 0,
  178. }
  179. },
  180. computed: {
  181. countdownText() {
  182. const hours = Math.floor(this.countdown / 3600)
  183. const minutes = Math.floor((this.countdown % 3600) / 60)
  184. const seconds = this.countdown % 60
  185. return `${hours}${minutes}${seconds}`
  186. }
  187. },
  188. methods: {
  189. // 打开礼物动画
  190. openGift() {
  191. this.isOpening = true
  192. setTimeout(() => {
  193. this.$refs.giftPopup.close()
  194. this.isOpeningEnd = true
  195. }, 1000)
  196. },
  197. // 开始抽奖
  198. startLottery() {
  199. if (this.isSpinning) return
  200. uni.showLoading({
  201. title: '抽奖中...',
  202. })
  203. this.isSpinning = true
  204. this.showResult = false
  205. // 模拟抽奖过程
  206. setTimeout(() => {
  207. this.$api('getGiveShopLottery', {
  208. orderId : this.giftId,
  209. }).then(res => {
  210. uni.hideLoading()
  211. this.showResult = true
  212. if(res.code == 200){
  213. this.isWinner = !!res.result
  214. }
  215. })
  216. // this.isSpinning = false
  217. // 这里可以根据实际接口返回决定是否中奖
  218. // this.isWinner = Math.random() > 0.5
  219. }, 2000)
  220. },
  221. // 获取礼品信息
  222. async getGiftInfo() {
  223. try {
  224. const res = await this.$api('getOrderDetail', {
  225. id: this.giftId
  226. })
  227. this.giftInfo = res.result
  228. this.$nextTick(() => {
  229. // 获取信息后显示弹窗
  230. if(this.giftInfo.isGive == 3){
  231. this.$refs.lotteryPopup.open()
  232. }else{
  233. this.$refs.giftPopup.open()
  234. }
  235. })
  236. // 计算倒计时
  237. // if (this.giftInfo.expireTime) {
  238. // const expireTime = new Date(this.giftInfo.expireTime).getTime()
  239. // const now = new Date().getTime()
  240. // this.countdown = Math.max(0, Math.floor((expireTime - now) / 1000))
  241. // this.startCountdown()
  242. // }
  243. } catch (e) {
  244. uni.showToast({
  245. title: '获取礼品信息失败',
  246. icon: 'none'
  247. })
  248. }
  249. },
  250. // 领取礼品
  251. async receiveGift(address) {
  252. try {
  253. let res = await this.$api('getGiveShop', {
  254. orderId : this.giftId,
  255. address : address.address,
  256. addressDetails : address.addressDetails,
  257. name : address.name,
  258. phone : address.phone,
  259. })
  260. if(res.code == 200){
  261. this.$refs.addressPopup.close()
  262. uni.showToast({
  263. title: '领取成功',
  264. icon: 'success'
  265. })
  266. }
  267. this.giftInfo.giveStatus = 1
  268. } catch (e) {
  269. uni.showToast({
  270. title: '领取失败',
  271. icon: 'none'
  272. })
  273. }
  274. },
  275. // 开始倒计时
  276. startCountdown() {
  277. this.countdownTimer = setInterval(() => {
  278. if (this.countdown > 0) {
  279. this.countdown--
  280. } else {
  281. clearInterval(this.countdownTimer)
  282. }
  283. }, 1000)
  284. },
  285. // 打开选择地址
  286. openAddress() {
  287. this.$refs.addressPopup.open('bottom')
  288. },
  289. // 获取地址列表
  290. getAddressList() {
  291. // 获取地址列表
  292. this.$refs.addressList.getAddressList().then(res => {
  293. this.addressTotal = res.total
  294. })
  295. },
  296. clickTabs({index}){
  297. this.addressCurrent = index
  298. if(index == 1){
  299. this.$nextTick(() => {
  300. this.getAddressList()
  301. })
  302. }
  303. },
  304. },
  305. onLoad(options) {
  306. if (options.shareId) {
  307. uni.setStorageSync('shareId', options.shareId)
  308. }
  309. if (options.id) {
  310. this.giftId = options.id
  311. if(uni.getStorageSync('token')){
  312. this.getGiftInfo()
  313. }else{
  314. this.$refs.loginPopup.open()
  315. }
  316. }
  317. },
  318. onShow() {
  319. },
  320. beforeDestroy() {
  321. if (this.countdownTimer) {
  322. clearInterval(this.countdownTimer)
  323. }
  324. }
  325. }
  326. </script>
  327. <style lang="scss" scoped>
  328. .receive-gift {
  329. min-height: 100vh;
  330. background: #fff;
  331. .main-image {
  332. width: 100%;
  333. height: 400rpx;
  334. image {
  335. width: 100%;
  336. height: 100%;
  337. }
  338. }
  339. .gift-info {
  340. padding: 30rpx;
  341. .gift-name {
  342. font-size: 36rpx;
  343. font-weight: 600;
  344. color: #333;
  345. margin-bottom: 20rpx;
  346. }
  347. .gift-value {
  348. font-size: 28rpx;
  349. color: #666;
  350. .price {
  351. color: #E3441A;
  352. font-weight: 600;
  353. margin-left: 10rpx;
  354. }
  355. }
  356. }
  357. .blessing-area {
  358. padding: 30rpx;
  359. background: #FFF5F5;
  360. margin: 30rpx;
  361. border-radius: 12rpx;
  362. .sender-info {
  363. text-align: center;
  364. margin-bottom: 20rpx;
  365. .label {
  366. color: #666;
  367. font-size: 28rpx;
  368. }
  369. .sender {
  370. color: #333;
  371. font-size: 32rpx;
  372. font-weight: 600;
  373. margin: 0 10rpx;
  374. }
  375. }
  376. .blessing-content {
  377. color: #333;
  378. font-size: 30rpx;
  379. line-height: 1.6;
  380. text-align: center;
  381. }
  382. }
  383. .bottom-area {
  384. position: fixed;
  385. left: 0;
  386. bottom: 0;
  387. width: 100%;
  388. box-sizing: border-box;
  389. padding: 20rpx 30rpx calc(30rpx + env(safe-area-inset-bottom)) 30rpx;
  390. background: #fff;
  391. box-shadow: 0 -2rpx 10rpx rgba(0,0,0,0.05);
  392. z-index: 10;
  393. .countdown {
  394. text-align: center;
  395. font-size: 28rpx;
  396. color: #666;
  397. margin-bottom: 20rpx;
  398. font-weight: 500;
  399. }
  400. .receive-btn {
  401. width: 100%;
  402. height: 88rpx;
  403. line-height: 88rpx;
  404. background: linear-gradient(to right, #FF4B4B, #E3441A);
  405. color: #fff;
  406. border-radius: 44rpx;
  407. font-size: 32rpx;
  408. font-weight: 500;
  409. &[disabled] {
  410. background: #ccc;
  411. opacity: 0.8;
  412. }
  413. }
  414. }
  415. }
  416. .addressPopupTitle{
  417. padding: 40rpx;
  418. font-size: 32rpx;
  419. font-weight: 900;
  420. text-align: center;
  421. }
  422. .redactAddressForm{
  423. padding: 0 40rpx;
  424. padding-bottom: 40rpx;
  425. .save {
  426. display: flex;
  427. align-items: center;
  428. justify-content: center;
  429. width: 90%;
  430. height: 80rpx;
  431. border-radius: 40rpx;
  432. color: white;
  433. font-size: 28rpx;
  434. margin: 0rpx auto;
  435. background: $uni-color;
  436. margin-top: 150rpx;
  437. }
  438. }
  439. // 普通礼包开启动画
  440. .gift-card {
  441. position: relative;
  442. background: $uni-color;
  443. border-radius: 20rpx;
  444. padding: 60rpx 40rpx;
  445. width: 600rpx;
  446. transform-origin: center;
  447. transition: all 0.8s cubic-bezier(0.68, -0.55, 0.265, 1.55);
  448. .gift-icon {
  449. position: absolute;
  450. top: -60rpx;
  451. left: 50%;
  452. transform: translateX(-50%);
  453. width: 120rpx;
  454. height: 120rpx;
  455. display: flex;
  456. align-items: center;
  457. justify-content: center;
  458. transition: all 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55);
  459. transform-origin: center bottom;
  460. animation: giftBounce 2s infinite;
  461. }
  462. .gift-content {
  463. margin-top: 60rpx;
  464. transform-origin: center;
  465. transition: all 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55);
  466. color: #fff;
  467. text-align: center;
  468. .gift-open-btn {
  469. position: relative;
  470. width: 120rpx;
  471. height: 120rpx;
  472. border-radius: 50%;
  473. background: linear-gradient(135deg, #FF4B4B, #E3441A);
  474. margin: 40rpx auto;
  475. display: flex;
  476. align-items: center;
  477. justify-content: center;
  478. cursor: pointer;
  479. overflow: hidden;
  480. box-shadow: 0 6rpx 20rpx rgba(227, 68, 26, 0.3);
  481. text {
  482. color: #fff;
  483. font-size: 36rpx;
  484. font-weight: bold;
  485. z-index: 1;
  486. }
  487. .btn-sparkle {
  488. position: absolute;
  489. top: -50%;
  490. left: -50%;
  491. width: 200%;
  492. height: 200%;
  493. background: linear-gradient(90deg,
  494. rgba(255,255,255,0) 0%,
  495. rgba(255,255,255,0.2) 50%,
  496. rgba(255,255,255,0) 100%);
  497. transform: rotate(45deg);
  498. animation: sparkle 2s infinite;
  499. }
  500. &.pulse {
  501. animation: pulse 2s infinite;
  502. }
  503. }
  504. .gift-title{
  505. color: #fff;
  506. font-size: 32rpx;
  507. font-weight: bold;
  508. margin-bottom: 20rpx;
  509. }
  510. .gift-title, .gift-tip {
  511. transition: all 0.5s ease;
  512. &.fade {
  513. opacity: 0;
  514. transform: translateY(-20rpx);
  515. }
  516. }
  517. .gift-icon {
  518. &.bounce {
  519. animation: giftBounce 2s infinite;
  520. }
  521. }
  522. }
  523. &.open {
  524. transform: perspective(1000px) rotateY(720deg) scale(0.5);
  525. opacity: 0;
  526. .gift-icon {
  527. transform: translateX(-50%) translateY(-200%) scale(1.5) rotate(720deg);
  528. opacity: 0;
  529. }
  530. .gift-content {
  531. transform: scale(0) rotate(-720deg);
  532. opacity: 0;
  533. }
  534. }
  535. }
  536. @keyframes sparkle {
  537. 0% {
  538. transform: rotate(45deg) translateX(-100%);
  539. }
  540. 50% {
  541. transform: rotate(45deg) translateX(100%);
  542. }
  543. 100% {
  544. transform: rotate(45deg) translateX(100%);
  545. }
  546. }
  547. @keyframes pulse {
  548. 0% {
  549. transform: scale(1);
  550. }
  551. 50% {
  552. transform: scale(1.05);
  553. }
  554. 100% {
  555. transform: scale(1);
  556. }
  557. }
  558. // 修改原有的giftBounce动画
  559. @keyframes giftBounce {
  560. 0%, 100% {
  561. transform: translateX(-50%) translateY(0) rotate(0deg);
  562. }
  563. 25% {
  564. transform: translateX(-50%) translateY(-15rpx) rotate(-5deg);
  565. }
  566. 75% {
  567. transform: translateX(-50%) translateY(-15rpx) rotate(5deg);
  568. }
  569. }
  570. // 抽奖动画
  571. .lottery-card {
  572. width: 600rpx;
  573. background: linear-gradient(135deg, #FF6B6B, #E3441A);
  574. border-radius: 20rpx;
  575. padding: 40rpx;
  576. text-align: center;
  577. .lottery-title {
  578. color: #fff;
  579. font-size: 32rpx;
  580. font-weight: bold;
  581. margin-bottom: 20rpx;
  582. }
  583. .gift-tip{
  584. color: #fff;
  585. font-size: 28rpx;
  586. margin-bottom: 40rpx;
  587. }
  588. .lottery-content {
  589. background: #fff;
  590. border-radius: 16rpx;
  591. padding: 30rpx;
  592. margin: 0 auto 30rpx;
  593. .product-image {
  594. width: 300rpx;
  595. height: 300rpx;
  596. margin: 0 auto 20rpx;
  597. border-radius: 8rpx;
  598. overflow: hidden;
  599. image {
  600. width: 100%;
  601. height: 100%;
  602. }
  603. }
  604. .product-info {
  605. text-align: center;
  606. .product-name {
  607. font-size: 28rpx;
  608. color: #333;
  609. margin-bottom: 10rpx;
  610. }
  611. .product-price {
  612. font-size: 36rpx;
  613. color: #E3441A;
  614. font-weight: bold;
  615. }
  616. }
  617. }
  618. .lottery-btn {
  619. margin-top: 40rpx;
  620. background: #FFD700;
  621. color: #E3441A;
  622. padding: 20rpx 60rpx;
  623. border-radius: 40rpx;
  624. display: inline-block;
  625. font-weight: bold;
  626. box-shadow: 0 6rpx 20rpx rgba(255, 215, 0, 0.3);
  627. }
  628. .lottery-result {
  629. margin-top: 30rpx;
  630. color: #fff;
  631. font-size: 32rpx;
  632. font-weight: bold;
  633. animation: fadeIn 0.5s ease;
  634. }
  635. }
  636. </style>