珠宝小程序前端代码
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

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
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. <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>