index.vue 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. <script lang="ts" setup>
  2. import { computed, ref } from 'vue'
  3. import { useScroll } from '@/hooks/useScroll'
  4. import { useShare } from '@/hooks/useShare'
  5. import { safeAreaInsets } from '@/utils'
  6. import { getCouponByType, getCouponDetail } from '../../api/home'
  7. import DiscountCoupon from '../../components/DiscountCoupon.vue'
  8. definePage({
  9. style: {
  10. navigationBarTitleText: '',
  11. navigationStyle: 'custom',
  12. // 禁用默认下拉刷新
  13. enablePullDownRefresh: false,
  14. },
  15. })
  16. const params = ref()
  17. // 使用内置的useScroll hooks
  18. const {
  19. list: data, // 响应式的数据列表
  20. loading, // 是否加载中
  21. finished, // 是否已全部加载
  22. error, // 是否加载失败
  23. refresh: onRefresh, // 下拉刷新方法
  24. loadMore: onLoadMore, // 加载更多方法
  25. } = useScroll({
  26. fetchData: async (page, pageSize) => {
  27. const response = await getCouponByType({
  28. pageNo: page,
  29. pageSize,
  30. ...params.value,
  31. })
  32. return response.records || []
  33. },
  34. pageSize: 7,
  35. })
  36. // 计算底部安全区高度
  37. const safeBottomHeight = computed(() => {
  38. return safeAreaInsets?.bottom || 0
  39. })
  40. const topSafeAreaHeight = safeAreaInsets?.top || 0
  41. // 页面加载时获取数据
  42. onLoad((options) => {
  43. if (options.type) {
  44. params.value = options
  45. onRefresh()
  46. }
  47. })
  48. const { getShareConfig: getShareCouponConfig } = useShare({
  49. shareType: 'COUPON',
  50. imageSource: 'REMOTE',
  51. path: '/pages/receiveCoupon/index',
  52. pathParamKey: 'couponShareRecordId',
  53. })
  54. // #ifdef MP-WEIXIN
  55. // 分享功能实现
  56. // 分享生命周期函数
  57. onShareAppMessage(async (options) => {
  58. console.log(options)
  59. if (options.from === 'button' && options.target.dataset.shareType === 'coupon') {
  60. const couponId = options.target.dataset.couponId
  61. const couponinfo = await getCouponDetail({ templateId: couponId })
  62. return await getShareCouponConfig({
  63. imageUrl: couponinfo?.imageUrl,
  64. }, {
  65. shareContentId: couponId,
  66. })
  67. }
  68. return null
  69. })
  70. // #endif
  71. </script>
  72. <template>
  73. <view class="page-container">
  74. <up-navbar title="折扣券" :auto-back="true" />
  75. <up-status-bar />
  76. <scroll-view :style="{ paddingTop: `${topSafeAreaHeight}px` }" class="discount-coupon-list" scroll-y
  77. refresher-enabled :refresher-triggered="loading" @refresherrefresh="onRefresh" @scrolltolower="onLoadMore">
  78. <view class="list-content">
  79. <view v-for="item in data" :key="item.templateId" class="discount-coupon-item">
  80. <DiscountCoupon :coupon="item" />
  81. </view>
  82. <!-- 加载状态提示 -->
  83. <view v-if="loading && data.length > 0" class="loadmore-container">
  84. <up-loadmore status="loading" loading-text="努力加载中..." icon-size="18" />
  85. </view>
  86. <view v-else-if="finished && data.length > 0" class="loadmore-container">
  87. <up-loadmore status="nomore" nomore-text="我们是有底线的" icon-size="18" />
  88. </view>
  89. </view>
  90. </scroll-view>
  91. <!-- 安全区底部占位 -->
  92. <view class="safe-bottom" :style="{ height: `${safeBottomHeight}px` }" />
  93. </view>
  94. </template>
  95. <style lang="scss" scoped>
  96. .page-container {
  97. height: 100vh;
  98. display: flex;
  99. flex-direction: column;
  100. }
  101. .discount-coupon-list {
  102. flex: 1;
  103. overflow-y: auto;
  104. .list-content {
  105. padding-top: 20rpx;
  106. padding-left: 24rpx;
  107. padding-right: 24rpx;
  108. display: flex;
  109. flex-direction: column;
  110. gap: 20rpx;
  111. }
  112. .loadmore-container {
  113. padding: 30rpx 0;
  114. text-align: center;
  115. }
  116. }
  117. .safe-bottom {
  118. width: 100%;
  119. background-color: #fff;
  120. }
  121. </style>