效果:数据未回来就是加载中,加载数据完成后就会显示没有更多的数据了
步骤一,定义无限下拉的商品组件:xtx-infinite-loading.vue
使用了vueuse中的useIntersectionObserve来监听是否达到了可视化界面中,如果到达了界面,就像父组件传自定义属性load通知父组件做接下来的操作
<template>
<div class="xtx-infinite-loading" ref="container">
<div class="loading" v-if="loading">
<span class="img"></span>
<span class="text">正在加载...</span>
</div>
<div class="none" v-if="finished">
<span class="img"></span>
<span class="text">亲,没有更多了</span>
</div>
</div>
</template>
<script>
import { ref } from 'vue'
import { useIntersectionObserver } from '@vueuse/core'
export default {
name: 'XtxInfiniteLoading',
props: {
// loading判断上一页是否加载完成
loading: {
type: Boolean,
default: false
},
// 整个列表是否还有更多数据可以加载
finished: {
type: Boolean,
default: false
}
},
setup(props, { emit }) {
const container = ref(null)
useIntersectionObserver(
// 被监听的dom元素
container,
([{ isIntersecting }], dom) => {
// isIntersecting为true的时候进入可视区
if (isIntersecting) {
// 发送请求获取下一页的数据
// 调用接口的条件,上一页的数据已经加载完成,并且列表还有更多的数据可以加载
// 数据已经加载 finished = false是有更多的数据可以加载
if (props.loading === false && props.finished === false) {
emit('load')
}
}
},
{
threshold: 0
}
)
return { container }
}
}
</script>
<style scoped lang="less">
.xtx-infinite-loading {
.loading {
display: flex;
align-items: center;
justify-content: center;
height: 200px;
.img {
width: 50px;
height: 50px;
background: url(../../assets/load.gif) no-repeat center / contain;
}
.text {
color: #999;
font-size: 16px;
}
}
.none {
display: flex;
align-items: center;
justify-content: center;
height: 200px;
.img {
width: 200px;
height: 134px;
background: url(../../assets/none.png) no-repeat center / contain;
}
.text {
color: #999;
font-size: 16px;
}
}
}
</style>
步骤二:接口数据需要的参数中有pagesize,pape,都不是必须填的,但是categoryId是必填的,使用route.params.id获取地址栏的id值
父组件:(父组件给子组件传值,loading=true是正在加载中,false是获取到接口的数据;finished=true是所有的数据已经全部加载完毕,没有数据可以加载了,false是还有数据;onLoad子组件传过来的自定义事件)
<template>
<div class="sub-category">
<div class="container">
<!-- 面包屑 -->
<SubBread />
<!-- 筛选条件 -->
<SubFilter></SubFilter>
<div class="goods-list">
<!-- 排序条件 -->
<SubSort />
<ul>
<li v-for="item in list" :key="item.id">
<GoodsItem :goods="item"></GoodsItem>
</li>
</ul>
<!-- 控制加载更多 -->
<XtxInfiniteLoading :loading="loading" :finished="finished" @load="onLoad"></XtxInfiniteLoading>
</div>
<!-- 测试v-model -->
<!-- <Xtxcheckbox v-model="checked">{{ checked }}</Xtxcheckbox> -->
</div>
</div>
</template>
<script>
import { findSubCategoryGoods } from '@/api/category.js'
import SubBread from './components/sub-bread'
import SubFilter from './components/sub-filter.vue'
import SubSort from './components/sub-sort.vue'
import GoodsItem from './components/goods-item.vue'
import { reactive, ref } from 'vue'
import { useRoute } from 'vue-router'
// import Xtxcheckbox from '@/components/library/xtx-checkbox.vue'
export default {
name: 'SubCategory',
components: { SubBread, SubFilter, SubSort, GoodsItem },
setup() {
const checked = ref(true)
const route = useRoute()
// 获取筛选条件的数据
const filterParams = reactive({
categoryId: route.params.id,
pagesize: 10,
page: 1
})
// 商品列表
const list = ref([])
const total = ref(0)
const loading = ref(false)
const finished = ref(false)
const onLoad = () => {
// console.log('我露出了来了')
loading.value = true
findSubCategoryGoods(filterParams).then(res => {
// 开始调用接口的时候将loading为true ,结束为false
// list用push是因为里面用的数据每加载一次就push进去,里面有pagesize page 会重新获取数据
// 获取数据是累加操作
list.value.push(...res.result.items)
total.value = res.result.counts
loading.value = false
// 每调用一次接口将page+1
filterParams.page += 1
// 数据>= total的时候,finished为true
if (list.value.length >= total.value) {
finished.value = true
}
})
}
return { checked, onLoad, filterParams, list, total, loading, finished }
}
}
</script>
<style scoped lang="less">
.goods-list {
background: #fff;
padding: 0 25px;
margin-top: 25px;
ul {
display: flex;
flex-wrap: wrap;
padding: 0 5px;
li {
margin-right: 20px;
margin-bottom: 20px;
&:nth-child(5n) {
margin-right: 0;
}
}
}
}
</style>
GoodsItem组件:
<template>
<RouterLink to="/" class="category-goods">
<img :src="goods.picture" alt="" />
<p class="name ellipsis">{{ goods.name }}</p>
<p class="desc ellipsis">{{ goods.tag }}</p>
<p class="price">¥{{ goods.price }}</p>
</RouterLink>
</template>
<script>
export default {
name: 'CategoryGoods',
props: {
goods: {
type: Object,
default: () => {}
}
}
}
</script>
<style scoped lang="less">
.category-goods {
display: block;
width: 220px;
padding: 20px 30px;
text-align: center;
.hoverShadow();
img {
width: 160px;
height: 160px;
}
p {
padding-top: 10px;
}
.name {
font-size: 16px;
}
.desc {
color: #999;
height: 29px;
}
.price {
color: @priceColor;
font-size: 20px;
}
}
</style>