小程序 列表组件 可上拉刷新 下拉加载

组件描述

这是一个支持下拉刷新和上拉加载更多的列表组件。它提供了空数据状态显示、加载动画、以及到达底部提示等功能,适用于需要展示分页数据的场景。

属性说明

属性名类型默认值说明
emptyTextString“暂无相关数据”空数据状态时的提示文本
refreshTextString“正在刷新…”下拉刷新时的提示文本
loadingTextString“加载中…”加载更多数据时的提示文本
hasMoreBooleantrue是否还有更多数据可加载
scrollHeightStringcalc(100vh - 150px)列表容器的高度
listArray[]需要展示的数据列表

插槽(Slot)

  • empty: 自定义空数据状态视图
  • listContent: 列表内容区域,通过:list="list"传递当前列表数据

方法(Methods)

handleRefresh()

处理下拉刷新逻辑,重置页码并调用父组件的refresh方法。

handleLoadMore()

处理上拉加载更多逻辑,递增页码并调用父组件的loadmore方法。

handleAction(butItem, key, item)

触发列表项操作,更新对应项的状态。

样式说明

  • .container: 主容器样式
  • .card-list: 列表样式
  • .refresh-indicator: 下拉刷新指示器样式
  • .loading-more: 加载更多提示样式
  • .empty-container: 空数据状态容器样式

使用示例

<template>
  <list-refresh-load :emptyText="'没有找到匹配的结果'" :hasMore="hasMoreData" :list="dataList">
    <template v-slot:listContent="{ list }">
      <!-- 自定义列表内容 -->
      <view v-for="(item, index) in list" :key="index">{{ item.name }}</view>
    </template>
  </list-refresh-load>
</template>

<script>
export default {
  data() {
    return {
      dataList: [],
      hasMoreData: true,
    }
  },
  methods: {
    // 刷新数据的方法
    onRefresh(resolve, reject, page) {
      // 模拟网络请求
      setTimeout(() => {
        this.dataList = fetchData(page); // 假设fetchData是一个获取数据的函数
        resolve();
      }, 1000);
    },
    // 加载更多数据的方法
    onLoadMore(resolve, reject, page) {
      // 模拟网络请求
      setTimeout(() => {
        const newData = fetchData(page);
        if (newData.length > 0) {
          this.dataList = [...this.dataList, ...newData];
        } else {
          this.hasMoreData = false;
        }
        resolve();
      }, 1000);
    }
  }
}
</script>

代码部分

<template>
  <view class="container">
    <scroll-view scroll-y :style="{height: scrollHeight}" :refresher-enabled="true" :refresher-triggered="isRefreshing"
      @refresherrefresh="handleRefresh" @scrolltolower="handleLoadMore">
      <!-- 下拉刷新动画 -->
      <view v-if="isRefreshing" class="refresh-indicator" :class="{ refreshing: isRefreshing }">
        <text>{{ refreshText }}</text>
      </view>
      <view class="card-list">
        <!-- 空数据状态 -->
        <view v-if="showEmpty" class="empty-container">
          <slot name="empty">
            <view class="default-empty">
              <!-- <image src="/static/empty.png" class="empty-image" /> -->
              <text class="empty-text">{{ emptyText }}</text>
            </view>
          </slot>
        </view>
        <!-- 列表插槽 -->
        <slot name="listContent" :list="list"></slot>
        <!-- 加载更多提示 -->
        <view class="loading-more">
          <view v-if="loading" class="loading-spinner">
            <text class="loading-text">{{ loadingText }}</text>
          </view>
          <text v-else-if="!hasMore" class="no-more">—— 已经到底啦 ——</text>
        </view>
      </view>
    </scroll-view>
  </view>
</template>

<script>
  export default {
    name: 'infiniteScrollList',
    components: {},
    props: {
      emptyText: {
        type: String,
        default: '暂无相关数据'
      },
      refreshText: {
        type: String,
        default: '正在刷新...'
      },
      loadingText: {
        type: String,
        default: '加载中...'
      },
      // 是否还有更多
      hasMore: {
        type: Boolean,
        default: true
      },

      // 列表高度
      scrollHeight: {
        type: String,
        default: () => {
          return `calc(100vh - 150px)`
        }
      },
      // 列表
      list: {
        type: Array,
        default: () => []
      },
    },
    computed: {
      // 计算是否显示空状态
      showEmpty() {
        return !this.loading && !this.isRefreshing && this.list.length === 0
      }
    },
    data() {
      return {
        startX: 0,
        currentIndex: -1,
        isRefreshing: false, // 刷新状态
        loading: false, // 加载状态
        currentPage: 1, // 页码跟踪
        showHint: false
      }
    },
    watch: {},
    mounted() {},
    methods: {
      handleAction(butItem, key, item) {
        this.$set(item, 'offsetX', 0)
        this.$emit('handleAction', butItem, key, item)
      },
      // 优化后的刷新处理
      async handleRefresh() {
        if (this.isRefreshing) return
        this.isRefreshing = true
        try {
          this.currentPage = 1 // 重置页码
          // 等待父组件返回的 Promise
          await new Promise((resolve, reject) => {
            this.$emit('refresh', resolve, reject, this.currentPage)
          })
        } catch (error) {
          console.error('刷新失败:', error)
        } finally {
          this.isRefreshing = false
        }
      },
      // 优化后的加载处理
      async handleLoadMore() {
        if (this.loading || !this.hasMore) return
        this.loading = true
        try {
          this.currentPage += 1 // 递增页码
          // 等待父组件返回的 Promise
          await new Promise((resolve, reject) => {
            this.$emit('loadmore', resolve, reject, this.currentPage)
          })
        } catch (error) {
          console.error('加载失败:', error)
        } finally {
          this.loading = false
        }
      },
    }
  }
</script>

<style lang="scss" scoped>
  .container {
    flex: 1;
    background: #f5f5f5;
  }

  .card-list {
    padding: 12rpx;
  }

  .refresh-indicator {
    height: 80rpx;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 28rpx;
    color: #666;
  }

  .loading-more {
    padding: 30rpx 0;
    text-align: center;
    font-size: 28rpx;
    color: #666;
  }


  .empty-container {
    padding: 60rpx 30rpx;
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 400rpx;

    .default-empty {
      display: flex;
      flex-direction: column;
      align-items: center;

      .empty-image {
        width: 240rpx;
        height: 240rpx;
        margin-bottom: 30rpx;
        opacity: 0.6;
      }

      .empty-text {
        color: #999;
        font-size: 28rpx;
        text-align: center;
      }
    }
  }
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值