更新记录

3.2(2021-04-09)

  • 修复 vShow模式 下拉刷新不生效问题

  • 优化 config.js 判断安卓性能为判断安卓版本号是否大于8

3.1(2020-07-03)

  • 修复 因在wxs使用了es6语法而导致可能报错的问题

3.0(2020-06-28)

  • 修复 使用多个该组件时,下拉刷新相关变量共享导致各个组件之间刷新状态问题

查看更多

平台兼容性

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

该组件不过是实现了tabs线条跟随手指移动效果,若无此需求请不要使用该组件!

终极详解!终极详解!看不懂请弃用该组件,及时止损!

android端若性能不足则建议使用QS-tabs-list Nvue版

  • 请先下载示例项目体验,页面右上角蓝色按钮

  • 若觉得复杂不会用请先加群交流不要盲目主观下定论

  • 愿君学长松,慎勿作桃李。受屈不改心,然后知君子。

  • QQ交流群: 750104037 点我加入

注意: 目前若直接引入组件到项目中会报错, 因为方便示例演示, 在组件目录/components/QS-tabs-wxs-template-def.vue中的script标签最前面有引入示例用的js

快速指引

简介

示例项目结构

支持度

使用须知

Attributes

config.js(v2.3+)

Events

Methods

使用步骤

分页加载概述

组件结构图

获取分页数据原理图

终极详解

简介

  • 使用wxs实现性能更好的tab线条滑动效果和下拉刷新效果, 并使用swiper嵌套scrollview的方式集成了list方便开发分类列表业务需求, 同时简化分页加载方式

示例项目结构

|-- QS-tabs-wxs-list
    |-- App.vue
    |-- main.js
    |-- manifest.json
    |-- pages.json
    |-- README.md   //自述文档
    |-- components
    |   |-- QS-tabs-wxs-list    //组件文件夹
    |       |-- QS-tabs-wxs-list-refresh.vue    //下拉刷新模板
    |       |-- QS-tabs-wxs-list.vue    //组件核心vue文件
    |       |-- components  //组件中应用的组件文件,可自定义多个以便实现业务需求
    |       |   |-- QS-tabs-wxs-template-def.vue    //示例列表模板
    |       |-- css
    |       |   |-- QS-refresh-image-isrefreshing.css
    |       |   |-- QS-tabs-wxs-list-components.css
    |       |   |-- QS-tabs-wxs-list.css
    |       |-- js
    |       |   |-- config.js   //v2.3新增checkContentMode、checkvShowDuration方法
    |       |   |-- pageDemand.js   //分页加载js
    |       |   |-- publicProps.js
    |       |-- mixins
    |       |   |-- QS-tabs-wxs-list-mixin.js   //子组件必须实现的mixin
    |       |-- wxs
    |           |-- QS-refresh-wxs.wxs  //下拉刷新实现的wxs
    |           |-- QS-tabs-wxs.wxs //滑动线条实现的wxs
    |-- pages
    |   |-- default
    |   |   |-- default.vue
    |   |-- index
    |   |   |-- index.vue
    |   |-- more
    |   |   |-- more.vue
    |   |-- reset
    |   |   |-- reset.vue
    |   |-- select
    |   |   |-- select.vue
    |   |-- setColor
    |       |-- setColor.vue
    |-- static
    |   |-- logo.png
    |   |-- refresh.png
    |-- util    //示例项目所需的js
        |-- getTabList.js   //获取tabs对应的list内容的js
        |-- request //封装uni.request
            |-- app.js
            |-- interfaces.js
            |-- mock.js //本地模拟返回list数据
            |-- QS-request.js

支持度

  • 因wxs支持度原因,目前支持: APP(vue)、微信小程序、H5、QQ小程序

使用须知

  • tabs数组不从组件传入, 而是使用ref调用setTabs方法设置tabs, 所以setTabs为组件必须调用方法, tabs可传参数详见tabs参数详解

  • 正式使用前请务必详细阅读pageDemand.js分页加载实现代码, 以便对接自己的接口, 否则需自行实现分页逻辑

  • 该组件的列表内容样式展示取决于引入的模板(示例中为QS-tabs-wxs-template-def.vue), 开发者可以自行增加不同的模板文件并由外部传自定义标识type决定内部展示哪个模板

  • 需要开发者自行计算该组件的高度, 并传入属性height中, 单位px

  • 组件内必须拥有初始调用函数, 若组件内的初始调用函数名称不为init, 必须传入initFnName属性指定初始调用函数名称

  • 请不要在列表中使用video、textarea等原生组件

  • 若开启下拉刷新 请确保组件高度是占满屏幕或者使用下拉刷新模板QS-tabs-wxs-list-refresh.vue

  • 安卓H5网页下拉冲突, 可加入下面的代码

    // #ifdef H5
    document.body.addEventListener('touchmove', function (e) {
    e.preventDefault(); 
    }, {passive: false}); 
    // #endif

Attributes

    tabsPosition: { //tabs位置 top: 上, bottom: 下
        type: String,
        default: 'top'
    },
    minWidth: { //tab最小宽度
        type: String,
        default: '250rpx'
    },
    space: { //tab间距, 左右padding值
        type: String,
        default: '10px'
    },
    tabHeight: { //tabs高度
        type: String,
        default: '44px'
    },
    height: { //组件总高度, 需外部计算并传入
        type: [Number, String],
        default: 500
    },
    lineWidth: { //线条宽度,若小于1则当做百分比计算
        type: [Number, String],
        default: .7
    },
    lineHieght: { //线条高度
        type: String,
        default: '2px'
    },
    lineColor: { //线条颜色
        type: String,
        default: '#f1505c'
    },
    lineMarginBottom: { //线条距离底部距离
        type: [Number, String],
        default: 0
    },
    defCurrent: { //默认当前项
        type: [Number, String],
        default: 0
    },
    autoCenter: { //scrollview自动居中
        type: [Boolean, String],
        default: true
    },
    tapTabRefresh: { //点击当前项tab触发组件内部init函数
        type: [Boolean, String],
        default: true
    },
    fontSize: { //tab默认字体大小
        type: String,
        default: '28rpx'
    },
    activeFontSize: { //当前项字体大小
        type: String,
        default: '32rpx'
    },
    swiperBackgroundColor: { //swiper背景颜色
        type: String,
        default: '#f8f8f8'
    },
    tabsBackgroundColor: { //tabs背景颜色
        type: String,
        default: '#fff'
    },
    tabsFontColor: { //tabs默认字体颜色
        type: String,
        default: '#999'
    },
    activeFontColor: { //tabs当前项字体颜色
        type: String,
        default: '#000'
    },
    activeBold: { //当前项字体加粗
        type: [Boolean, String],
        default: true
    },
    initFnName: { //初始调用函数名称(组件内部)
        type: String,
        default: 'init'
    },
    type: { //用于区分展示不同列表模板的标识
        type: String,
        default: 'default'
    },
    zIndex: { //z-index属性值
        type: [String, Number],
        default: 99
    },
    tabsSticky: { //tabs是否sticky定位(粘贴组件顶部)
        type: [Boolean, String],
        default: false
    },
    hasRefresh: {
        type: [Boolean, String],
        default: false
    },
    refreshImage: { //刷新动画图片
        type: String,
        default: ''
    },
    refreshDistance: { //刷新距离
        type: [Number, String],
        default: 90
    },
    beforRefreshText: { //刷新前文字
        type: String,
        default: '下拉刷新'
    },
    releaseRefreshText: { //释放刷新文字
        type: String,
        default: '松开刷新'
    },
    isRefreshingText: { //刷新中文字
        type: String,
        default: '刷新中...'
    },
    successRefreshText: {//刷新成功
        type: String,
        default: '刷新成功'
    },
    failRefreshText: {//刷新失败
        type: String,
        default: '刷新失败'
    },
    completeRefreshText: { //刷新完成文字
        type: String,
        default: '刷新完毕'
    },
    refreshTextColor: { //刷新文字的颜色
        type: String,
        default: '#666'
    },
    refreshTextFontSize: { //刷新文字的大小
        type: String,
        default: '25rpx'
    },
    restrictSlider: {   //限制用户只能滑动一块
        type: [Boolean, String],
        default: true
    },
    contentMode: {  //content展示模式, default、swiper、vShow, 若传default, 则会根据config.js中的checkContentMode返回相应模式, 安卓平台下传default或swiper都会经过checkContentMode的检测
        type: String,
        default: 'default'
    },
    customData: {   //自定义数据,最终传至列表vue中 v2.9+
        type: [Object, String, Number, Array, Boolean],
        default: ()=>{ return {} }
    }

config.js(v2.3+)

  • config.js位于QS-tabs-wxs-list/js/config.js, 在v2.3中新增了两个方法

    方法名称 形参 说明
    checkContentMode 安卓平台下, 当contentMode为default或swiper时, 进一步判断当前机型性能是否可以开启swiper模式
    checkvShowDuration (当前平台不支持wxs时)安卓平台下, 当contentMode最终为vShow时, 进一步配置当前机型的下拉刷新过渡时间值

Events

事件名称 形参 说明
change v2.9+ current 内部current改变时触发
disabledTap 组件disabled为true时,组件区域点击时触发
disabledTouchmove 组件disabled为true时,组件区域滑动时触发

Methods

方法名 返回值 传入参数 说明
setTabs tabs 详见tabs参数详解 设置tabs
setDisabled Boolean 设置组件是否可以被点击和滑动
setCustomData v2.9+ Object/Array/String/Number/Boolean 设置自定义数据, 该数据可在list vue中获取

tabs参数详解

  • 注: tabs由组件ref实例调用setTabs方法设置

    |tabs Array
    |----String
    |--------tab名称
    |----Object
    |--------name: tab名称
    |--------activeFontColor: 当前项文字颜色
    |--------tabsBackgroundColor: 当前项tabs背景颜色
    |--------lineColor: 当前项线条颜色
    |--------swiperBackgroundColor: 当前项swiper背景颜色
    |--------swiperItemBackgroundColor: 当前项swiper-item背景颜色

使用步骤

Step 1:

  • 引入并注册组件

<script>
//页面中引入组件实例
import QSTabsWxsList from '组件路径';
export default {
    components: {
        QSTabsWxsList //注册组件
    }
}
</script>

Step 2:

  • template标签内写入并绑定ref与动态高度

<template>
    <view>
        <!-- 使用组件并绑定ref, 动态绑定height属性 -->
        <QSTabsWxsList ref="QSTabsWxsList" :height="QSTabsWxsListHeight"></QSTabsWxsList>
    </view>
</template>

Step 3:

  • 实现计算组件高度方法

<script>
//页面中引入组件实例
import QSTabsWxsList from '组件路径';
export default {
    components: {
        QSTabsWxsList //注册组件
    },
    data() {
        return {
            QSTabsWxsListHeight: 0  //组件高度
        }
    },
    onReady() {
        //执行计算组件高度方法
        this.countQSTabsWxsListHeight();
    },
    methods: {
        //计算组件高度
        countQSTabsWxsListHeight() {
            //...
            this.QSTabsWxsListHeight = xxx;
        }
    }
}
</script>

Step 4:

  • 设置tabs

//页面中引入组件实例
import QSTabsWxsList from '组件路径';
export default {
    components: {
        QSTabsWxsList //注册组件
    },
    data() {
        return {
            QSTabsWxsListHeight: 0  //组件高度
        }
    },
    onReady() {
        //执行计算组件高度方法
        this.countQSTabsWxsListHeight();
    },
    methods: {
        //计算组件高度
        countQSTabsWxsListHeight() {
            //...
            this.QSTabsWxsListHeight = xxx;
        },
        setTabs() {
            //接口获取tabs数组,使用ref调用setTabs方法传入
            //...获取tabs
            this.$refs.QSTabsWxsList.setTabs(tabs);
        }
    }
}
</script>

Step 5:

  • 后续需要详细看示例列表获取数据与分页加载的实现-分页加载概述

分页加载概述

在components/QS-tabs-wxs-list/js/pageDemand.js文件中抽离出了公共的分页加载逻辑, 开发者可以自行修改逻辑从而契合自己的分页加载逻辑, 该js文件中的doPageDemand方法由call方法绑定this指向实现, 并传入相应参数, 具体逻辑需查看源码注释

组件结构图

Image

获取分页数据原理图

Image

终极详解

首先,该组件的对接分为两部分视图数据

视图

我们先来看视图部分:
该组件的列表样式(或者说视图)是集中放在一个"池子"里面的, 位于 组件文件夹/components/components, 该文件夹下存放了列表的vue文件.
而所有的列表vue文件都由 组件文件夹/components/QS-tabs-wxs-list-template.vue分流,所谓的分流就是不同的页面使用不同的模板,甚至是不同的列也是用不同的模板.
因此你新创建的列表vue需要在模板分流组件内注册并由type属性控制不同的页面使用不同的列表模板.
也有小伙伴的需求是不同列也需要不同的模板, 那么这时候就可以使用mixins混入的index(当前tab下标)或者自己再tab中设置的自定义id来区分不同的模板.
SO 区分不同的列表模板都是在 组件文件夹/components/QS-tabs-wxs-list-template.vue 这个文件进行的.
列表的具体样式与数据的获取是在 组件文件夹/components/components/xxx.vue 下进行.

数据

我们接着来看数据部分的对接:
该组件在示例中已经实现了从访问接口过滤数据再到分页数据的封装.
首先在示例项目根目录有一个util文件夹, 在里面我们放了request文件夹和获取列表数据的api js文件(getTabList.js 其实我们大可以把这里面的api整合在一个公共的apis.js里面).
getTabList.js中import './request/QS-request.js', QS-request.js导出对象中的QSRequest就是对uni.request的封装.
!!注意:QSRequest为了便于示例演示在其内使用的是mock方法, 并没有直接使用uni.request, 所以在正式使用时请删除使用mock的逻辑.
那么QSRequest传入的参数是:

{
    urlField,   //接口地址, 在interface.js导出的对象中使用 . 链式获取对应的接口地址
    sendData,   //传入uni.request的参数, 详细参数见uni.request api所传的参数, 这里有多出一个contentType属性,为了便于Content-Type的设置, 详见源码,当然其实这里我自己不是很满意,你大可可以自己修改
    checkType,  //健壮性判断的类型
    field,  //在健壮性判断通过后使用.链式获取想要的数据
    check,  //是否经使用壮性判断
    filterFn,   //代理函数, 在Promise.resolve之前会把field获取的数据传进该函数执行一次,最终resolve函数return的数据
    hasLoading  //是否显示交互loading
    ...//其实可以自己增加一些逻辑 像我自己增加了 login属性, 判断若未登录则跳转登录页面或者提示未登录,还有promiseProxyFn,可以对返回数据做一次异步的代理
}

QSRequest 接收上面的对象后会返回一个经过一系列处理的Promise对象, 所以在getTabList.js中是直接return了一个QSRequest所返回的Promise对象,而外部可以使用then方法或者 async await的方式获取数据.
到这里我们已经讲了访问接口过滤数据的过程, 那么在分页数据之前,我们需要做的是如下几点:

  • 1.删除mock逻辑(源码大概在QS-request.js 20-24行左右, 即是fn等于request方法即可)

  • 2.在util/interface.js中写好接口地址以便使用QSRequest中的urlField属性来获取

  • 3.因为QSRequest默认会对返回数据做一次健壮性判断,所以你应该修改QS-request.js中的checkRes方法来符合自己的业务需求

    假如我们已经把上面的问题都搞定了,那么geTabList.js这个api在外部调用时返回的应该就是我们想要的数据了, 那么我们接下来看分页数据.
    分页数据的js位于 组件文件夹/js/pageDemand.js, 该js文件是一个分页公共逻辑的封装,注意 抽离出的时公共的逻辑, 所以外部调用就是用Function.prototype.call方法来使用, 因为call方法能改变this指向, 所以我们只要在每个list的实例中使用call方法使用它,它this所指向的都是这个list vue的实例.
    我们所说的list vue实例 其实指的就是列表样式池(组件文件夹/components/components)里的vue文件, 那么我们看看示例代码:

    import { getTabList } from '@/util/getTabList.js';  //引入获取列表数据的api 就是我们前面说的 getTabList.js
    import { doPageDemand } from '../../js/pageDemand.js';  //引入分页js
    doPageDemand.call(_this, {  //传入this, 使pageDemand.js中的this指向使用他的vue实例
        getDataFn: getTabList, //传入我们引入的获取数据的方法
        successEnd() {  //获取数据成功后将要结束的回调
            if (refresh) _this.$emit('refreshEnd', true);   //触发父级事件, true表示刷新成功
        },
        fail() {
            console.log('访问接口失败');
            if (refresh) _this.$emit('refreshEnd', false);  //触发父级事件, false表示刷新失败
        }, //接口访问失败回调
    
        sendDataName: 'sendData', //携带数据字段名称
        pageNumName: 'pageNum', //携带数据中的页数字段名称
        pageSizeName: 'pageSize', //携带数据中的条数字段名称
    
        checkLastPageMode: false, //判断是否是最后一页的逻辑标识, 用于逻辑判断, 目前默认有两个参数 size: 判断条数, page: 判断页数, 默认为size
        newDatafields: 'list', //接口访问成功后获取列表数据字段名称, 可用. 链式获取
        dataLastPageName: false, //接口访问成功后数据中的最大页数字段名称, 可用. 链式获取
        sizeName: false, //接口访问成功后数据中条数字段名称, 可用. 链式获取
    
        setName: 'list', //页面中列表数据字段名称, 如果在页面中分别有两个或两个以上列表使用该js, 则页面中需区分传入, 否则可以忽略
        statusTextName: 'statusText', //页面中列表状态字段名称, 如果在页面中分别有两个或两个以上列表使用该js, 则页面中需区分传入, 否则可以忽略
        lastPageName: false, //页面中最后一页字段名称, 如果在页面中分别有两个或两个以上列表使用该js, 则页面中需区分传入, 否则可以忽略
        waitingName: false, //页面中获取数据等待字段名称, 如果在页面中分别有两个或两个以上列表使用该js, 则页面中需区分传入, 否则可以忽略
    
        refresh, //刷新标识, 若为true则会将携带数据中的页数重置为1
        force, //强制标识, 若为true则会忽略等待标识为true时的跳过操作
        doEvent, //进入状态判断标识, 若为true则会进入判断列表status而进行相应操作
    
        noDataText: false, //访问接口后若数据长度为0则可自定义为空时文字
    
        refreshClear: false, //刷新时是否清空数据
    })

那么pageDemand.js 的具体逻辑还需要看源码, 他的大概步骤如下:

  • 1.判断是否已经是最后一页, 若是则直接return

  • 2.执行从外面传入的getDataFn函数(就是我们在getTabList.js中写的获取数据的那个函数), 该函数使用then方式获取了最终需要的数据

  • 3.对list vue实例data中的list属性赋值, 该操作就会影响视图变化

  • 3.判断并展示对应的列表状态

    list vue页面它data上绑定的应该有(比如示例):

    data() {
    return {
        list: [], //列表数据,
        sendData: {}, //访问接口时携带的数据,
        statusText: {}, //列表状态对象
    }
    }

    好了, 讲到这里应该就差不多了, 如果还有不明白的......请加QQ群.

隐私、权限声明

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

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

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

许可协议

MIT协议

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