uniapp-微信小程序实现swiper左右滚动切换tab,上下滚动加载列表

思路:左右滑动使用swiper,上下滑动用scroll-view,swiper改变时同时改变tab并更新列表

坑点:

1. swiper高度问题,导致滑动不到最底部和最顶部

        需要手动计算,减去顶部高度和底部tabbar,并且需要同时设置padding-top和paddin-botton,否则列表显示不完整

2. 由于最开始的代码是每次切换tab后都会重新请求,导致swiper还没切换成功的过程中,显示的列表错误

        将代码改造成每一个tab下面的list都是隔离开的,原先使用了一个list维护不同tab

3. 每个tab下的list的加载完成状态也需要隔离开,否则导致tab1切换到tab2以后,tab2将控制加载完成的状态改为true后,返回tab1无法加载第2页了

4. 触底加载

        无法使用系统的触底加载了,需要使用scroll-view的自定义上拉刷新方法

5. 下拉更新

        也需要自定义,由于目前我的系统自带的还是正常的,所以还没有改造,但是体验没有scroll-view的好,因为顶部tab会遮挡下拉刷新的loading状态,后期再改造

源码:已去除不相关代码

<template>
  <view class="home-page">
    <my-tabs :tabs="tabs" :currentTab="currentTabIndex" @change="onTabChange" :top="topStyle.total" isFixed>
    </my-tabs>
    <swiper class="activity-swiper" :current="currentTabIndex" interval="500" @change="onSwiperChange"
      :style="{ height: `calc(100vh - ${listStyle.paddingTop} - ${listStyle.paddingBottom})`, paddingBottom: listStyle.paddingBottom, paddingTop: listStyle.paddingTop,}">
      <swiper-item class="activity-swiper-item" v-for="item in tabs" :key="item.key" :item-id="item.key"
        :style="{ paddingBottom: 0, paddingTop: 0 }">
        <scroll-view scroll-y class="scroll-list" @scrolltolower="onBottom" :style="{ height: '100%' }"
          :scroll-top="scrollTop" @scroll="scroll">
          <card-activity v-for="(activity, index) in listData[item.index]" :key="activity.activityId" :data="activity"
            custom-class="activity-item" @click="toActivityDetail" @share="onShare"></card-activity>
          <tui-nomore v-if="isListFinished[item.index] && listData[item.index].length > 3"></tui-nomore>
        </scroll-view>
      </swiper-item>
    </swiper>
  </view>
</template>

<script>
  import {
    mapState
  } from 'vuex'
  import {
    userApi,
    activityApi
  } from '@/api'
  import {
    defaultConst,
    shareTypeConst,
  } from '@/const'
  const tabObj = {
    textList: [{
        name: '我创建的',
        key: 'create',
        index: 0
      },
      {
        name: '我参与的',
        key: 'partake',
        index: 1
      },
      {
        name: '我关注的',
        key: 'follow',
        index: 2
      }
    ],
    value: {
      0: 1,
      1: 2,
      2: 3,
    }
  }
  export default {
    data() {
      return {
        currentActivity: {},
        tabs: tabObj.textList,
        currentTabIndex: 0,
        currentTabValue: 1,
        listData: {
          0: [], // create
          1: [], // partake
          2: [], // follow
        },
        // 每个tab的加载状态隔离开:解决切换到第三个tab加载完成后,返回第一个tab无法加载第二页的问题
        isListFinished: [false, false, false],
        pageIndex: 1,
        isListLoading: false,

        tabbarIndex: 0,

        scrollTop: 0,
        oldScrollTop: 0
      }
    },
    computed: {
      ...mapState(['systemInfo', 'userInfo', 'showTabBarDot']),
      topStyle() {
        const {
          statusBarHeight
        } = this.systemInfo
        const menuButtonInfo = wx.getMenuButtonBoundingClientRect()
        const navigationBarHeight = (menuButtonInfo.top - statusBarHeight) * 2 + menuButtonInfo.height
        return {
          statusBarHeight,
          navigationBarHeight,
          total: statusBarHeight + navigationBarHeight
        }
      },
      listStyle() {
        return {
          paddingTop: this.topStyle.total + 45 + 'px',
          paddingBottom: '180rpx',
        }
      }
    },
    watch: {
      currentTabIndex(value) {
        // 首次切换tab才会重新加载数据
        if (!this.listData[value].length) this.getActivityList(true)
        // this.isListFinished = false
      },
    },
    methods: {
      async getActivityList(isRefresh, params, isFirst) {
        const tab = this.currentTabIndex
        this.showNoData = false

        if (isRefresh) {
          this.pageIndex = defaultConst.DEFAULT_PAGE_INDEX
          this.isListFinished[this.currentTabIndex] = false
          this.listData[this.currentTabIndex] = []
        }
        const data = {
          pageIndex: this.pageIndex++,
          pageSize: defaultConst.DEFAULT_PAGE_SIZE,
          type: this.currentTabValue,
        }
        Object.keys(params || {}).map(key => {
          if (params[key]) {
            data[key] = params[key]
          }
        })
        const {
          hasNext,
          list
        } = await activityApi.getList(data)
        if (tab !== this.currentTabIndex) return

        if (!hasNext) {
          this.isListFinished[this.currentTabIndex] = true
        }
        if (isRefresh) {
          uni.stopPullDownRefresh()
        }
        this.listData[this.currentTabIndex].push(...list)

      },
      onTabChange({
        index
      }) {
        this.currentTabIndex = index
        this.currentTabValue = tabObj.value[index]
        if (!this.listData[index].length) this.skeletonShow = true
        this.goTop()
      },
      onSwiperChange(e) {
        this.currentTabIndex = e.detail.current
        this.currentTabValue = tabObj.value[e.detail.current]
        if (!this.listData[e.detail.current].length) this.skeletonShow = true
        this.goTop()
      },
      onNoticeClose() {
        this.showNotice = false;
      },
      toActivityDetail(id) {
        uni.navigateTo({
          url: `/pages/activity/detail?activityId=${id}`
        })
      },
      async onShare(activity) {
        const res = await activityApi.getDetail({
          activityId: activity.activityId
        })
        this.currentActivity = res
        this.$refs.shareActivity.share()
      },
      toCreate() {
        uni.navigateTo({
          url: '/pages/activity/create'
        })
      },
      onBottom() {
        console.log('触底了')
        if (!this.isListFinished[this.currentTabIndex]) {
          this.getActivityList(false)
        }
      },
      scroll(e) {
        //记录scroll  位置
        this.oldScrollTop = e.detail.scrollTop
      },
      goTop(e) {
        //视图会发生重新渲染 scrollTop不会随着滚动而实时更新 所以需要手动设置 之所以不在scroll方法中设置是为了防止页面抖动
        this.scrollTop = this.oldScrollTop
        //当视图渲染结束 重新设置为0
        wx.nextTick(() => {
          this.scrollTop = 0
        });
      }
    },
 
    onLoad() {
      this.skeletonShow = true
      this.getActivityList(false)
    },

    onPullDownRefresh() {
      this.skeletonShow = true
      this.getActivityList(true)
    },
  }
</script>

<style lang="scss" scoped>
  .activity-swiper {

    &-item {
      box-sizing: border-box;
      padding-left: 20rpx;
      padding-right: 20rpx;
    }

    /deep/ .activity-item {
      margin-top: 20rpx;
    }
  }

  .text-link {
    color: $text-link;
  }

  .list-no-data-text {
    color: $text-3;
  }
</style>

  • 7
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
是的,可以使用swiper和scroll-view组件来实现微信小程序左右分页功能。 首先,在wxml文件中添加swiper和scroll-view组件,并设置相应的属性。 ```html <swiper class="swiper-container" current="{{current}}" duration="{{500}}" style="height:{{pageHeight}}px"> <swiper-item> <scroll-view class="scroll-view" scroll-x="{{true}}" scroll-y="{{false}}" style="width:{{windowWidth}}px;height:{{pageHeight}}px;"> <!--第一页内容--> </scroll-view> </swiper-item> <swiper-item> <scroll-view class="scroll-view" scroll-x="{{true}}" scroll-y="{{false}}" style="width:{{windowWidth}}px;height:{{pageHeight}}px;"> <!--第二页内容--> </scroll-view> </swiper-item> <!--更多swiper-item--> </swiper> ``` 其中,swiper组件设置了current属性,用于控制当前显示的页码;scroll-view组件设置了scroll-x属性,用于开启水平方向的滚动,并设置了样式宽度为屏幕宽度,高度为页面高度。 接着,在js文件中,监听swiper组件的change事件,用于动态更新当前页码。 ```javascript Page({ data: { current: 0, windowWidth: wx.getSystemInfoSync().windowWidth, pageHeight: wx.getSystemInfoSync().windowHeight }, onLoad: function () { //... }, onPageChange: function (e) { this.setData({ current: e.detail.current }) } }) ``` 最后,在样式文件中,设置swiper-container和scroll-view的样式,用于实现左右分页效果。 ```css .swiper-container { overflow: hidden; position: relative; } .scroll-view { white-space: nowrap; } .swiper-item { display: inline-block; vertical-align: top; width: 100%; } ``` 以上就是使用swiper和scroll-view组件实现微信小程序左右分页功能的方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值