抽空把之前封装Vue2的插件,转成Vue3了,封装了数据请求的加载状态,错误,及结果的自动显示,能自动对接口响应数据的状态显示页面及提示,三种类型,实现数据下拉刷新,上拉加载,重点在思路,供大家参考:
/*数据供应高阶组件,封装数据请求的加载状态,错误,及结果的自动显示,调用方法如下:@state (可选)设置页面初始状态 取值如下ViewState@type (可选)设置状态页面功能类型,默认default,下拉刷新 refresh, 刷新列表 refreshList (包含下拉刷新,上拉加载)@pageSize (可选)设置分页加载数,默认为10@:params (可选)为请求函数initData传参@immediate (可选)是否立即执行一次 默认true@request (必填)Promise数据请求返回对象,暴露 params, {pageNum:pageNum.value, pageSize:this.pageSize}, this 三个对象参数,可获取高阶组件的params传参及分页对象参数,以及上下文this,用于手动访问高阶组件的内部状态和方法@created (可选)暴露高阶组件内部created钩子及内部this对象@mounted (可选)暴露高阶组件内部mounted钩子及内部this对象@#default="{ data }" (必填)暴露的请求数据插槽对象,供模板调用--------------------------------------<provider :state="0" type="default" :pageSize = "10" :params="params" @request="request" #default="{ data }">hello world, data={{data}}</provider>//request方法调用示例setup(){ const params = reactive({market:'ethusdt'}) function request(params, {pageNum, pageSize}, context){ //params绑定参数,pageNum 传递当前分页, pageSize分页单页加载数,context高阶组件内部this对象 return api.get(`api/v3/trade/market`, {data:{pageNum,pageSize}}) } return { params, request }}--------------------------------------*/import FLoading from '@/components/FLoading'import StateView from './provider_state_view'import ViewState from './view_state.js'import { onMounted, getCurrentInstance, watch, h } from 'vue'import api from '@/api/request'import { PullRefresh, List } from 'vant'import { ref } from 'vue'import { useI18n } from 'vue-i18n'export default { install: (app, options) => { app.component('provider', { props: { params: { default: null }, pageSize: { type: Number, default: 15 }, state: { type: [Number, String], default: null }, immediate: { type: Boolean, default: true }, type: String }, setup(props, ctx) { const self = getCurrentInstance() const { t } = useI18n() const refreshing = ref(false) //刷新状态 const loading = ref(false) //加载更多状态 const isLoading = ref(false) //是否在加载状态 const finished = ref(false) //是否加载完所有数据 const pageNum = ref(1) //当前分页 const viewState = ref(props.state<<0 || ViewState.idle) //页面状态 const message = ref('') //状态信息 const successText = ref('刷新成功') //刷新结果状态信息 const data = ref(null) //数据请求结果 let request = ctx.attrs.onRequest if (typeof request != 'function') { request = null console.error('Provider: request method no setting or not a function') } async function initData(init) { //init 是否第一次请求数据 if (!request) { return } if (init) { viewState.value = props.state<<0 || ViewState.idle } let result = await request(props.params, { pageNum: props.pageNum, pageSize: props.pageSize }, self); //暴露组件变量传参params, 分页对象参数, 及组件上下文this对象 switch (result.rst) { case api.SUCCESS: init && !result.data && (viewState.value = ViewState.empty) return result.data case api.UnAuthorized: message.value = `${result.code || ''} ${t('error_code.' + result.msg)}` init && (viewState.value = ViewState.unAuthorized) return default: message.value = `${result.code || ''} ${t('error_code.' + result.msg)}` init && (viewState.value = ViewState.error) return } } async function refresh(init = false) { pageNum.value = 1 finished.value = false let _data = await initData(init); if (_data) { data.value = _data successText.value = '刷新成功' if (Array.isArray(_data)) { if (_data.length > 0) { init && (viewState.value = ViewState.idle) if (props.type == 'refreshList' && _data.length < props.pageSize) { finished.value = true } } else { init && (viewState.value = ViewState.empty) } } else { init && (viewState.value = ViewState.idle) } } else { successText.value = '刷新失败' } refreshing.value = false } async function loadMore() { if (isLoading.value) { return } isLoading.value = true pageNum.value += 1 let _data = await initData(false); if (!_data || _data.length == 0) { pageNum.value -= 1 finished.value = true } else { data.value = data.value.concat(_data) if (_data.length < props.pageSize) { finished.value = true } } isLoading.value = false loading.value = false } function setBusy(value) { message.value = '' viewState.value = value ? ViewState.busy : ViewState.idle } function setEmpty(value) { message.value = ''; viewState.value = ViewState.empty } function setError(value) { message.value = value; viewState.value = ViewState.error } function setUnAuthorized(value) { message.value = value ? t('未登录') : '' viewState.value = value ? ViewState.unAuthorized : ViewState.idle } props.immediate && refresh(true); if (typeof ctx.attrs.onCreated == 'function') { ctx.emit('created', self) } onMounted(() => { watch( () => props.params, () => { refresh(true) }, { deep: true } ) if (typeof ctx.attrs.onMounted == 'function') { ctx.emit('mounted', self) } }) return { refreshing, successText, refresh, loading, finished, loadMore, viewState, message, data, context: self.ctx } }, render() { return h('div', null, showViewByState(this)) } }) }}const showViewByState = function (context) { function getChild(context) { let child , slots = {...context.$slots} slots.default = ()=> context.$slots.default({ data: context.data }) switch (context.type) { case 'refreshList': child = h(PullRefresh, { modelValue: context.refreshing, 'onUpdate:modelValue': val => { context.refreshing = val }, successText: context.$t(context.successText), onRefresh: () => context.refresh() }, {default:()=> h(List, { loading: context.loading, finished: context.finished, finishedText: context.$t('没有更多了'), immediateCheck: false, 'onUpdate:loading': val => { context.loading = val }, onLoad: () => context.loadMore() }, slots)}) break case 'refresh': child = h(PullRefresh, { modelValue: context.refreshing, 'onUpdate:modelValue': val => { context.refreshing = val }, successText: context.$t(context.successText), onRefresh: () => context.refresh() }, slots) break default: child = slots break } return child } switch (context.viewState) { case ViewState.idle: return getChild(context) case ViewState.busy: return [h(FLoading, { bgcolor: 'rgba(0, 0, 0, 0.32)', size: 40 })] case ViewState.empty: return [h(StateView, { context: context })] case ViewState.error: return [h(StateView, { image: require('@/assets/img/empty/empty-image-error.png'), message: context.message, buttonText: context.$t('重试'), context: context })] case ViewState.unAuthorized: return [h(StateView, { image: require('@/assets/img/empty/empty-image-error.png'), message: context.message, buttonText: context.$t('点击登录'), context: context })] default: return [] }}