敢为人鲜小程序前端代码仓库
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.

701 lines
17 KiB

3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 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. </view>
  141. </template>
  142. <script>
  143. import addressList from '../components/address/addressList.vue'
  144. import redactAddressForm from '../components/address/redactAddressForm.vue'
  145. export default {
  146. components : {
  147. addressList,
  148. redactAddressForm,
  149. },
  150. data() {
  151. return {
  152. giftId: '',
  153. giftInfo: {
  154. },
  155. countdown: 24 * 60 * 60, // 倒计时秒数
  156. countdownTimer: null,
  157. // 领奖
  158. isOpening: false,
  159. isOpeningEnd : false,
  160. // 抽奖
  161. isSpinning: false,
  162. showResult: false,
  163. isWinner: false,
  164. // 地址
  165. addressTotal: 0,
  166. tabs: [
  167. {
  168. name: '新地址'
  169. },
  170. {
  171. name: '现有地址'
  172. }
  173. ],
  174. addressCurrent : 0,
  175. }
  176. },
  177. computed: {
  178. countdownText() {
  179. const hours = Math.floor(this.countdown / 3600)
  180. const minutes = Math.floor((this.countdown % 3600) / 60)
  181. const seconds = this.countdown % 60
  182. return `${hours}${minutes}${seconds}`
  183. }
  184. },
  185. methods: {
  186. // 打开礼物动画
  187. openGift() {
  188. this.isOpening = true
  189. setTimeout(() => {
  190. this.$refs.giftPopup.close()
  191. this.isOpeningEnd = true
  192. }, 1000)
  193. },
  194. // 开始抽奖
  195. startLottery() {
  196. if (this.isSpinning) return
  197. uni.showLoading({
  198. title: '抽奖中...',
  199. })
  200. this.isSpinning = true
  201. this.showResult = false
  202. // 模拟抽奖过程
  203. setTimeout(() => {
  204. this.$api('getGiveShopLottery', {
  205. orderId : this.giftId,
  206. }).then(res => {
  207. uni.hideLoading()
  208. this.showResult = true
  209. if(res.code == 200){
  210. this.isWinner = !!res.result
  211. }
  212. })
  213. // this.isSpinning = false
  214. // 这里可以根据实际接口返回决定是否中奖
  215. // this.isWinner = Math.random() > 0.5
  216. }, 2000)
  217. },
  218. // 获取礼品信息
  219. async getGiftInfo() {
  220. try {
  221. const res = await this.$api('getOrderDetail', {
  222. id: this.giftId
  223. })
  224. this.giftInfo = res.result
  225. this.$nextTick(() => {
  226. // 获取信息后显示弹窗
  227. if(this.giftInfo.isGive == 3){
  228. this.$refs.lotteryPopup.open()
  229. }else{
  230. this.$refs.giftPopup.open()
  231. }
  232. })
  233. // 计算倒计时
  234. // if (this.giftInfo.expireTime) {
  235. // const expireTime = new Date(this.giftInfo.expireTime).getTime()
  236. // const now = new Date().getTime()
  237. // this.countdown = Math.max(0, Math.floor((expireTime - now) / 1000))
  238. // this.startCountdown()
  239. // }
  240. } catch (e) {
  241. uni.showToast({
  242. title: '获取礼品信息失败',
  243. icon: 'none'
  244. })
  245. }
  246. },
  247. // 领取礼品
  248. async receiveGift(address) {
  249. try {
  250. let res = await this.$api('getGiveShop', {
  251. orderId : this.giftId,
  252. address : address.address,
  253. addressDetails : address.addressDetails,
  254. name : address.name,
  255. phone : address.phone,
  256. })
  257. if(res.code == 200){
  258. this.$refs.addressPopup.close()
  259. uni.showToast({
  260. title: '领取成功',
  261. icon: 'success'
  262. })
  263. }
  264. this.giftInfo.giveStatus = 1
  265. } catch (e) {
  266. uni.showToast({
  267. title: '领取失败',
  268. icon: 'none'
  269. })
  270. }
  271. },
  272. // 开始倒计时
  273. startCountdown() {
  274. this.countdownTimer = setInterval(() => {
  275. if (this.countdown > 0) {
  276. this.countdown--
  277. } else {
  278. clearInterval(this.countdownTimer)
  279. }
  280. }, 1000)
  281. },
  282. // 打开选择地址
  283. openAddress() {
  284. this.$refs.addressPopup.open('bottom')
  285. },
  286. // 获取地址列表
  287. getAddressList() {
  288. // 获取地址列表
  289. this.$refs.addressList.getAddressList().then(res => {
  290. this.addressTotal = res.total
  291. })
  292. },
  293. clickTabs({index}){
  294. this.addressCurrent = index
  295. if(index == 1){
  296. this.$nextTick(() => {
  297. this.getAddressList()
  298. })
  299. }
  300. },
  301. },
  302. onLoad(options) {
  303. if (options.id) {
  304. this.giftId = options.id
  305. this.getGiftInfo()
  306. }
  307. },
  308. onShow() {
  309. },
  310. beforeDestroy() {
  311. if (this.countdownTimer) {
  312. clearInterval(this.countdownTimer)
  313. }
  314. }
  315. }
  316. </script>
  317. <style lang="scss" scoped>
  318. .receive-gift {
  319. min-height: 100vh;
  320. background: #fff;
  321. .main-image {
  322. width: 100%;
  323. height: 400rpx;
  324. image {
  325. width: 100%;
  326. height: 100%;
  327. }
  328. }
  329. .gift-info {
  330. padding: 30rpx;
  331. .gift-name {
  332. font-size: 36rpx;
  333. font-weight: 600;
  334. color: #333;
  335. margin-bottom: 20rpx;
  336. }
  337. .gift-value {
  338. font-size: 28rpx;
  339. color: #666;
  340. .price {
  341. color: #E3441A;
  342. font-weight: 600;
  343. margin-left: 10rpx;
  344. }
  345. }
  346. }
  347. .blessing-area {
  348. padding: 30rpx;
  349. background: #FFF5F5;
  350. margin: 30rpx;
  351. border-radius: 12rpx;
  352. .sender-info {
  353. text-align: center;
  354. margin-bottom: 20rpx;
  355. .label {
  356. color: #666;
  357. font-size: 28rpx;
  358. }
  359. .sender {
  360. color: #333;
  361. font-size: 32rpx;
  362. font-weight: 600;
  363. margin: 0 10rpx;
  364. }
  365. }
  366. .blessing-content {
  367. color: #333;
  368. font-size: 30rpx;
  369. line-height: 1.6;
  370. text-align: center;
  371. }
  372. }
  373. .bottom-area {
  374. position: fixed;
  375. left: 0;
  376. bottom: 0;
  377. width: 100%;
  378. box-sizing: border-box;
  379. padding: 20rpx 30rpx calc(30rpx + env(safe-area-inset-bottom)) 30rpx;
  380. background: #fff;
  381. box-shadow: 0 -2rpx 10rpx rgba(0,0,0,0.05);
  382. z-index: 10;
  383. .countdown {
  384. text-align: center;
  385. font-size: 28rpx;
  386. color: #666;
  387. margin-bottom: 20rpx;
  388. font-weight: 500;
  389. }
  390. .receive-btn {
  391. width: 100%;
  392. height: 88rpx;
  393. line-height: 88rpx;
  394. background: linear-gradient(to right, #FF4B4B, #E3441A);
  395. color: #fff;
  396. border-radius: 44rpx;
  397. font-size: 32rpx;
  398. font-weight: 500;
  399. &[disabled] {
  400. background: #ccc;
  401. opacity: 0.8;
  402. }
  403. }
  404. }
  405. }
  406. .addressPopupTitle{
  407. padding: 40rpx;
  408. font-size: 32rpx;
  409. font-weight: 900;
  410. text-align: center;
  411. }
  412. .redactAddressForm{
  413. padding: 0 40rpx;
  414. padding-bottom: 40rpx;
  415. .save {
  416. display: flex;
  417. align-items: center;
  418. justify-content: center;
  419. width: 90%;
  420. height: 80rpx;
  421. border-radius: 40rpx;
  422. color: white;
  423. font-size: 28rpx;
  424. margin: 0rpx auto;
  425. background: $uni-color;
  426. margin-top: 150rpx;
  427. }
  428. }
  429. // 普通礼包开启动画
  430. .gift-card {
  431. position: relative;
  432. background: $uni-color;
  433. border-radius: 20rpx;
  434. padding: 60rpx 40rpx;
  435. width: 600rpx;
  436. transform-origin: center;
  437. transition: all 0.8s cubic-bezier(0.68, -0.55, 0.265, 1.55);
  438. .gift-icon {
  439. position: absolute;
  440. top: -60rpx;
  441. left: 50%;
  442. transform: translateX(-50%);
  443. width: 120rpx;
  444. height: 120rpx;
  445. display: flex;
  446. align-items: center;
  447. justify-content: center;
  448. transition: all 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55);
  449. transform-origin: center bottom;
  450. animation: giftBounce 2s infinite;
  451. }
  452. .gift-content {
  453. margin-top: 60rpx;
  454. transform-origin: center;
  455. transition: all 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55);
  456. color: #fff;
  457. text-align: center;
  458. .gift-open-btn {
  459. position: relative;
  460. width: 120rpx;
  461. height: 120rpx;
  462. border-radius: 50%;
  463. background: linear-gradient(135deg, #FF4B4B, #E3441A);
  464. margin: 40rpx auto;
  465. display: flex;
  466. align-items: center;
  467. justify-content: center;
  468. cursor: pointer;
  469. overflow: hidden;
  470. box-shadow: 0 6rpx 20rpx rgba(227, 68, 26, 0.3);
  471. text {
  472. color: #fff;
  473. font-size: 36rpx;
  474. font-weight: bold;
  475. z-index: 1;
  476. }
  477. .btn-sparkle {
  478. position: absolute;
  479. top: -50%;
  480. left: -50%;
  481. width: 200%;
  482. height: 200%;
  483. background: linear-gradient(90deg,
  484. rgba(255,255,255,0) 0%,
  485. rgba(255,255,255,0.2) 50%,
  486. rgba(255,255,255,0) 100%);
  487. transform: rotate(45deg);
  488. animation: sparkle 2s infinite;
  489. }
  490. &.pulse {
  491. animation: pulse 2s infinite;
  492. }
  493. }
  494. .gift-title{
  495. color: #fff;
  496. font-size: 32rpx;
  497. font-weight: bold;
  498. margin-bottom: 20rpx;
  499. }
  500. .gift-title, .gift-tip {
  501. transition: all 0.5s ease;
  502. &.fade {
  503. opacity: 0;
  504. transform: translateY(-20rpx);
  505. }
  506. }
  507. .gift-icon {
  508. &.bounce {
  509. animation: giftBounce 2s infinite;
  510. }
  511. }
  512. }
  513. &.open {
  514. transform: perspective(1000px) rotateY(720deg) scale(0.5);
  515. opacity: 0;
  516. .gift-icon {
  517. transform: translateX(-50%) translateY(-200%) scale(1.5) rotate(720deg);
  518. opacity: 0;
  519. }
  520. .gift-content {
  521. transform: scale(0) rotate(-720deg);
  522. opacity: 0;
  523. }
  524. }
  525. }
  526. @keyframes sparkle {
  527. 0% {
  528. transform: rotate(45deg) translateX(-100%);
  529. }
  530. 50% {
  531. transform: rotate(45deg) translateX(100%);
  532. }
  533. 100% {
  534. transform: rotate(45deg) translateX(100%);
  535. }
  536. }
  537. @keyframes pulse {
  538. 0% {
  539. transform: scale(1);
  540. }
  541. 50% {
  542. transform: scale(1.05);
  543. }
  544. 100% {
  545. transform: scale(1);
  546. }
  547. }
  548. // 修改原有的giftBounce动画
  549. @keyframes giftBounce {
  550. 0%, 100% {
  551. transform: translateX(-50%) translateY(0) rotate(0deg);
  552. }
  553. 25% {
  554. transform: translateX(-50%) translateY(-15rpx) rotate(-5deg);
  555. }
  556. 75% {
  557. transform: translateX(-50%) translateY(-15rpx) rotate(5deg);
  558. }
  559. }
  560. // 抽奖动画
  561. .lottery-card {
  562. width: 600rpx;
  563. background: linear-gradient(135deg, #FF6B6B, #E3441A);
  564. border-radius: 20rpx;
  565. padding: 40rpx;
  566. text-align: center;
  567. .lottery-title {
  568. color: #fff;
  569. font-size: 32rpx;
  570. font-weight: bold;
  571. margin-bottom: 20rpx;
  572. }
  573. .gift-tip{
  574. color: #fff;
  575. font-size: 28rpx;
  576. margin-bottom: 40rpx;
  577. }
  578. .lottery-content {
  579. background: #fff;
  580. border-radius: 16rpx;
  581. padding: 30rpx;
  582. margin: 0 auto 30rpx;
  583. .product-image {
  584. width: 300rpx;
  585. height: 300rpx;
  586. margin: 0 auto 20rpx;
  587. border-radius: 8rpx;
  588. overflow: hidden;
  589. image {
  590. width: 100%;
  591. height: 100%;
  592. }
  593. }
  594. .product-info {
  595. text-align: center;
  596. .product-name {
  597. font-size: 28rpx;
  598. color: #333;
  599. margin-bottom: 10rpx;
  600. }
  601. .product-price {
  602. font-size: 36rpx;
  603. color: #E3441A;
  604. font-weight: bold;
  605. }
  606. }
  607. }
  608. .lottery-btn {
  609. margin-top: 40rpx;
  610. background: #FFD700;
  611. color: #E3441A;
  612. padding: 20rpx 60rpx;
  613. border-radius: 40rpx;
  614. display: inline-block;
  615. font-weight: bold;
  616. box-shadow: 0 6rpx 20rpx rgba(255, 215, 0, 0.3);
  617. }
  618. .lottery-result {
  619. margin-top: 30rpx;
  620. color: #fff;
  621. font-size: 32rpx;
  622. font-weight: bold;
  623. animation: fadeIn 0.5s ease;
  624. }
  625. }
  626. </style>