uni-app/小程序自定义导航栏和下拉刷新完美解决方案(封装原生page滑动组件)

·  阅读 2938

本组件优势:

1、使用小程序/uniapp原生page滑动,流畅度高于scroll-view组件
2、采用组件方式直接使用,只需在下拉刷新上拉加载加载完成时触发组件方法即可
3、包含无数据时空布局展示
4、可自定义下拉刷新上拉加载样式
5、采用组件的双向绑定v-model
6、完美解决自定义导航后看不到下拉刷新loading的问题

实际效果图


我们在page.json中开启了自定义导航栏属性和下拉刷新属性后

// 开启下拉刷新
"enablePullDownRefresh": true
// 自定义导航栏
"navigationStyle": "custom"
复制代码

此时,页面中的下拉刷新三个小圆点会被我们的导航栏遮盖住,导致用户下拉刷新看不到loading效果,如下图
这样用户体验就不好了,接下来我们看看怎么解决:
1、封装mscroll组件
2、封装组件内下拉刷新上拉加载加载完成时方法
3、利用margin-top: -100upx;在用户下拉刷新之前隐藏我们写的loading

封装mscroll组件

<template>
  <div class="mscroll">
    <!-- 下拉刷新 -->
    <div class="loading pullLoading">
      <i class="dot"></i>
    </div>
    <!-- 内容插槽 -->
    <slot/>
    <!-- 上拉加载 -->
    <div v-if="hasNextPage" class="loading">
      <i class="dot"></i>
    </div>
    <!-- 空布局 -->
    <div class="empty" v-if='isEmpty'>
      <!-- 自己更换图片哟 -->
      <img mode='widthFix' src="../../assets/image/logoPng.png" alt="">
      <p>这里什么都没有呀~</p>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    value: {
      type: Object,
      required: true,
      default () {
        return {
          // 当前页码
          page: 1,
          // 每页条数
          pageSize: 10,
          // 是否有下一页
          hasNextPage: false,
          // 数据总数
          total: 0
        }
      }
    }
  },
  data () {
    return {
      // 下一页
      hasNextPage: false,
      // 无数据
      isEmpty: false,
      // 加载中
      isLoading: false
    }
  },
  computed: {
    // 转换一下pages
    pages () {
      return this.value
    }
  },
  methods: {
    // 下拉刷新
    pullRefresh () {
      // 加载中
      this.isLoading = true
      this.$emit('input', {...this.pages, page: 1})
      this.$emit('getData')
    },
    // 上拉加载
    loadMore () {
      // 无下一页或加载中不加载
      if (!this.hasNextPage || this.isLoading) return
      // 加载中
      this.isLoading = true
      this.$emit('input', {...this.pages, page: this.pages.page + 1})
      this.$emit('getData')
    },
    // 加载成功方法
    loadSuccess (data) {
      // 第一页要回到顶部
      if (data.page == 1) {
        uni.pageScrollTo({
          scrollTop: 0
        })
      }
      // 结束下拉刷新
      uni.stopPullDownRefresh()
      // 关闭加载中
      this.isLoading = false
      // 是否有下一页(可根据总页数和当前页数判断)
      this.hasNextPage = data.hasNextPage
      // 是否有数据
      this.isEmpty = !data.total
    }
  }
}
</script>

<style lang='scss' scoped>
  // 加载中动画
  @keyframes dotFlashing {
    0% {
      background-color: #ccc;
    }
    100% {
      background-color: #999;
    }
  }
  .mscroll{
    // loading
    .loading {
      display: flex;
      justify-content: center;
      align-items: center;
      height: 100upx;
      overflow: hidden;
      &.pullLoading{
        // 这里是关键
        margin-top: -100upx;
      }
      // 模拟微信小圆点
      .dot {
        display: inline-block;
        position: relative;
        width: 14upx;
        height: 14upx;
        border-radius: 50%;
        background: #999;
        animation: dotFlashing 1s infinite linear alternate;
        animation-delay: .5s;
        &::before {
          left: -28upx;
          animation: dotFlashing 1s infinite alternate;
          animation-delay: 0s;
        }
        &::after {
          left: 28upx;
          animation: dotFlashing 1s infinite alternate;
          animation-delay: 1s;
        }
        &::before,
        &::after {
          content: '';
          display: inline-block;
          position: absolute;
          top: 0;
          width: 14upx;
          height: 14upx;
          border-radius: 50%;
          background: #999;
        }
      }
    }
    // 空布局
    .empty{
      margin-top: 280upx;
      text-align: center;
      img{
        width: 180upx;
      }
      p{
        margin-top: 20upx;
        color: #999;
        font-size: 26upx
      }
    }
  }
</style>
复制代码

使用方式

page.json配置

"pages": [
  {
    "path": "pages/homePage/index",
    "style": {
      "navigationBarTitleText": "页面标题", // 页面标题
	  "enablePullDownRefresh": true, // 开启下拉刷新(必需)
      "backgroundTextStyle": "light", // 下拉刷新loading小圆点颜色,白底+白色小圆点可以实现“隐藏”原生小圆点哦
      "navigationStyle": "custom", // 自定义导航栏(不自定义导航栏也可以使用本组件哦,只不过就只能使用原生小圆点样式了,无法自定义下拉刷新样式)
      "backgroundColor": "#F5F7F9" // 页面背景底色
    }
  }
]
复制代码

父组件使用

<template>
                              <!-- 计算出你的导航高度 -->
  <div class='index' :style="{'padding-top': '100px'}">
    <!-- 自定义导航栏 -->
    <nav :style="{height: '100px'}"></nav>
    <!-- 列表(pages必传,getData是加载方法) -->
    <m-scroll ref='mscroll' v-model="pages" @getData='getData'>
      <!-- 列表数据 -->
      <ul>
        <li v-for="i in list" :key="i">模拟数据 ------------- 第{{i + 1}}条</li>
      </ul>
    </m-scroll>
  </div>
</template>

<script>
import mScroll from './mscroll'
export default {
  components: {
    mScroll
  },
  data () {
    return {
      list: 20,
      pages: {
        page: 1,
        pageSize: 10
      }
    }
  },
  // 触发下拉刷新
  onPullDownRefresh () {
    this.$refs.mscroll.pullRefresh()
  },
  // 触发上拉加载
  onReachBottom () {
    this.$refs.mscroll.loadMore()
  },
  onLoad () {
    this.getData()
  },
  methods: {
    getData () {
      // 模拟请求
      setTimeout(() => {
        this.list += 10
        const total = 50
        const hasNextPage = total != this.list
        const data = {
          page: this.pages.page, // 需包含页码
          total, // 需包含总数
          hasNextPage // 需包含是否下一页
        }
        // 触发加载成功,需包含当前页码、数据总数、是否有下一页
        this.$refs.mscroll.loadSuccess(data)
      }, 1000)
    }
  }
}
</script>

<style lang='scss' scoped>
  // 自定义导航栏
  nav{
    background: #446AAD;
    position: fixed;
    z-index: 10;
    left: 0;
    top: 0;
    right: 0;
  }
  ul{
    li{
      padding: 20upx;
      border-bottom: 1px solid #eee;
    }
  }
</style>
复制代码

至此,代码就写完啦,考虑不周或者有bug的地方,还望多多留言告知我哟😁!

分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改