效果预览
组件封装
src\components\SUI_Guess.vue
<script setup lang="ts"> import { ref, onMounted } from 'vue' import type { GuessItem } from '@/types/index' import { getGuessListAPI } from '@/apis/index' import type { PageParams } from '@/types/global' // 分页参数 -- Required指定分页参数必传 const pageParams: Required<PageParams> = { page: 1, pageSize: 10, } // 已结束标记 const finish = ref(false) // 猜你喜欢的数据列表 const GuessList = ref<GuessItem[]>([]) // 获取猜你喜欢的数据列表 const getGuessList = async () => { // 已标记为无更多数据时,不再查询 if (finish.value === true) { return uni.showToast({ icon: 'none', title: '没有更多数据~' }) } // 查询分页数据 let res = await getGuessListAPI(pageParams) // 新数据不断累加--数组追加 GuessList.value.push(...res.result.items) // 未到最后一页时,页码不断累加 if (pageParams.page < res.result.pages) { // 页码累加 pageParams.page++ } else { // 到达最后一页时,标记为无更多数据 finish.value = true } } // 生命周期-组件挂载成功时执行 onMounted(() => { getGuessList() }) // 对外暴露方法 -- 供父组件调用 defineExpose({ getGuessList: getGuessList, }) </script> <template> <!-- 猜你喜欢 --> <view class="caption"> <text class="text">猜你喜欢</text> </view> <view class="guess"> <navigator class="guess-item" v-for="(item, index) in GuessList" :key="'guess' + index" :url="item.url" > <image class="image" mode="aspectFill" :src="item.picture"></image> <view class="name"> {{ item.name }} </view> <view class="price"> <text class="small">¥</text> <text>{{ item.price }}</text> </view> </navigator> </view> <view class="loading-text"> {{ finish ? '没有更多数据~' : '正在加载...' }} </view> </template> <style lang="scss"> :host { display: block; } /* 分类标题 */ .caption { display: flex; justify-content: center; line-height: 1; padding: 36rpx 0 40rpx; font-size: 32rpx; color: #262626; .text { display: flex; justify-content: center; align-items: center; padding: 0 28rpx 0 30rpx; &::before, &::after { content: ''; width: 20rpx; height: 20rpx; background-image: url(@/static/images/bubble.png); background-size: contain; margin: 0 10rpx; } } } /* 猜你喜欢 */ .guess { display: flex; flex-wrap: wrap; justify-content: space-between; padding: 0 20rpx; .guess-item { width: 345rpx; padding: 24rpx 20rpx 20rpx; margin-bottom: 20rpx; border-radius: 10rpx; overflow: hidden; background-color: #fff; } .image { width: 304rpx; height: 304rpx; } .name { height: 75rpx; margin: 10rpx 0; font-size: 26rpx; color: #262626; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; } .price { line-height: 1; padding-top: 4rpx; color: #cf4444; font-size: 26rpx; } .small { font-size: 80%; } } // 加载提示文字 .loading-text { text-align: center; font-size: 28rpx; color: #666; padding: 20rpx 0; } </style>
注册为全局组件
src\pages.json
必要的 TS 类型声明
组件和组件实例类型声明
src\types\component.d.ts
import 'vue' import SUI_Swiper from '@/components/SUI_Swiper.vue' import SUI_Guess from '@/components/SUI_Guess.vue' // 组件类型 declare module 'vue' { export interface GlobalComponents { SUI_Guess: typeof SUI_Guess } } // 组件实例类型 export type SUI_GuessInstance = InstanceType<typeof SUI_Guess>
接口参数和返回值类型声明
src\types\global.d.ts
/** 通用分页参数类型 */ export type PageParams = { /** 页码:默认值为 1 */ page?: number /** 页大小:默认值为 10 */ pageSize?: number } /** 通用分页结果类型 */ export type PageResult<T> = { /** 列表数据 */ items: T[] /** 总条数 */ counts: number /** 当前页数 */ page: number /** 总页数 */ pages: number /** 每页条数 */ pageSize: number }
业务数据类型声明
src\types\index.d.ts
/** 猜你喜欢 */ export type GuessItem = { /** 商品描述 */ desc: string /** 商品折扣 */ discount: number /** id */ id: string /** 商品名称 */ name: string /** 商品已下单数量 */ orderNum: number /** 商品图片 */ picture: string /** 商品价格 */ price: number // 导航地址 url: string }
接口封装
src\apis\index.ts
import { http } from '@/utils/http' import type { GuessItem } from '@/types/index' import type { PageParams, PageResult } from '@/types/global' /** * 公共组件-猜你喜欢 */ export const getGuessListAPI = (data?: PageParams) => { return http<PageResult<GuessItem>>({ method: 'GET', url: '/home/goods/guessLike', data, }) }
页面使用
src\pages\index\index.vue
<!-- 中间--自适配高度的滚动区 --> <scroll-view class="contentBox" scroll-y @scrolltolower="onScrolltolower"> <SUI_Guess ref="guessRef" /> </scroll-view>
<style lang="scss"> page { background-color: #f7f7f7; // 总容器高度撑满屏幕 height: 100%; // 使容器内元素使用flex布局 display: flex; flex-direction: column; } .contentBox { // 滚动区自适配高度 flex: 1; } </style>
import type { SUI_GuessInstance } from '@/types/component' // 获取猜你喜欢组件实例 const guessRef = ref<SUI_GuessInstance>() // 滚动触底事件 const onScrolltolower = () => { guessRef.value?.getGuessList() }