uniapp页面列表列表请求hook记录

请求hook

js 复制代码
import {
	ref,
	watch,
	computed
} from "vue"
import {
	onLoad,
	onReachBottom,
	onPullDownRefresh
} from "@dcloudio/uni-app"
import {
	throttle,
	debounce
} from "@/assets/utils/common.js"

export const useListData = (requestParams = {}) => {
	const request = requestParams.request || ref(null) // 请求数据的方法
	const params = requestParams.params || ref({}) // 请求传递的参数
	const size = requestParams.size || 10 // 一次加载多少条数据
	const immediate = requestParams.immediate !== undefined ? requestParams.immediate : true // 页面加载时立即加载数据
	const watchParams = requestParams.watchParams || true // 监听参数变化
	const watchRequest = requestParams.watchRequest === undefined ? true : requestParams.watchRequest // 监听请求参数的变化
	const includeKeys = requestParams.includeKeys || [] // 需要监听那些参数的变化

	const loading = ref(true)
	const finished = ref(false)
	const error = ref(false)
	const noData = ref(false)
	const pageIndex = ref(1)
	const pageSize = ref(size)
	const list = ref([])

	const resetData = (refresh = true) => {
		loading.value = true
		finished.value = false
		error.value = false
		noData.value = false
		list.value = []
		pageIndex.value = 1
		refresh && getListFn()
	}
	const getData = () => {
		// if (!request.value) return getApp().globalData.toast('请传入请求数据的方法')
		// if (!request?.value || loginInfoStore.loading.value) return
		if (!request?.value) return
		pageIndex.value === 1 && resetData(false)
		loading.value = true
		const data = {
			...params.value,
			pageIndex: pageIndex.value,
			pageSize: pageSize.value,
		}
		request.value(data)
			.then(res => {
				// 兼容不同接口返回来的数据结构不同-集优臻选
				if (res?.data?.records) {
					const data = res?.data?.records || []
					if (pageIndex.value === 1) {
						list.value = [...data]
					} else {
						list.value = [...list.value, ...data]
					}
					if (list.value?.length === res?.data?.total) {
						finished.value = true
					}
					if (!list.value?.length) {
						noData.value = true
					}
					return
				}
				// 兼容不同接口返回来的数据结构不同-速团
				if (res?.data?.list) {
					const data = res?.data?.list.data || []
					list.value = [...list.value, ...data]
					if (Number(res?.data?.list.pageCount) === pageIndex.value || (pageSize.value > data?.length)) {
						finished.value = true
					}
					if (!list.value?.length) {
						noData.value = true
					}
					return
				}
				if (res?.data?.data) {
					const data = res?.data?.data || []
					list.value = [...list.value, ...data]
					if (Number(res?.data?.pageCount) === pageIndex.value) {
						finished.value = true
					}
					if (!list.value?.length) {
						noData.value = true
					}
					return
				}
				if (Array.isArray(res.data)) {
					list.value = res.data
					finished.value = true
					if (!list.value?.length) {
						noData.value = true
					}
					return
				}
				console.log('没有找到对应结构')
				finished.value = true
				noData.value = true
			})
			.catch((err) => {
				error.value = true
			})
			.finally(() => {
				loading.value = false
			})
	}

	const getListFn = debounce(getData, 200)

	const onError = () => {
		getListFn()
	}
	const filterWatchKeys = () => {
		//  保证includeKeys中的key存在与params中
		const paramsKeys = Object.keys(params.value)
		const includesKeys = paramsKeys.filter(key => includeKeys.includes(key))
		return includesKeys.map(key => params.value[key])
	}
	onLoad(() => {
		immediate && getListFn()
	})
	onReachBottom(() => {
		if (loading.value || finished.value) return
		// 防止前面内容过长,页面还没有初始化加载的时候就触发了page++,造成初次加载加载的是第二页的数据
		if (pageIndex.value === 1 && !list.value?.length) return
		pageIndex.value = pageIndex.value + 1
		getListFn()
	})
	const onScrollToLower = () => {
		if (loading.value || finished.value) return
		// 防止前面内容过长,页面还没有初始化加载的时候就触发了page++,造成初次加载加载的是第二页的数据
		if (pageIndex.value === 1 && !list.value?.length) return
		pageIndex.value = pageIndex.value + 1
		getListFn()
	}

	onPullDownRefresh(() => {
		resetData()
	})
	// 监听参数变化
	watch(
		() => filterWatchKeys(),
		() => {
			watchParams && resetData()
		}
	)
	// 监听请求数据的方法的变化
	watch(
		() => request.value,
		() => {
			watchRequest && resetData()
		}
	)
	return {
		loading,
		finished,
		error,
		noData,
		list,
		pageIndex,
		pageSize,
		getListFn,
		resetData,
		onError,
		onScrollToLower
	}
}

export default useListData

使用

js 复制代码
import useListData from "@/hooks/useListData.js";
import { getList } from "@/api/product.js";

const request = ref(getList);
const params = ref({
  // lon: location.value.longitude,
  // lat: location.value.latitude,
  isGold: 0,
  isPresale: 1
});
const {
  loading,
  finished,
  error,
  noData,
  list,
  pageIndex,
  pageSize,
  getListFn,
  resetData,
  onError,
} = useListData({
  immediate: true,
  request,
  params,
  // includeKeys: ['cityId', 'tagKey', 'categoryKey', 'lat']
});

对应列表状态组件

html 复制代码
<template>
	<view class="comp-my-list-status">
		<view v-if="loading" class="text-tip" :class="textClass" >{{ loadingText }}</view>
		<view v-else-if="error" class="text-tip" :class="textClass" @click="onError">{{ errorText }}</view>
		<view v-else-if="finished && !noData" class="text-tip" :class="textClass">{{ finishedText }}</view>
		<view v-else-if="finished && noData" class="no-data-box" >
			<view class="no-data-img-box">
				<image class="no-data-img" :class="noDataImgClass" src="../static/imgs/img-no-data@2x.png" mode="widthFix"></image>
			</view>
			
			<view class="no-data-text" :class="noDataTextClass">{{ noDataText }}</view>
		</view>
		<view v-else class="" ></view>
	</view>
</template>

<script setup>
	import {
		defineProps,
		defineEmits
	} from "vue"
	
	const emits = defineEmits(['error'])
	const props = defineProps({
		loading: {
			type: Boolean,
			default: false
		},
		finished: {
			type: Boolean,
			default: false
		},
		error: {
			type: Boolean,
			default: false
		},
		noData: {
			type: Boolean,
			default: false
		},
		loadingText: {
			type: String,
			default: '数据加载中...'
		},
		finishedText: {
			type: String,
			default: '已加载全部数据'
		},
		errorText: {
			type: String,
			default: '数据加载错误,点击重新加载'
		},
		noDataText: {
			type: String,
			default: '暂时还没有数据哦~'
		},
		noDataTextClass: {
			type: String,
			default: ''
		},
		noDataImgClass: {
			type: String,
			default: ''
		},
		textClass: {
			type: String,
			default: ''
		}
	})
	
	const onError = () => {
		emits('error')
	}
</script>

<style lang="scss">
.comp-my-list-status{
	.text-tip{
		text-align: center;
		font-size: 24rpx;
		padding: 16rpx;
		color: #ccc;
	}
	.no-data-box{
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;
		
		.no-data-img-box{
			width: 50%;
			aspect-ratio: 1;
			.no-data-img{
				// width: 228rpx;
				// height: 228rpx;
				width: 100%;
				height: 100%;
			}
		}
		
		.no-data-text{
			text-align: center;
			font-weight: 400;
			// font-size: 20rpx;
			font-size: 24rpx;
			color: #000000;
			line-height: 28rpx;
		}
	}
}
</style>
相关推荐
bluceli2 小时前
前端微前端架构实战指南:构建可扩展的大型应用架构
前端
进击的尘埃2 小时前
给 PR 接一个 LLM 自动 Review:GitHub Actions 落地踩坑全记录
javascript
阿懂在掘金2 小时前
Vue Asyncx 库三周年,回顾起源时的三十行代码
前端·typescript·开源
一只不会编程的猫2 小时前
Echart 3D环形图
前端·javascript·3d
脸大是真的好~2 小时前
黑马AI+前端教程 01-HTML-Trae-F12-live Server-标签-块级和内联元素-图片格式-路径
前端·html
楚城相拥2 小时前
uniapp引入bmob实现预览
uni-app
前端付豪2 小时前
拍照识题 OCR
前端·后端·python
专业流量卡2 小时前
龙虾写useEffect源码第二天
前端
2501_915921432 小时前
iOS APP上架工具,在没有 Mac 的环境中发布苹果应用
android·macos·ios·小程序·uni-app·iphone·webview