更新记录

1.0.0(2023-09-07)

自定义tab导航栏,支持多种样式,支持h5,小程序,App


平台兼容性

Vue2 Vue3
×
App 快应用 微信小程序 支付宝小程序 百度小程序 字节小程序 QQ小程序
HBuilderX 3.1.0 app-vue × × × × ×
钉钉小程序 快手小程序 飞书小程序 京东小程序
× × × ×
H5-Safari Android Browser 微信浏览器(Android) QQ浏览器(Android) Chrome IE Edge Firefox PC-Safari

unq-tabs

 <!-- tabs栏 -->
 <!-- 传入 :config="{textColor:'#00ff00'}" 能定制相应的主题 -->
 <unq-tabs ref="unqTabsRefs" :tabData="tabData" :defaultIndex="currentIndex" @tabsClick="tabsClick" />

 <!-- list视图 -->
 <!-- 
        5.让list 具备左右切换的能力
            5.1:思考如何让 list 拥有左右切换的能力
                 解决:使用Swiper对其进行改造
            5.2:问题1 - list 列表的高度展示错误
                 原因:没有为Swiper指定高度
                 解决:计算出每个item的高度,然后叠加到一起
            5.3:问题2-切换tab时,list的卡顿问题
                 原因:Swiper 动画未完成时,就获取数据 渲染DOM
                 解决:Swiper 动画完成之后,获取数据,渲染DOM
        6.完成list 与 tabs联动的能力
            6.1 tabs 切换时,Swiper 联动切换
            6.2 Swiper 切换时,tabs 联动切换
     -->
 <!-- 
        功能补充:
            1.tabs 具备吸顶的效果
            2.控制列表滚动位置
                2.1 当用户滚动页面之后
                2.2 切换 tabs 时,让页面的滚动位置返回为 tabs吸顶的位置

      -->
 <!-- current 表示当前展示哪个 swiperItem  @animationfinish 动画结束时会触发 animationfinish 事件, @change    current 改变时会触发 change 事件-->
 <swiper
   class="swiper"
   :style="{ height: swiperHeightData[currentIndex] + 'px' }"
   :current="currentIndex"
   @animationfinish="onSwiperEnd"
   @change="omSwiperChange"
 >
   <!-- swiperItem 的数量,需要由 tabData进行决定 -->
   <swiper-item class="swiper-item" v-for="(tabitem, tabindex) in tabData" :key="tabindex">
     <!-- {{tabindex}} 就是currentIndex -->
     <view>
       <!-- Loading -->
       <uni-load-more status="loading" v-if="isLoading"></uni-load-more>
       <!-- 列表 -->
       <block v-else>
        <!-- todo 列表展示项 -->
       </block>
     </view>
   </swiper-item>
 </swiper>
export default {
    data(){
        return {
            tabData: [
                {
                    label:"热门",
                    value:'1',
                },
                {
                    label:"男装",
                    value:"2"
                },
                {
                    label:"女装",
                    value:"3"
                },
                {
                    label:"童装",
                    value:"4"
                },
            ], // tabs列表类型
            currentIndex: 0, // 默认tabs激活项
            swiperHeightData: {}, // 缓存高度的计算结果。以index 为key,以对应的高度 为value
        }
    },
    methods:{
        // 请求热搜文章列表
        async getHotList() {
          /**
           * 做数据缓存,请求过的数据不在请求
           * 1. 没有获取过数据
           * 1.1 展示loading
           * 1.2 调用接口获取数据
           * 1.3 把数据保存到本地
           * 1.4 隐藏loading
           *
           * 2. 已经获取过数据(有了缓存之后)
           * 2.1 直接渲染数据就可以了
           * */
          if (!this.listData[this.currentIndex]) {
            // 判断当前listData[key]有没有值,有则已请求过数据
            // 1
            // 1.1
            this.isLoading = true
            // 1.2
            const id = this.tabData[this.currentIndex].id
            const { list } = await _getHotList(id)
            // 1.3
            this.listData[this.currentIndex] = list // 当前激活项做listData的key添加对应的list文章数据
            // 1.4
            this.isLoading = false
            // console.log(this.listData)
            // 渲染完成数据之后,计算Swiper高度
            if (!this.swiperHeightData[this.currentIndex]) {
              // 判断缓存高度
              setTimeout(async () => {
                this.currentSwiperHeight = await this.getCurrentSwiperHeight()
                this.swiperHeightData[this.currentIndex] = this.currentSwiperHeight
                /* #ifdef H5 */
                this.$forceUpdate() // 解决H5中高度问题
                /* #endif */
                console.log(this.swiperHeightData[this.currentIndex])
              }, 0)
            }
          }
        },
        /**
         * 计算当前 Swiper高度
         * */
        getCurrentSwiperHeight() {
          return new Promise((resolve, reject) => {
            // 1.拿到所有的 item -> 异步
            let sum = 0
            const query = uni.createSelectorQuery().in(this)
            query
              .selectAll(`.hot-list-item${this.currentIndex}`)
              .boundingClientRect(res => {
                // 2.拿到所有 item 的高度
                // 3.把所有的高度累加
                console.log(res)
                res.forEach((item, index) => {
                  sum += item.height
                })
                resolve(sum)
              })
              .exec()
          })
        },
        // tabs激活项
        tabsClick(index) {
          // 保存tabs激活项
          this.currentIndex = index
          // this.getHotList() 点击切换时在Swiper动画完成时再调用请求数据进行渲染
        },
        /**
         * 判断动画是否完成,执行渲染数据
         * 让动画Swiper执行完成后再执行渲染数据
         * */
        onSwiperEnd() {
          this.getHotList()
          // console.log('swiper动画完成')
        },
        /**
         * Swiper 切换时,tabs 联动切换
         * */
        omSwiperChange({ detail }) {
          if (this.currentPageScrollTop > 125) {
            uni.pageScrollTo({
              scrollTop: 125,
              duration: 200 // 滚动动画的时长,默认300ms,单位 ms
            })
          }
          this.currentIndex = detail.current // 切换的激活项重新赋值

          // 父调用子的方法 同步slider滑动快
          setTimeout(() => {
            // props是异步的,解决currentIndex最新值还没传给子组件去计算最新的slider left值
            this.$refs.unqTabsRefs.tobToIndex()
          }, 0)

          // console.log('切换了',this.$refs.unqTabsRefs.activeIndex)
        },
    }
}

隐私、权限声明

1. 本插件需要申请的系统权限列表:

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集任何数据

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

许可协议

MIT协议

使用中有什么不明白的地方,就向插件作者提问吧~ 我要提问