效果预览
组件封装
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()
}