Vue3 结合Vant框架封装页面级数据供应Provider高阶插件

1,105 阅读2分钟

抽空把之前封装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 []  }}