vue3 通过hooks优雅封装列表组件

333 阅读1分钟

列表组建封装

<template>
  <van-pull-refresh
    ref="ListRefreshRef"
    v-model="data.freshData.refreshLoad"
    :class="classMap?.refreshClass"
    disabled
    @scroll="scrollHander"
    @refresh="onRefresh"
  >
    <van-list
      v-model:loading="data.freshData.listLoading"
      :finished="data?.freshData?.listFinished"
      :immediate-check="false"
      :finished-text="finishedText"
      :offset="20"
      class="h-[100%] scroll-container pt-[10px] px-[12px]"
      @load="onLoad"
    >
      <slot />
    </van-list>
  </van-pull-refresh>
</template>

<script setup name="ListRefresh">
const emit = defineEmits(['onRefresh', 'onLoad']);
const props = defineProps({
  freshMap: {
    type: Object,
    default: () => {
      return {
        refreshLoad: false,
        listLoading: false,
        listFinished: false,
        listData: []
      };
    }
  },
  classMap: {
    type: Object,
    default: () => {
      return {
        refreshClass: ''
      };
    }
  },
  resetScroll: {
    type: Number,
    default: 0
  },
  finishedText: {
    type: String,
    default: '没有更多了'
  },
  emptyText: {
    type: String,
    default: '暂无数据'
  }
});
const data = reactive({
  freshData: props?.freshMap
});

// 判断数据是否为空
const isEmpty = computed(() => {
  const { listFinished, listData } = data?.freshData ?? {};
  return listFinished && !listData?.length;
});

const ListRefreshRef = ref();

// 下拉刷新
const onRefresh = () => {
  emit('onRefresh');
};

// 触底加载
const onLoad = () => {
  emit('onLoad');
};

// 重置滚动距离
const onResetScroll = () => {
  ListRefreshRef.value.$el.scrollTop = 0;
};

// 滚动监听
const scrollHander = () => {};

watchEffect(() => {
  if (props?.resetScroll) {
    onResetScroll();
  }
});
</script>

<style lang="less" scoped></style>

hooks封装useListRefresh.ts方法

import { ref } from 'vue';

type PromiseFunc = (params?: any) => Promise<any>;
interface IPage {
  pageIndex: number;
  pageSize: number;
}

export const useListRefresh = <T>(): {
  pagination: IPage;
  listData: T[];
  loading: boolean;
  finished: boolean;
  refreshing: boolean;
  getListData: (func: PromiseFunc) => Promise<any>;
  refreshCallback: (func: PromiseFunc) => void;
  loadMore: (func: PromiseFunc) => void;
} => {
  const pagination = ref<IPage>({
    pageIndex: 1,
    pageSize: 10
  });
  const listData = ref([]);
  const loading = ref(false);
  const finished = ref(false);
  const refreshing = ref(false);
  const getListData = async (func: PromiseFunc) => {
    loading.value = true;
    const { data, pages } = await func();
    if (refreshing.value) {
      refreshing.value = false;
      listData.value = [];
    }
    const list = data ?? [];
    listData.value =
      pagination.value.pageIndex > 1 ? listData.value.concat(list) : list;
    loading.value = false;
    finished.value = listData.value.length >= pages?.total ?? 0;
  };
  const refreshCallback = (func: PromiseFunc) => {
    finished.value = false;
    loading.value = true;
    pagination.value.pageIndex = 1;
    setTimeout(() => {
      getListData(func);
    }, 20);
  };
  const loadMore = async (func: PromiseFunc) => {
    pagination.value.pageIndex += 1;
    getListData(func);
  };
  return {
    pagination,
    listData,
    loading,
    finished,
    refreshing,
    getListData,
    refreshCallback,
    loadMore
  };
};

export default useListRefresh;

组件中使用

// 列表分页
const {
  pagination,
  listData,
  loading,
  finished,
  refreshing,
  getListData,
  refreshCallback,
  loadMore
} = useListRefresh();