1.0.5 • Published 1 year ago

list-pull v1.0.5

Weekly downloads
-
License
MIT
Repository
-
Last release
1 year ago

list-pull

一个用于 H5 的 vue 列表组件,基于vant的pull和list组件可以下拉刷新和上拉加载。 最简单参数是apiList

安装

# yarn add list-pull
npm i list-pull

简单使用

<template>
  <list-pull ref="listPull" :apiList="apiListFormat" class="list-pull">
    <template #item="item">
      <!-- item可以是组件 或 节点,能拿到item数据 -->
      <div class="item" :key="item.id">{{ item.name }}</div>
    </template>
  </list-pull>
</template>

<script>
import ListPull from 'list-pull';

export default {
  // ...
  methods: {
    async apiListFormat(params) {
      const res = await apiXX(params);
      const { status, data } = res.data;
      const isSuccess = status === 1;
      return { isSuccess, data };
    },
  },
};
</script>

item 里面记得加key属性

效果如下:list_pull4

页面特定高度元素内刷新

页面中如果特定元素限定高度内刷新的话,css 设定高度。 数据为空的时候,需要设定位置

.list-pull {
  height: 300px;
  overflow-y: auto;
}
/deep/ .empty {
  position: absolute;
  top: 10%;/* 根据自身情况设置*/
}

效果如下:list_pull3

条件变化需要更新数据

有时新增或者条件变化,需要手动刷新,重新请求数据

this.$refs.listPull.refresh()

属性

apiList - 必传

返回Promise的函数,参数主要是pageNo,返回值必须固定是{isSuccess:Boolean,data:[]},data 就是列表数据,数组形式

pageSize

分页数。默认 20

disabled

是否禁止刷新。默认是false

isDisabledPushSet

是否禁止下拉加载。默认是false

其他参数

  • textEnd,数据请求完的文本,默认是到底了~
  • successText,刷新成功的文本,默认是刷新成功
  • textEmpty,空状态的文本,默认是暂无数据~
  • imgEmpty,空状态的图像,默认是x_empty

组件逻辑

加载一定要分类

加载有三种情况:

  • 初始加载
  • 顶部下拉刷新加载
  • 底部上拉加载

这三种加载均需要分情况

组件代码和组件逻辑注释

<template>
  <van-pull-refresh
    v-model="isTopPullLoading"
    :success-text="textRefreshSuccess"
    @refresh="onTopPullRefresh"
    :disabled="isDisabledRefresh"
  >
    <template v-if="list.length">
      <van-list
        class="list"
        immediate-check
        v-model="isBottomPushLoading"
        :finished="isEnd"
        finished-text=""
        @load="onBottomPushLoad"
      >
        <template v-for="item in list">
          <slot name="item" v-bind="item"></slot>
        </template>
      </van-list>
      <div class="end-tip" v-if="isEnd">{{ textEnd }}</div>
    </template>
    <div class="empty" v-if="isShowEmptyStatus">
      <img class="img" :src="imgEmpty" alt="" />
      <div class="text">{{ textEmpty }}</div>
    </div>
  </van-pull-refresh>
</template>
<script>
import { PullRefresh as vanPullRefresh, List as vanList, Toast } from 'vant';
import 'vant/lib/pull-refresh/style';
import 'vant/lib/list/style';
const configInit = {
  isEnd: false,
  isInitLoading: false,
  pageNo: 1,
  isBottomPushLoading: false,
};

export default {
  name: 'list-pull',
  props: {
    apiList: Function,
    pageSize: {
      default: 10,
    },
    isDisabledRefreshSet: {
      default: false,
    },
    isDisabledPushSet: {
      default: false,
    },
    textEmpty: {
      default: '暂无数据~',
    },
    imgEmpty: {
      default:
        'https://blog-huahua.oss-cn-beijing.aliyuncs.com/blog/code/empty2.png',
    },
    textEnd: {
      default: '已经到底了~',
    },
    textRefreshSuccess: {
      default: '刷新成功',
    },
  },
  components: {
    vanPullRefresh,
    vanList,
  },
  data() {
    return {
      isAlreadyRequested: false,
      list: [],
      ...configInit,
      // 这个不放在configInit 因为下拉的时候 isTopPullLoading是true,此时请求结果还没回来,不能设置为false
      isTopPullLoading: false,
    };
  },

  mounted() {
    this.requestInit();
  },
  computed: {
    /**
     * 显示空状态同时满足两个条件
     *  1. 请求过了
     *  2. 空数据
     */
    isShowEmptyStatus() {
      return this.isAlreadyRequested && this.list.length === 0;
    },
    /**
     * 不能下拉刷新,任意满足一个条件
     *  - 设置了isDisabledRefreshSet
     *  - 显示空状态了
     */
    isDisabledRefresh() {
      return this.isDisabledRefreshSet || this.isShowEmptyStatus;
    },
  },

  methods: {
    /** 初始请求,需要显示加载图标,且请求完成之后,设置请求过的flag,以控制空状态的显示和是否能刷新 */
    async requestInit() {
      const toast = Toast.loading({
        message: '加载中...',
        forbidClick: true,
      });
      this.list = await this._apiListRequest();
      toast.clear();
      this.isAlreadyRequested = true;
    },

    /**
     * 顶部下拉刷新
     * 如果禁止下拉的话,isTopPullLoading始终为false,不会触发此函数
     * 下拉的时候,isTopPullLoading自动触发为true,触发onTopPullRefresh
     * 下拉主要是刷新,重置页面部分配置,然后请求,请求回来之后,手动设置isTopPullLoading,结束下拉加载状态
     * */
    async onTopPullRefresh() {
      this.updateData();
    },
    async updateData() {
      this._resetConfig();
      this.list = await this._apiListRequest();
      this.isTopPullLoading = false;
    },

    /**
     * 底部上拉加载
     * 上拉的时候,isBottomPushLoading自动触发为true,触发onBottomPushLoad
     * 不加载但可能触发上拉的情形:父组件不需要上拉加载、数据完结了、正在加载、正在刷新
     * 触发下拉加载
     *  1. 如果不能加载,直接isBottomPushLoading为false,结束上拉加载状态
     *  2. 如果能加载,页数加一再请求,请求成功之后,数据追加,设置isBottomPushLoading为false,结束上拉加载状态
     */
    async onBottomPushLoad() {
      const isDisabledPush =
        this.isDisabledPush ||
        this.isEnd ||
        this.isInitLoading ||
        this.isTopPullLoading;
      if (isDisabledPush) {
        this.isBottomPushLoading = false;
        return;
      }
      this.pageNo += 1;
      const newList = await this._apiListRequest();
      this.list = [...this.list, ...newList];
      this.isBottomPushLoading = false;
    },

    /**
     * 三种情况发生请求:初始请求、下拉刷新请求、上拉加载请求
     * 但三种请求前后的逻辑并不相同,这边抽离共同逻辑,只返回list数据,其他逻辑各自处理
     * 请求的参数,主要是pageNo,如果不成功,返回[],成功的话,list的数据少于pageSize,表示到底,返回data
     */
    async _apiListRequest() {
      const res = await this.apiList({
        pageNo: this.pageNo,
      });
      const { data, isSuccess } = res;
      if (!isSuccess) {
        return [];
      }
      this.isEnd = data.length < this.pageSize;
      return data;
    },
    /** 下拉刷新的时候,页面部分配置初始化 */
    async _resetConfig() {
      Object.keys(configInit).forEach((key) => {
        this[key] = configInit[key];
      });
    },
  },
};
</script>
<style scoped>
.end-tip {
  margin-top: 20px;
  padding-bottom: 20px;
  text-align: center;
  font-size: 12px;
  font-weight: 400;
  color: #bbbbbb;
  line-height: 17px;
}
.van-pull-refresh {
  /* 有一定的高度 可以滚动 */
  height: auto;
  overflow: auto;
  /* min-height: 100vh; */
  position: relative;
}
.empty {
  /* position: absolute; */
  left: 0;
  right: 0;
  top: 40%;
  text-align: center;
  z-index: 3;
  /* transform: translate(0, -40%); */
  margin-top: 30px;
  justify-content: center;
  display: flex;
  flex-direction: column;
  align-items: center;
}
.img {
  display: block;
  width: 110px;
}
.text {
  font-size: 14px;
  font-weight: 300;
  color: #999999;
  line-height: 20px;
  margin-top: 10px;
}
</style>
1.0.5

1 year ago

1.0.4

1 year ago

1.0.3

1 year ago

1.0.2

1 year ago

1.0.1

1 year ago

1.0.0

1 year ago