Q3学习计划
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.

223 lines
5.0 KiB

  1. <template>
  2. <view class="viewport">
  3. <!-- 推荐封面图 -->
  4. <view class="cover">
  5. <image class="image" mode="widthFix" :src="bannerPicture"></image>
  6. </view>
  7. <!-- 推荐选项 -->
  8. <view class="tabs">
  9. <text
  10. v-for="(item, index) in subTypes"
  11. :key="item.id"
  12. class="text"
  13. :class="{ active: index === activeIndex }"
  14. @tap="activeIndex = index"
  15. >{{ item.title }}</text
  16. >
  17. </view>
  18. <!-- 推荐列表 -->
  19. <scroll-view
  20. v-for="(item, index) in subTypes"
  21. :key="item.id"
  22. v-show="activeIndex === index"
  23. @scrolltolower="onScrolltolower"
  24. scroll-y
  25. class="scroll-view"
  26. >
  27. <view class="goods">
  28. <navigator
  29. hover-class="none"
  30. class="navigator"
  31. v-for="goods in item.goodsItems.items"
  32. :key="goods.id"
  33. :url="`/pages/goods/goods?id=`"
  34. >
  35. <image class="thumb" :src="goods.picture"></image>
  36. <view class="price">
  37. <text class="symbol">¥</text>
  38. <text class="number">{{ goods.price }}</text>
  39. </view>
  40. </navigator>
  41. </view>
  42. <view class="loading-text">
  43. {{ item.finish ? '没有更多数据了~' : '正在加载...' }}
  44. </view>
  45. </scroll-view>
  46. </view>
  47. </template>
  48. <script setup lang="ts">
  49. import { getHotRecommendAPI } from '@/services/hot'
  50. import { onLoad } from '@dcloudio/uni-app'
  51. import { ref } from 'vue'
  52. import type { SubTypeItem } from '@/types/hot'
  53. // 热门推荐页 标题和url
  54. const hotMap = [
  55. { type: '1', title: '特惠推荐', url: '/hot/preference' },
  56. { type: '2', title: '爆款推荐', url: '/hot/inVogue' },
  57. { type: '3', title: '一站买全', url: '/hot/oneStop' },
  58. { type: '4', title: '新鲜好物', url: '/hot/new' },
  59. ]
  60. // uniapp 获取页面参数
  61. const query = defineProps<{
  62. type: string
  63. }>()
  64. // console.log(query)
  65. const currUrlMap = hotMap.find((v) => v.type === query.type)
  66. // 动态设置标题
  67. uni.setNavigationBarTitle({ title: currUrlMap!.title })
  68. // 推荐封面图
  69. const bannerPicture = ref('')
  70. // 推荐选项
  71. const subTypes = ref<(SubTypeItem & { finish?: boolean })[]>([])
  72. // 高亮的下标
  73. const activeIndex = ref(0)
  74. // 获取热门推荐数据
  75. const getHotRecommendData = async () => {
  76. const res = await getHotRecommendAPI(currUrlMap!.url, {
  77. // 技巧:环境变量,开发环境,修改初始页面方便测试分页结束
  78. page: import.meta.env.DEV ? 30 : 1,
  79. pageSize: 10,
  80. })
  81. bannerPicture.value = res.result.bannerPicture
  82. subTypes.value = res.result.subTypes
  83. }
  84. // 页面加载
  85. onLoad(() => {
  86. getHotRecommendData()
  87. })
  88. // 滚动触底
  89. const onScrolltolower = async () => {
  90. // 获取当前选项
  91. const currsubTypes = subTypes.value[activeIndex.value]
  92. // 分页条件
  93. if (currsubTypes.goodsItems.page < currsubTypes.goodsItems.pages) {
  94. // 当前页码累加
  95. currsubTypes.goodsItems.page++
  96. } else {
  97. // 标记已结束
  98. currsubTypes.finish = true
  99. // 退出并轻提示
  100. return uni.showToast({ icon: 'none', title: '没有更多数据了~' })
  101. }
  102. // 调用API传参
  103. const res = await getHotRecommendAPI(currUrlMap!.url, {
  104. subType: currsubTypes.id,
  105. page: currsubTypes.goodsItems.page,
  106. pageSize: currsubTypes.goodsItems.pageSize,
  107. })
  108. // 新的列表选项
  109. const newsubTypes = res.result.subTypes[activeIndex.value]
  110. // 数组追加
  111. currsubTypes.goodsItems.items.push(...newsubTypes.goodsItems.items)
  112. }
  113. </script>
  114. <style lang="scss">
  115. page {
  116. height: 100%;
  117. background-color: #f4f4f4;
  118. }
  119. .viewport {
  120. display: flex;
  121. flex-direction: column;
  122. height: 100%;
  123. padding: 180rpx 0 0;
  124. position: relative;
  125. }
  126. .cover {
  127. width: 750rpx;
  128. height: 225rpx;
  129. border-radius: 0 0 40rpx 40rpx;
  130. overflow: hidden;
  131. position: absolute;
  132. left: 0;
  133. top: 0;
  134. .image {
  135. width: 750rpx;
  136. }
  137. }
  138. .scroll-view {
  139. flex: 1;
  140. }
  141. .tabs {
  142. display: flex;
  143. justify-content: space-evenly;
  144. height: 100rpx;
  145. line-height: 90rpx;
  146. margin: 0 20rpx;
  147. font-size: 28rpx;
  148. border-radius: 10rpx;
  149. box-shadow: 0 4rpx 5rpx rgba(200, 200, 200, 0.3);
  150. color: #333;
  151. background-color: #fff;
  152. position: relative;
  153. z-index: 9;
  154. .text {
  155. margin: 0 20rpx;
  156. position: relative;
  157. }
  158. .active {
  159. &::after {
  160. content: '';
  161. width: 40rpx;
  162. height: 4rpx;
  163. transform: translate(-50%);
  164. background-color: #27ba9b;
  165. position: absolute;
  166. left: 50%;
  167. bottom: 24rpx;
  168. }
  169. }
  170. }
  171. .goods {
  172. display: flex;
  173. flex-wrap: wrap;
  174. justify-content: space-between;
  175. padding: 0 20rpx 20rpx;
  176. .navigator {
  177. width: 345rpx;
  178. padding: 20rpx;
  179. margin-top: 20rpx;
  180. border-radius: 10rpx;
  181. background-color: #fff;
  182. }
  183. .thumb {
  184. width: 305rpx;
  185. height: 305rpx;
  186. }
  187. .name {
  188. height: 88rpx;
  189. font-size: 26rpx;
  190. }
  191. .price {
  192. line-height: 1;
  193. color: #cf4444;
  194. font-size: 30rpx;
  195. }
  196. .symbol {
  197. font-size: 70%;
  198. }
  199. .decimal {
  200. font-size: 70%;
  201. }
  202. }
  203. .loading-text {
  204. text-align: center;
  205. font-size: 28rpx;
  206. color: #666;
  207. padding: 20rpx 0 50rpx;
  208. }
  209. </style>