vue3+ts+vant 下拉分页加载列表

·  阅读 43

转自 github.com/Sujb-sus/vu…

<script setup lang="ts">
import base from '@/utils/base'
import NoData from '@/components/noData'
import SvgIcon from '@/components/svgIcon'
import { apiGetBlogList, apiUpdateLikes } from '@/api/blog'
import { formatTime, formatNumber } from '@/filters/index'
import useClickLike from '@/useMixin/useClickLike'
import useGetLabelColor from '@/useMixin/useGetLabelColor'
import { ref, watch } from 'vue'
import { useRouter } from 'vue-router'
import { articleModel } from '@/models/index'
interface Props {
  showTitle?: boolean
  params?: object
}
// 定义props默认值
const props = withDefaults(defineProps<Props>(), {
  showTitle: true,
  params: undefined,
})
const { getLikesNumber, getLikesColor, handleLikes, likeList } =
  useClickLike(apiUpdateLikes)
const { getLabelColor } = useGetLabelColor()
const route = useRouter()
let loading = ref(false)
let finished = ref(false)
let refreshing = ref(false)
let hasLoad = ref(false)
let pageindex = ref(1)
let pagesize = ref(10)
let total = ref(0)
let list = ref<Array<articleModel>>([])
// 监听label页传下来的参数
if (props.params) {
  watch(props.params, () => {
    pageindex.value = 1
    hasLoad.value = false
    loading.value = false
    finished.value = false
    list.value = []
    likeList.value = []
    getBlogList()
  })
}
// 获取文章列表
const getBlogList = (reload = true) => {
  reload && base.showLoading()
  return apiGetBlogList({
    pageindex: pageindex.value,
    pagesize: pagesize.value,
    ...props.params,
  })
    .then((res) => {
      list.value = [...list.value, ...res?.data?.list]
      total.value = res?.data?.total
      finished.value = pageindex.value * pagesize.value >= total.value
    })
    .catch((err) => console.log(err))
    .finally(() => {
      hasLoad.value = true
      loading.value = false
      refreshing.value = false
      reload && base.hideLoading()
    })
}
getBlogList()
// 下拉刷新
const handleRefresh = () => {
  pageindex.value = 1
  list.value = []
  likeList.value = []
  getBlogList()
}
// 滚动加载
const handleLoad = () => {
  if (!refreshing.value) {
    pageindex.value++
    getBlogList(false)
  }
}
// 去详情页
const gotoDetail = (id: string) => {
  route.push({ path: '/article/detail', query: { id } })
}
</script>

<template>
  <div
    :class="{ 'list-container': true, 'list-container__pt': !props.showTitle }"
    v-if="hasLoad"
  >
    <van-pull-refresh
      v-model="refreshing"
      @refresh="handleRefresh"
      v-if="total"
    >
      <div class="list-title" v-if="props.showTitle">
        <SvgIcon name="icon-label01"></SvgIcon>
        <span>最新文章({{ total }})</span>
      </div>
      <van-list
        v-model:loading="loading"
        :finished="finished"
        :immediate-check="false"
        :finished-text="total > 10 ? '--我是有底线的--' : ''"
        @load="handleLoad"
      >
        <div
          class="list-item"
          v-for="item in list"
          :key="item._id"
          @click.stop="gotoDetail(item._id)"
        >
          <div class="item-content">
            <img :src="item.fileCoverImgUrl" />
            <div class="content-box">
              <div class="content-top">
                <div class="content-title">{{ item.title }}</div>
                <div class="content-desc">{{ item.desc }}</div>
              </div>
              <div class="content-label">
                <div
                  class="label-text"
                  :style="{ backgroundColor: getLabelColor(label) }"
                  v-for="label in item.type"
                  :key="label"
                >
                  {{ label }}
                </div>
              </div>
            </div>
          </div>
          <div class="item-footer">
            <div class="footer-item">
              <SvgIcon name="icon-date02"></SvgIcon>
              <div class="footer-text">
                {{ formatTime(item.releaseTime, 'yyyy-MM-dd') }}
              </div>
            </div>
            <div class="footer-item">
              <SvgIcon name="icon-browse02"></SvgIcon>
              <div class="footer-text">{{ formatNumber(item.pv) }}</div>
            </div>
            <div
              class="footer-item"
              :class="{ 'icon-likes': getLikesColor(item._id) }"
              @click.stop="handleLikes(item._id)"
            >
              <SvgIcon name="icon-like02"></SvgIcon>
              <div class="footer-text">
                {{ formatNumber(getLikesNumber(item._id, item.likes)) }}
              </div>
            </div>
          </div>
        </div>
      </van-list>
    </van-pull-refresh>
    <NoData v-else></NoData>
  </div>
</template>

<style lang="scss" scoped>
.list-container__pt {
  padding-top: 137px;
}
.list-container {
  .van-pull-refresh {
    min-height: calc(100vh - 137px);
  }
  .list-title {
    padding: 12px 0 8px 16px;
    display: flex;
    align-items: center;
    :deep(.icon) {
      font-size: 18px;
    }
    span {
      font-size: 16px;
      color: $strongFontColor;
      margin-left: 4px;
    }
    position: relative;
    &::after {
      @include borderZeroPointFive(16px, 0, calc(100vw - 32px));
    }
  }
  .list-item {
    padding: 10px 16px;
    position: relative;
    &::after {
      @include borderZeroPointFive(16px, 0, calc(100vw - 32px));
    }
    .item-content {
      display: flex;
      img {
        width: 80px;
        height: 80px;
        margin-right: 8px;
        border-radius: 4px;
        object-fit: cover;
      }
      .content-box {
        flex: 1;
        display: flex;
        flex-direction: column;
        justify-content: space-between;
        .content-top {
          .content-title {
            color: $strongFontColor;
            font-size: 16px;
            @include singleText();
          }
          .content-desc {
            color: $subFontColor;
            font-size: 12px;
            margin: 4px 0;
            @include multiText(2);
          }
        }
        .content-label {
          @include flex(flex-start);
          @include singleText();
          .label-text {
            margin-right: 6px;
            font-size: 12px;
            color: #fff;
            border-radius: 12px;
            padding: 2px 4px;
          }
        }
      }
    }
    .item-footer {
      @include flex(flex-start);
      margin-top: 8px;
      .footer-item {
        @include flex();
        min-width: 80px;
        &:first-child {
          margin-left: 0;
        }
        &:last-child {
          display: flex;
          justify-content: flex-start;
        }
        .footer-text {
          font-size: 12px;
          color: $subFontColor;
          margin-left: 6px;
        }
      }
      .icon-likes {
        color: $warningColor;
        .footer-text {
          color: $warningColor;
        }
      }
    }
  }
}
</style>
复制代码
分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改