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

2023-03-02 11:02
椰子皮
1190
0
1
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>

 

效果:

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