Vue3实现下拉刷新,上滑加载列表,列表内容自定义

组件代码如下:

<template>
  <div class="pullDown" ref="pullDown">
    <div class="pullDownTop"><span v-show="showRefresh">刷新中...</span></div>
    <div>
      <slot></slot>
    </div>
  </div>
</template>
<script setup lang="ts">

import {
    
    ref, onMounted, watch, onUnmounted} from "vue";

const props = defineProps({
    
    
  loading: Boolean,//异步加载数据后标识是否加载完成
  noMore: Boolean//标识是否还有更多数据需要加载
})
//refresh事件标识刷新列表
//loadMore事件标识加载更多数据内容
const emits = defineEmits(['refresh', 'loadMore'])
onMounted(() => {
    
    
  getPullDown().ontouchstart = FnStart;//初始化下拉刷新
  initScroll()//初始化上滑加载
});
onUnmounted(() => {
    
    
//退出时销毁上滑加载监听的滚动事件
  window.removeEventListener('scroll', scrollListener)
})
//加载结束,隐藏UI
watch(() => props.loading, () => {
    
    
  setTimeout(() => {
    
    
    showRefresh.value = false
    y.value = 0;
    getChild(0).style.height = y.value + "px";
  }, 1000);
})
//没有更多需要加载了
watch(() => props.noMore, () => {
    
    
  if (props.noMore) {
    
    
  //销毁上滑加载监听的滚动事件
    window.removeEventListener('scroll', scrollListener)
  }
})
//标识加载提示是否显示
const showRefresh = ref(false);
//ref容器
const pullDown = ref(null);
let disY = ref<number>(0);
let y = ref<number>(0);
const getPullDown = () => {
    
    
  return pullDown.value as unknown as HTMLDivElement;
};
const getChild = (n: number) => {
    
    
  return getPullDown().children[n] as HTMLDivElement;
};
const FnStart = function (ev: TouchEvent) {
    
    
//监听滑动开始点
  getChild(0).style.transition = `null`
  disY.value = ev.changedTouches[0].pageY - y.value;
  getChild(1).ontouchmove = FnMove;
  getChild(1).ontouchend = FnEnd;
};
const FnMove = function (ev: TouchEvent) {
    
    
//监听滑动中,动态设置高度,实现下拉动效
  y.value = ev.changedTouches[0].pageY - disY.value;
  getChild(0).style.height = y.value / 3 + "px";
};
const FnEnd = function (ev: TouchEvent) {
    
    
//监听滑动结束点,判断是否需要触发刷新事件
  getChild(0).style.transition = `0.4s ease height`
  console.log(y.value);
  //下拉距离超过80则刷新
  if (y.value > 80) {
    
    
    //下拉
    showRefresh.value = true
    y.value = 80;
    console.log('刷新列表...')
    emits('refresh')
  } else {
    
    
    //下拉不足80或者是上滑,不触发刷新,并且隐藏提示
    showRefresh.value = false
    y.value = 0;
  }
  getChild(0).style.height = y.value + "px";
  getChild(1).ontouchmove = () => false;
  getChild(1).ontouchend = () => false;
}


const loadMore = () => {
    
    
  if (!props.noMore) {
    
    
    console.log('加载更多...')
    //触发加载更多事件
    emits('loadMore')
  }
}
//防抖函数,默认100ms
const debounce = (() => {
    
    
  let timer = 0
  return (callback: any, ms: any = 100) => {
    
    
    clearTimeout(timer)
    timer = setTimeout(callback, ms)
  }
})()
const container = ref(null)
const initScroll = () => {
    
    
//初始化滑动事件监听器
  window.addEventListener('scroll', scrollListener)
}
const scrollListener = () => {
    
    
  // screen.availHeight表示屏幕高度
  // document.documentElement.scrollTop表示当前页面滚动条的位置,documentElement对应的是html标签,body对应的是body标签
  // document.compatMode用来判断当前浏览器采用的渲染方式,CSS1Compat表示标准兼容模式开启
  const scrollY = document.documentElement.scrollTop || document.body.scrollTop // 滚动条在Y轴上的滚动距离
  const vh = document.compatMode === 'CSS1Compat' ? document.documentElement.clientHeight : document.body.clientHeight // 页面的可视高度(能看见的)
  const allHeight = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight) // 页面的总高度(所有的)
  // console.log('scrollY',scrollY + vh)
  // console.log('allHeight',allHeight - 300)
  if (scrollY + vh >= (allHeight - 300)) {
    
     // 当滚动条滑到页面底部
    debounce(loadMore)
  }
}
</script>
<style lang="scss" scoped>
.pullDown {
    
    
  .pullDownTop {
    
    
    width: 100vw;
    height: 0px;
    //background-color: skyblue;
    text-align: center;
  }

  .pullDownBottom {
    
    
    width: 100vw;
    height: 100vh;
    background-color: pink;
  }
}
</style>

使用示例:

ui部分:

    <common-list :loading="loading" :no-more="noMore" @refresh="initData" @loadMore="loadMore">
      <view class="contentBoxClass" ref="contentBox">
        <view class="card" v-for="(item, index) of dataList" :key="index">
        //unit-item是自定义的组件内容,用于根据需要实现不同的自定义内容
        //  <unit-item :unit-info="item" :index="index" @handleMaintenance="handleMaintenance"/>
        </view>
      </view>
    </common-list>

代码部分:

//第一次加载和加载更多不同,需要手动调用,初始化内容数组和数据总量
onMounted(() => {
    
    
  initData()
})


...

//分页参数,当前页码
const pageNum= ref(1)
//请求是否完成
const loading = ref(false)
//是否有更多数据需要加载
const noMore = ref(false)

//初始化函数,也可以用于刷新
const initData = async () => {
    
    
pageNum.value = 1
//根据需要传递给后台的参数
  let params = {
    
    
  ...
  }
  loading.value=true
  //请求后台接口
  let resList = await getXXXList(params)
  //后台反馈的数据放入变量中
  dataList.value = resList.content
  //设置数据总量
  total.value = resList.totalSize
  loading.value=false
}
watch(dataList, () => {
    
    
//判断是否有更多数据需要加载,如果接口没有返回数据,或者返回的数据大于等于总数则没有更多数据需要加载
  if (!dataList.value || total.value <= dataList.value.length) {
    
    
    noMore.value = true
  }
})
const loadMore = async () => {
    
    
	pageNum.value++
  let params = {
    
    
  ...
  }
  loading.value=true
  let resList = await getXXXList(params)
  //加载更多数据,和初始化数据不同,需要对数据进行追加,而不是直接覆盖
  dataList.value = dataList.value.concat(resList.ontent)
  total.value = resList.totalSize
  loading.value=false
}


猜你喜欢

转载自blog.csdn.net/geshi201028/article/details/125180990