unipp vue3版本如何封装一个请求加载更多loading组件

4周前
椰子皮
55
0
0
uniapp

我们在开发app的时候几乎每个app页面都有列表,获取详情,上拉加载更多;能不能把这几种情况封装成一个组件呢,包括错误信息显示,loading等。新建一个fetch.vue组件粘贴如下代码:<t

我们在开发app的时候几乎每个app页面都有列表,获取详情,上拉加载更多;能不能把这几种情况封装成一个组件呢,包括错误信息显示,loading等。

新建一个fetch.vue组件粘贴如下代码:

<template>
	<view class="ky-fetch-wrap">

		<view v-show="loadState === 1" class="ky-fetch-content">
			<slot v-if="isLoadmore" :list="list" name="default"></slot>
			<slot v-else :data="data" name="default"></slot>
			<view v-if="loadState === 1 && isLoadmore" class="ky-fetch-more-nodata" @click="reload">
				<text class="ky-fetch-more-tips">{{ tips }}</text>
				<image v-if="!noMore && !loadError" class="ky-fetch-more-loading" src="/static/img/load.gif"></image>
			</view>
		</view>

		<view v-if="loadState === 0 || !loadState" :class="{ isRelative }" class="ky-fetch-loading">
			<loading v-if="!$slots.loading"></loading>
			<slot name="loading"></slot>
		</view>

		<view v-if="loadState !== 0 && loadState !== 1" class="ky-fetch-error">

			<view v-if="!$slots.nodata || !$slots.error" class="ky-fetch-error-tip" @click="reload">
				<template v-if="!noIcon">
					<image v-if="loadState === -1" class="ky-fetch-err-img" src="@/static/img/nodata.png"></image>
					<image v-else-if="loadState === -2" class="ky-fetch-err-img" src="@/static/img/request_error.png">
					</image>
					<image v-else class="ky-fetch-err-img" src="@/static/img/network_error.png"></image>
				</template>
				<text
					class="ky-fetch-err-txt">{{ msg || noDataMsg || (loadState === -1 ? '暂无相关内容' : '加载失败,请点击重试') }}</text>
			</view>

			<view v-if="$slots.nodata && loadState === -1" class="ky-fetch-error-tip">
				<slot name="nodata"></slot>
			</view>

			<view v-if="$slots.error && loadState === -2" class="ky-fetch-error-tip">
				<slot name="error"></slot>
			</view>

		</view>

	</view>
</template>

<script>
	export default {
		props: {
			// 是否加载列表
			isLoadmore: {
				type: Boolean,
				default: false
			},
			// api地址
			api: {
				type: String,
				default: '',
				required: true,
			},
			// status 1:请求成功, -1:暂无数据, -2:请求失败, -3: 无网络
			status: {
				type: Number,
				default: 0
			},
			// 筛选条件
			condition: {
				type: Object,
				default: () => {}
			},
			// 不主动执行请求
			manual: {
				type: Boolean,
				default: false
			},
			// 是否相对定位
			isRelative: {
				type: Boolean,
				default: false,
			},
			// 是否请求完成就隐藏loading
			autoHideLoading: {
				type: Boolean,
				default: true
			},
			// 是否返回合并后的数据
			concat: {
				type: Boolean,
				default: true
			},
			noDataMsg: {
				type: String,
				default: '',
			},
			errMsg: {
				type: String,
				default: '',
			},
			// 无内容或者错误时不显示图标
			noIcon: {
				type: Boolean,
				default: false
			}
		},
		data() {
			return {
				noMore: false,
				loadState: 0,
				filter: {
					page: 1,
					size: 10
				},
				data: {},
				list: [],
				tips: '没有更多了',
				loadError: false,
				loading: false,
				isChangeCondition: false,
				msg: '',
			}
		},
		watch: {
			status(v) {
				this.loadState = v
			},
		},
		mounted() {
			if (!this.manual) {
				this.init()
			}
			this.msg = this.errMsg
			this.loadState = this.status
			this.setFilters(this.condition)
		},
		methods: {
			async init() {
				this.filter.page = 1
				this.list = []
				return await this.getList()
			},
			// 为了条件查询后使用的, noReload是否设置loadState为1
			refresh(noReload) {
				this.filter.page = 1
				this.list = []
				// 不延迟的话,condition里面的东西拿不到
				setTimeout(() => {
					this.getList(noReload)
				})
			},
			reload() {
				if (this.loading || this.noMore) {
					return
				}
				this.loading = false
				this.getList()
			},
			hideLoading() {
				this.loadState = 1
			},
			setErrMsg(msg) {
				this.msg = msg
				this.loadState = -2
			},
			setFilters(data = {}) {
				const obj = {
					...this.filter,
					...data,
				}
				for (let a in obj) {
					if (!obj[a] && typeof obj[a] !== 'number') {
						delete obj[a]
					}
				}
				this.filter = obj
			},
			async getList(noReload) {
				this.loading = true
				this.setFilters(this.condition)

				let rows = []
				const params = this.filter
				const {
					page,
					size
				} = params

				if (page === 1 && !noReload) {
					this.loadState = 0
				}

				if (params.requestApi) {
					params.requestBodyDic.page = page
					params.requestBodyDic.size = size
					delete params.page
					delete params.size
				}

				try {
					const apiUrl = this.api.split('.')
					const res = await this.$api[apiUrl[0]][apiUrl[1]](params)
					const {
						data,
						lists,
						list,
						count
					} = res

					if (!this.isLoadmore) {
						this.data = data || res
						this.loadState = 1
						return this.data
					}

					if (!data) {
						rows = lists || list || []
					} else {
						rows = Array.isArray(data) ? data : data.lists
					}

					const newList = rows
					const isNoData = !newList.length && page === 1

					this.list = this.concat ? [...this.list, ...newList] : newList

					this.noMore = newList.length < size

					if (data && data.count) {
						this.noMore = this.list.length >= data.count
					}

					if (count) {
						this.noMore = this.list.length >= count
					}

					this.tips = this.noMore ? '没有更多了' : '加载中...'

					if (this.autoHideLoading) {
						this.loadState = isNoData ? -1 : 1
					}

					this.loadError = false
					this.$emit('load', this.list)

					if (page === 1 && data) {
						const obj = {
							...data
						}
						delete obj.lists
						this.$emit('all', obj)
					}

				} catch (e) {
					if (page === 1) {
						this.loadState = -2
					}
					if (e.code === 504) {
						this.loadState = -3
					}
					this.tips = e.msg || '加载失败,请点击重试'
					this.msg = this.tips
					this.loadError = true
					this.$emit('error', e)
					return Promise.reject(e)
				} finally {
					this.loading = false
				}
			},
			loadMore() {
				if (this.noMore) {
					return
				}
				if (this.loading) {
					return
				}
				if (this.loadError) {
					return
				}
				this.filter.page++
				this.getList()
			}
		},
	}
</script>

<style scoped lang="scss">
	.ky-fetch-more-nodata {
		text-align: center;
		padding: 24rpx 0;
		display: flex;
		align-items: center;
		justify-content: center;

		.ky-fetch-more-tips {
			font-size: 24rpx;
			color: var(--color-gray);
		}

		.ky-fetch-more-loading {
			width: 40rpx;
			height: 40rpx;
			display: inline-block;
			margin-left: 12rpx;
		}
	}

	.ky-fetch-loading {
		position: absolute;
		top: 0;
		left: 0;
		right: 0;
		bottom: 0;
		margin: auto;
		display: flex;
		justify-content: center;
		align-items: center;
		z-index: 0;
		padding: 24rpx;

		&.isRelative {
			position: relative;
			min-height: 100rpx;
		}
	}

	.ky-fetch-error {
		position: absolute;
		top: 0;
		left: 0;
		right: 0;
		bottom: 0;
		margin: auto;
		display: flex;
		justify-content: center;
		align-items: center;
		z-index: 0;
		padding: 24rpx;

		.ky-fetch-error-tip {
			display: flex;
			align-items: center;
			justify-content: center;
			flex-direction: column;
		}

		.ky-fetch-err-img {
			width: 500rpx;
			height: 333rpx;
		}

		.ky-fetch-err-txt {
			font-size: 24rpx;
			color: var(--color-gray);
		}
	}
</style>

 

参数api我这里是通过访问下标的方式进行访问,你们自己的话可以换成自己封装的请求或者直接传接口地址

 

使用方式:

<fetch :condition="condition" isLoadmore ref="list" api="user.RequestForwarding">
	<template v-slot:default="{ list }">
		<view class="pa-list m-24">
			<view v-for="(item, index) in list" :key="index" class="pa-item card p-24">
				测试123
			</view>
		</view>
	</template>
</fetch>

 

上拉加载直接调用this.$refs.list.loadMore()

刷新调用this.$refs.list.refresh()

loading.vue:

<template>
	<view :class="{ isRelative }" class="ky-loading-box loading5">
		<view class="shape shape1"></view>
		<view class="shape shape2"></view>
		<view class="shape shape3"></view>
	</view>
</template>

<script>
	export default {
		props: {
			isRelative: {
				type: Boolean,
				default: false,
			}
		}
	}
</script>

<style scoped lang="scss">
	.ky-loading-box {
		height: 25rpx;
		position: absolute;
		top: 0;
		left: 0;
		right: 0;
		bottom: 0;
		margin: auto;
		display: flex;
		justify-content: center;
		align-items: center;
		z-index: 0;
		&.isRelative {
			position: relative;
			min-height: 100rpx;
		}
	}
	.shape {
	  width: 25rpx;
	  height: 25rpx;
	  border-radius: 50%;
	  background-color: var(--color-primary);
		margin: 0 6rpx;
	}
	.shape1 {
	  animation: pulse .4s ease 0s infinite alternate;
	}
	.shape2 {
	  animation: pulse .4s ease .2s infinite alternate;
	}
	.shape3 {
	  animation: pulse .4s ease .4s infinite alternate;
	}
	@keyframes pulse {
	  from {
	    opacity: 1;
	    transform: scale(1);
	  }
	  to {
	    opacity: .25;
	    transform: scale(.5);
	  }
	}
</style>

 

效果:

支付宝微信
0
关注公众号获取更多内容
什么是globalThis?globalThis全知道
结合lazyload实现文章页里面的图片预加载
暂无评论,快抢沙发吧
不支持canvas
春季
夏季
秋季
冬季
暗黑
简约
小清新