vant list refresh 二次封装

发布时间 2023-11-14 20:31:18作者: wjs0509

组件:

<template>
  <div>
    <van-pull-refresh v-model="refreshing" @refresh="onRefresh" :disabled="disabled" :style="fullScreen? 'min-height: 100vh;': ''">
      <van-list
        v-model="loading"
        :finished="finished"
        :finished-text="finishedText"
        error-text="请求失败,点击重新加载"
        @load="onLoad"
        :immediate-check="immeCheck"
      >
        <slot></slot>
        <div v-if="noData" class="no-data-cont" :class="noDataClass">
          <!-- 暂无数据图片展示 -->
          <img :src="noDateIcon" alt="">
          <p>{{searchInput ? '没有查询到任何内容':'- 暂无数据 -'}}</p>
        </div>
        <div slot="finished" class="finished-data">
          <p v-if="!noData">{{ finishedText }}</p>
        </div>
      </van-list>
    </van-pull-refresh>
  </div>
</template>

<script>
export default {
  props: {
    immeCheck: {
      type: Boolean,
      default: false
    },
    searchInput: {
      type: String,
      default: ''
    },
    fullScreen: { // 是否需要让下拉区域始终为全屏
      type:Boolean,
      default: false
    },
    disabled: {
      type: Boolean,
      default: false
    },
    noDataClass: {
      type: String,
      default: ''
    },
    finishedText: {
      type: String,
      default: '没有更多了'
    }
  },
  data () {
    return {
      refreshing: false,
      loading: false,
      finished: false,
      noData: false,
      noDateIcon: require('@/assets/common/page-empty.png'),
      len: 0,
      total: 0
    }
  },
  methods: {
    // 刷新加载完成
    onRefreshSuc () {
      this.refreshing = false
      this.finished = false
    },
    onRefresh () {
      setTimeout(() => {
        this.$emit('onRefreshList')
      }, 500)
    },
    // true  false  是否全部加载完成
    // onLoadSuc (isOver) {
    //   this.loading = false
    //   this.finished = isOver
    // },
    // len  已加载数据  total 总数据
    onLoadSuc(len, total) {
      this.loading = false
      this.len = len
      this.total = Number(total)
      if (Number(total) > 0) {
        this.noData = false
        if (len < Number(total)) {
          this.finished = false
        } else {
          this.finished = true
        }
      } else {
        this.finished = true
        this.noData = true
      }
    },
    onLoad () {
      setTimeout(() => {
        if (this.len < this.total) {
          this.$emit('onLoadMore')
        } else {
          this.finished = true
        }
      }, 500)
    }
  }
}
</script>

<style lang="scss" scoped>
/deep/ .van-pull-refresh {
  // overflow: visible!important;
}
.data-none {
  font-size: .2rem;
  color: #888888;
  line-height: .4rem;
}
.finished-data {
  p:nth-child(2) {
    text-align: center;
    color: #969799;
    font-size: .28rem;
  }
}
.no-data-cont {
  // height: 4rem;
  min-height: 4rem;
  width: 100%;
  padding-top: 0.3rem;
  text-align: center;
  img {
    // display: block;
    // margin: 0 auto;
    display: inline-block;
    height: 3rem;
    width: 3rem;
  }
  p {
    text-align: center;
    color: #969799;
    font-size: .28rem;
  }
}
.no-h-data-cont {
  height: auto;
  img {
    display: block;
    margin: 0 auto;
    height: auto;
    width: auto;
  }
}
</style>

使用:

<template>
  <div>
    <div class="panel-title">
      <div class="left-avatar">
        <img :src="userInfo.avatar || defaultAvatar" alt="" />
        <span v-if="userInfo.gender == 1" class="gender-icon">
          <svg-icon icon-class="nan"></svg-icon>
        </span>
        <span v-if="userInfo.gender == 2" class="gender-icon">
          <svg-icon icon-class="ru"></svg-icon>
        </span>
      </div>
      <div class="left-info">
        <div class="nickname">
          <span>{{ userInfo.title || '-' }}</span>
        </div>
        <div class="info">
          <span>{{ userInfo.age || '-' }}岁</span>
          <span></span>
          <span>{{ userInfo.phone || '-' }}</span>
        </div>
      </div>
    </div>
    <LoadMore
      class="loadMore"
      key="ehbList"
      @onRefreshList="onRefreshList"
      @onLoadMore="onLoadMore"
      ref="loadMoreCont"
    >
      <div class="userItem" v-for="(item, index) in list" :key="index">
        <div class="item-top">
          <van-image
            class="item-top-left-avatar"
            round
            :src="item.customerHeadImg || defaultAvatar"
          />
          <p class="item-top-left-name">{{ item.customerName }}</p>
        </div>
        <div class="item-bottom">
          <div class="item-bottom-content">
            <span class="label">添加时间</span>
            <span class="value">2023-10-19 17:37:45</span>
          </div>
          <div class="item-bottom-content">
            <span class="label">所属部门</span>
            <span class="value">产品部、技术部、测试部共3个部门</span>
          </div>
        </div>
      </div>
    </LoadMore>
  </div>
</template>

<script>
import LoadMore from '@/components/loadMore/index'
export default {
  name: 'friends',
  components: {
    LoadMore
  },
  data() {
    return {
      userInfo: {},
      defaultAvatar: require('@/assets/qwGuide/headportrait.png'),
      pageNum: 1,
      pageSize: 10,
      list: [
        {
          customerName: '张三'
        },
        {
          customerName: '张三'
        },
        {
          customerName: '张三'
        },
        {
          customerName: '张三'
        },
        {
          customerName: '张三'
        },
        {
          customerName: '张三'
        }
      ]
    }
  },

  created() {
    // this.getList()
  },

  methods: {
    getList() {
      const params = {
        pageNum: this.pageNum,
        pageSize: this.pageSize
      }
      this.$toast.loading({
        message: '加载中...',
        forbidClick: true,
        duration: 0
      })
      // receiveRecordPageList(params).then(res => {
      //   this.$toast.clear()
      //   if (this.$refs.loadMoreCont) this.$refs.loadMoreCont.onRefreshSuc()
      //   if (this.pageNum == 1) {
      //     this.list = res.data.list || []
      //   } else {
      //     this.list = this.list.concat(res.data.list || [])
      //   }
      //   if (this.$refs.loadMoreCont) this.$refs.loadMoreCont.onLoadSuc(this.list.length, res.data.total)
      // }).catch(() => {
      //   this.$toast.clear()
      // })
    },
    onRefreshList() {
      this.pageNum = 1
      this.getList()
    },
    onLoadMore() {
      this.pageNum++
      this.getList()
    }
  }
}
</script>

<style lang="scss" scoped>
.panel-title {
  background: #ffffff;
  border-radius: 0px 0px 0.08rem 0.08rem;
  padding: 0.48rem 0.32rem 0.32rem;
  display: flex;
  align-items: center;
  .left-avatar {
    position: relative;
    width: 0.88rem;
    height: 0.88rem;
    margin-right: 0.16rem;

    img {
      width: 100%;
      height: 100%;
      border-radius: 50%;
    }

    .gender-icon {
      position: absolute;
      display: block;
      width: 0.28rem;
      height: 0.28rem;
      line-height: 0.28rem;
      bottom: 0;
      right: 0;
      border-radius: 50%;
      // background: #165DFF;
    }

    .boy-class {
      background: #165dff;
      border-radius: 50%;
    }

    .girl-class {
      background: #f77234;
    }
  }

  .left-info {
    height: 0.88rem;

    .nickname {
      display: flex;
      align-items: center;
      span {
        max-width: 2.56rem;
        font-size: 0.32rem;
        font-weight: 700;
        white-space: nowrap;
        text-overflow: ellipsis;
        overflow: hidden;
      }
      /deep/ .van-rate__item:not(:last-child) {
        padding-right: 0.04rem;
      }
    }

    .info {
      display: flex;
      align-items: center;
      font-size: 0.24rem;
      margin-top: 0.08rem;
      color: #c9cdd4;
      span:nth-child(2) {
        width: 1px;
        height: 0.24rem;
        background-color: #86909c;
        margin: 0 0.16rem;
      }
    }
  }
}

.loadMore {
  height: calc(100vh - 1.68rem);
  overflow: hidden;
  /deep/ {
    .van-pull-refresh {
      height: calc(100vh - 1.68rem)!important;
      overflow: auto;
    }
  }
}

.userItem {
  padding: 0.32rem;
  margin: 0.24rem;
  display: flex;
  flex-direction: column;
  background: #ffffff;
  border-radius: 0.08rem;
  position: relative;
  .item-top {
    display: flex;
    align-items: center;
    gap: 0.16rem;
    margin-bottom: 0.24rem;
    .item-top-left-avatar {
      width: 0.64rem;
      height: 0.64rem;
      flex-shrink: 0;
    }
    .item-top-left-name {
      font-size: 0.32rem;
      line-height: 0.44rem;
      color: #1d2129;
      font-weight: 500;
    }
  }
  .item-bottom {
    display: flex;
    flex-direction: column;
    gap: 0.16rem;
    line-height: 0.32rem;
    font-size: 0.28rem;
    .item-bottom-content {
      display: flex;
      word-break: break-all;
      gap: 0.16rem;
    }
    .label {
      color: #86909c;
      width: 1.68rem;
      flex-shrink: 0;
    }
    .value {
      color: #4e5969;
    }
  }
}
</style>