项目一开始用的vue2,所以后面有点空闲时间了才使用组合式Api做的列表封装。 参考了掘金里的一些文章。
hooks/usePageList.js
import { ref, reactive, toRefs } from '@vue/composition-api';
import toast from '@/utils/toast.js';
/**
* @description 创建并管理页面列表数据的状态和操作方法。
* 接收一个配置对象`opts`,返回包含页面状态和操作函数的对象,以便于在Vue组件中直接使用。
* @param {Object} opts - 配置选项对象
* @param {Function} opts.getListApi - 获取列表数据的异步函数
* @param {Function} [opts.onFetchListSuccess] - 获取列表数据成功后的回调,默认为空函数
* @param {Function} [opts.onFetchListFail] - 获取列表数据失败后的回调,默认为空函数
* @returns {Object} 返回一个包含页面状态和操作方法的对象:
* @returns {Object} page - 响应式对象,管理页面列表的状态,具体包含:
* @returns {boolean} page.loading - 表示数据是否正在加载中。
* @returns {boolean} page.refreshing - 表示列表是否处于刷新状态。
* @returns {boolean} page.isEnablePullUp - 是否启用上拉加载更多逻辑。
* @returns {string} page.listStatus - 列表加载状态,可为:"loadmore"(需加载更多)、"loading"(加载中)、"nomore"(没有更多数据)。
* @returns {Array} page.list - 存储列表数据的数组。
* @returns {number} page.pageIndex - 当前分页的页码。
* @returns {number} page.pageSize - 每页显示的数据条数。
* @returns {number} page.total - 数据库中的总记录数,用于分页计算。
* @returns {Object} page.resData - 从后端获取的额外数据,后端老要塞其他数据。
* @returns {Function} debounceSearch - 防抖处理的搜索函数,用于触发列表数据刷新。
* @returns {Function} onSearch - 立即触发列表数据刷新的函数。
* @returns {Function} onRefresh - 刷新列表数据的函数,常用于下拉刷新操作。
* @returns {Function} loadData - 加载更多数据的函数,通常绑定至列表上拉加载事件。
*/
export default function usePage(opts) {
console.log('接收参数', opts);
const {
getListApi,
onFetchListSuccess = (opts) => {},
onFetchListFail = (opts) => {},
} = opts;
const page = reactive({
loading: false,
refreshing: false,
isEnablePullUp: true,
listStatus: 'loadmore',
list: [],
pageIndex: 1,
pageSize: 15,
// // 总共多少页 后端不给提供
// totalPage: 0,
total: 0,
resData: {},
searchParams: {}, // 查询参数
});
// const {
// loading,
// refreshing,
// isEnablePullUp,
// listStatus,
// list,
// pageIndex,
// pageSize,
// // totalPage, // 如果需要 totalPage,取消注释
// total,
// resData,
// } = toRefs(page);
// 加载数据
const loadData = () => {
return new Promise((resolve, reject) => {
console.log('加载数据');
page.loading = true; // 在开始加载时设置加载状态为true
getListApi()
.then((res) => {
if (res.status !== 200) {
toast.showToast(res.message);
onFetchListFail(res);
page.loading = false; // 加载失败后重置加载状态
reject(res);
return;
}
page.total = res.data.count;
page.resData = res.data;
page.list.push(...res.data.list);
if (page.list.length === res.data.count) {
console.log('列表加载完成,没有更多数据了');
page.isEnablePullUp = false;
page.listStatus = 'nomore';
}
page.loading = false; // 加载成功后重置加载状态
onFetchListSuccess(res.data);
resolve(res.data);
})
.catch((err) => {
console.log('列表接口失败', err);
page.loading = false; // 加载失败后重置加载状态
onFetchListFail(err);
reject(err);
});
});
};
// 下拉刷新
const onRefresh = async () => {
page.refreshing = true;
page.isEnablePullUp = true;
page.list = [];
page.listStatus = 'loadmore';
page.pageIndex = 1;
await loadData();
};
// 上拉加载
const onPullUp = async () => {
if (!page.isEnablePullUp) {
console.log('没有更多数据直接退出');
return;
}
page.pageIndex += 1;
// 加载数据
await loadData();
};
const onSearch = async () => {
await onRefresh();
};
// 防抖
const debounceSearch = () => {
uni.$u.debounce(onSearch(), 1000);
};
return {
page,
debounceSearch,
onSearch,
onRefresh,
onPullUp,
loadData,
};
}
使用 script setup 模式
xxx.vue
<template>
<view class="page">
<view
style="height: 300rpx; margin-bottom: 100rpx; background-color: red"
v-for="(item, index) in page.list"
:key="index"
></view>
</view>
</template>
<script setup>
import {
ref,
reactive,
defineExpose,
inject,
computed,
} from '@vue/composition-api';
import {
onReady,
onLoad,
onPullDownRefresh,
onReachBottom,
} from '@dcloudio/uni-app';
import usePage from '@/hooks/usePageList.js';
import { Api } from '../../models/api.js';
import toast from '@/utils/toast.js';
const app = getApp();
const api = new Api();
// 列表相关
const getListApi = () => {
return api.getMerchantList({
page: page.pageIndex,
limit: page.pageSize,
...page.searchParams,
});
};
const onFetchListSuccess = (data) => {
console.log('列表成功回调函数', data);
};
const onFetchListFail = (err) => {
console.log('列表失败回调函数', err);
};
// 加载数据,和下拉刷新,等一系列操作,封装在 usePage 中,
const { page, debounceSearch, onSearch, onRefresh, onPullUp } = usePage({
getListApi,
onFetchListSuccess,
onFetchListFail,
});
// 修改查询参数
page.searchParams = {
test: '123',
test1: '123',
};
console.log('xxxxxxxxx', page.searchParams);
// // 多个列表可以使用命名空间
// const newList = reactive(
// usePage({
// getListApi,
// onFetchListSuccess,
// onFetchListFail,
// })
// );
// console.log(newList.page, "newList", newList);
onLoad(async () => {
toast.showLoading();
await onSearch();
uni.hideLoading();
console.log('onLoad初始化', page);
});
onReady(() => {
console.log('onReady', page);
});
// 下拉刷新
onPullDownRefresh(() => {
onRefresh();
console.log('下拉刷新', page);
});
// 上拉加载
onReachBottom(() => {
onPullUp();
console.log('上拉加载', page);
});
defineExpose({
// 注意要在 defineExpose 函数外定义
// title,
// testClick,
});
</script>
<style lang="scss" scoped>
@import '@/base.scss';
</style>