更新记录

1.1.6(2024-09-29)

修复app/h5分页不可点击问题

1.1.5(2024-09-29)

修复h5问题

1.1.4(2024-09-29)

支持表头单元格高度和表格单元格高度设置

查看更多

平台兼容性

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

介绍

基于uni-app开发的一个普通的表格组件,功能有固定首列和表头、排序、操作按钮、 table 表格 无限分组表头、 固定表头、固定首列、多列 上拉加载更多、 排序、自适应列宽、多选checkbox、编辑、删除、按钮、合计 多页功能 已用于生产环境

遇到问题或有建议可以加入QQ群(455948571)反馈
如果觉得组件不错,给五星鼓励鼓励咯!

注意本插件依赖于scss的编译,如果没有使用scss请手动改源码去掉scss的语法方可使用。如果有疑问请加入加入QQ群(455948571)

注意

作者不介意你对组件源码进行改造使用,为了开源更加高效,谢谢你的配合;为了节省不必要的沟通浪费,以下情况请不要再反馈给作者,请自行解决;

在这感各位的理解,我支持开源,但是作者时间有限;谢谢各位的配合;在这里期望我写的小小插件能为你提供便捷;

1.如果你对源码进行了修改使用,请不需要对作者做任何的反馈,作者确实没有空陪你做技术分析解答;
2.如果你引入插件,连插件是否有正常被uniapp框架识别解析都不清楚,请你换个插件使用;
3.如果你引入插件,针对自己项目进行功能改造的,请自行仔细阅读源码并了解其原理,自行改造;这里作者不愿意浪费过多时间进行技术解答;

使用

(支持vue2/vue3 app 微信小程序 H5)

从uniapp插件市场导入

APP预览

appDemo安装包下载地址:android安装包;


功能预览(滚动表头完美同步) 动态分组表头/分页/排序/合计/fixed等功能

H5预览

动态分组表头/分页/排序/合计/fixed等功能

微信小程序预览

动态无限级分组表头/分页/排序/合计/固定列/下拉加载更多等功能

预览


功能预览 动态无限级分组表头/分页/排序/合计/固定列/下拉加载更多等功能
分页功能演示

示例demo(vue3 + ts)

<next-x-table
    :show-header="true"
    :columns="column"
    :stripe="true"
    :fit="false"
    show-summary
    sum-text="合计"
    @rowClick="rowClick"
    :summary-method="getSummaries"
    @toggleRowSelection="toggleRowSelection"
    @toggleAllSelection="toggleAllSelection"
    :border="true"
    @edit="buttonEdit"
    @dele="dele"
    :data="datalist"></next-x-table>
<script setup lang="ts">
import {ref, unref} from "vue"
const pageIndex = ref(1)
const pageTotal = ref(5)
const datalist = ref([])
const checkNameList = ref([])

function getdatalist(pageIndex) {
    const pageSize = 10
    const arr = []
    for(let i = pageSize*(pageIndex-1) + 1; i < pageSize*pageIndex; i++) {
        arr.push({
            date: '2023-06-23',
            name: `刘先生${i}`,
            province: '上海',
            sex: i%2 ? '0' : '1',
            disabled: i%2 ? true : false,
            checked: unref(checkNameList)[unref(pageIndex)] ? unref(checkNameList)[unref(pageIndex)].indexOf(`刘先生${i}`) !== -1 : false,
            age: 20,
            img: 'https://gss0.baidu.com/-Po3dSag_xI4khGko9WTAnF6hhy/zhidao/wh%3D450%2C600/sign=e58ae9feb1003af34defd464001aea6a/8601a18b87d6277f4d763bcf2f381f30e824fce5.jpg',
            city: '广州市',
            address: '天河区东圃镇2002号',
            zip: 200333
        })
    }
    datalist.value = arr
} 
function pageChange(index) {
    pageIndex.value = index
    getdatalist(unref(pageIndex))
}
function dele(item) {
    const index = unref(datalist).findIndex(it => it.name == item.name)
    if (index != -1) {
        unref(datalist).splice(index, 1)
    }
}
function toggleAllSelection(_, list) {
    unref(checkNameList)[unref(pageIndex)] = list.map(item => item.name)
}
function toggleRowSelection(bool, list) {
    unref(checkNameList)[unref(pageIndex)] = list.map(item => item.name)
}
function buttonEdit(item) {
    console.log(111111, item)
}
const column = ref([
    { type:'selection', fixed:true,width:60 },
    { name: 'name', label: '姓名',fixed:false,width:80,emptyString:'--' },
    { name: 'age', label: '年纪',sorter:false,align:'right', },
    { name: 'sex', label: '性别',filters:{'0':'男','1':'女'}},
    { name: 'img', label: '图片',type:"img" },
    { name: 'address', label: '地址' },
    { name: 'date', label: '日期',sorter:true },
    { name: 'province', label: '省份' },
    { name: 'city', label: '城市' },
    { name: 'zip', label: '邮编' },
    { name: 'operation', type:'operation',label: '操作',renders:[
        {
            name:'编辑',
            func:'edit' // func 代表子元素点击的事件 父元素接收的事件 父元素 @edit
        },
        {
            name:'删除',
            type:'warn',
            func:"dele"
        },
    ]},
])
getdatalist(unref(pageIndex))
</script>

多级表头示例demo,同样支持vue2(vue3 + ts)

<template>
    <view class="next-table-container">
        <next-x-table
            :show-header="true"
            :columns="column"
            :stripe="true"
            :fit="true"
            :show-summary="true"
            sum-text="合计"
            @rowClick="rowClick"
            :summary-method="getSummaries"
            @pageChange="pageChange"
            @toggleRowSelection="toggleRowSelection"
            @toggleAllSelection="toggleAllSelection"
            :pageIndex="pageIndex"
            :pageTotal="pageTotal"
            align="center"
            :showPaging="true"
            :border="true"
            :isShowLoadMore="false"
            :highlight="true"
            @edit="buttonEdit"
            @dele="dele"
            :pullUpLoading="pullUpLoading"
            :data="datalist" />
    </view>
</template>
<script setup lang="ts">
import {ref, unref, onMounted, nextTick } from "vue"
const pageIndex = ref(1)
const pageTotal = ref(5)
const datalist = ref([])
const checkNameList = ref([])
const tableWidth = ref(0)
const tableHeight = ref(0)
const pageSize = 30

function getdatalist(pageIndex) {
    const arr = []
    for(let i = pageSize*(pageIndex-1) + 1; i < pageSize*pageIndex; i++) {
        arr.push({
            date: '2023-06-23',
            name: `刘先生${i}`,
            province: '上海',
            sex: i%2 ? '0' : '1',
            other: "其他类型",
            remark: `备注-${i}`,
            disabled: i%2 ? true : false,
            checked: unref(checkNameList)[unref(pageIndex)] ? unref(checkNameList)[unref(pageIndex)].indexOf(`刘先生${i}`) !== -1 : false,
            age: 20,
            img: 'https://gss0.baidu.com/-Po3dSag_xI4khGko9WTAnF6hhy/zhidao/wh%3D450%2C600/sign=e58ae9feb1003af34defd464001aea6a/8601a18b87d6277f4d763bcf2f381f30e824fce5.jpg',
            city: '广州市',
            address: '天河区东圃镇2002号发撒锻炼腹肌爱的色放上帝发誓地方',
            zip: 200333
        })
    }
    if(pageIndex === 1) {
        datalist.value = arr
    } else {
        datalist.value = datalist.value.concat(arr)
    }
} 
function rowClick(e) {
    console.log('------rowClick------', e)
}
function _getdatalist(pageIndex) {
    const arr = []
    for(let i = pageSize*(pageIndex-1) + 1; i < pageSize*pageIndex; i++) {
        arr.push({
            date: '2023-06-23',
            name: `刘先生${i}`,
            province: '上海',
            sex: i%2 ? '0' : '1',
            other: "其他类型",
            remark: `备注-${i}`,
            disabled: i%2 ? true : false,
            checked: unref(checkNameList)[unref(pageIndex)] ? unref(checkNameList)[unref(pageIndex)].indexOf(`刘先生${i}`) !== -1 : false,
            age: 20,
            img: 'https://gss0.baidu.com/-Po3dSag_xI4khGko9WTAnF6hhy/zhidao/wh%3D450%2C600/sign=e58ae9feb1003af34defd464001aea6a/8601a18b87d6277f4d763bcf2f381f30e824fce5.jpg',
            city: '广州市',
            address: '天河区东圃镇2002号发撒锻炼腹肌爱的色放上帝发誓地方',
            zip: 200333
        })
    }
    datalist.value = arr
} 
function settingConf(type) {
    if(type === 1) {
        tableWidth.value = "50vw";
        tableHeight.value = "50vh";
    } else if(type === 2) {
        tableWidth.value = "80vw";
        tableHeight.value = "70vh";
    } else if(type === 3) {
        tableWidth.value = "100vw";
        tableHeight.value = "80vh";
    }
}
function pageChange(index) {
    console.log(1111111, index)
    pageIndex.value = index
    _getdatalist(unref(pageIndex))
}
function dele(item) {
    const index = unref(datalist).findIndex(it => it.name == item.name)
    if (index != -1) {
        unref(datalist).splice(index, 1)
    }
}
function toggleAllSelection(_, list) {
    unref(checkNameList)[unref(pageIndex)] = list.map(item => item.name)
}
function toggleRowSelection(bool, list) {
    unref(checkNameList)[unref(pageIndex)] = list.map(item => item.name)
}
function buttonEdit(item) {
    console.log(111111, item)
}
function pullUpLoading(callback) {
    console.log(1111111111111, pageIndex.value)
    pageIndex.value = pageIndex.value + 1
    setTimeout(() => {
        if(pageIndex.value > 3) {
            callback('ok')
        } else {
            getdatalist(unref(pageIndex))
            callback()
        }

    }, 2000)
}
// groupTitle为自动分组设置属性,归属多个分组请用英文逗号,分割开来;
// groupTitle配置不支持和fixed配置一起使用,使用fixed配置后,groupTitle配置自动失效
const column = ref([
    { type:'selection',width:60, fixed: true },
    { name: 'name', label: '姓名',align:'center',fixed: true, emptyString:'--' },
    { name: 'age', label: '年纪',sorter:false,align:'right',groupTitle: '分组二,分组一,总汇' },
    { name: 'sex', align:'center', label: '性别',filters:{'0':'男','1':'女'},groupTitle: '分组二,分组一,总汇'},
    { name: 'img', label: '图片',type:"img" },
    { name: 'address', label: '地址',align:'center',groupTitle: '分组三,分组一,总汇' },
    { name: 'date', label: '日期',sorter:true },
    { name: 'remark', label: '备注',groupTitle: '分组四,分组一,总汇' },
    { name: 'province', label: '省份',groupTitle: '分组四,分组一,总汇' },
    { name: 'other', label: '其他',groupTitle: '分组五,分组一,总汇' },
    { name: 'city', label: '城市',groupTitle: '总汇'  },
    { name: 'zip', label: '邮编' },
    { name: 'operation', type:'operation',label: '操作',renders:[
        {
            name:'编辑',
            func:'edit' // func 代表子元素点击的事件 父元素接收的事件 父元素 @edit
        },
        {
            name:'删除',
            type:'warn',
            func:"dele"
        },
    ]},
])
onMounted(() => {
})
getdatalist(unref(pageIndex))
</script>
<style lang="scss">

    .next-table-container {
        width: 100vw;
        height: 100vh;
        box-sizing: border-box;
    }
    .flex-btns {
        display: flex;
        position: fixed;
        width: 100%;
        left: 0;
        bottom: 0;
    }
</style>

示例demo(vue2)

<next-x-table
    :show-header="true"
    :columns="column"
    :stripe="true"
    :fit="false"
    show-summary
    sum-text="合计"
    @rowClick="rowClick"
    :summary-method="getSummaries"
    @toggleRowSelection="toggleRowSelection"
    @toggleAllSelection="toggleAllSelection"
    :border="true"
    @edit="buttonEdit"
    @dele="dele"
    :data="datalist"></next-x-table>
<script>
export default {
    data () {
        return {
            pageIndex: 1,
            pageTotal: 5,
            datalist: [],
            checkNameList: [],
            column: [
                { type:'selection', fixed:true,width:60 },
                { name: 'name', label: '姓名',fixed:false,width:80,emptyString:'--' },
                { name: 'age', label: '年纪',sorter:false,align:'right', },
                { name: 'sex', label: '性别',filters:{'0':'男','1':'女'}},
                { name: 'img', label: '图片',type:"img" },
                { name: 'address', label: '地址' },
                { name: 'date', label: '日期',sorter:true },
                { name: 'province', label: '省份' },
                { name: 'city', label: '城市' },
                { name: 'zip', label: '邮编' },
                { name: 'operation', type:'operation',label: '操作',renders:[
                    {
                        name:'编辑',
                        func:'edit' // func 代表子元素点击的事件 父元素接收的事件 父元素 @edit
                    },
                    {
                        name:'删除',
                        type:'warn',
                        func:"dele"
                    },
            ]
        }
    },
    methods: {
        getdatalist(pageIndex) {
            const pageSize = 10
            const arr = []
            for(let i = pageSize*(pageIndex-1) + 1; i < pageSize*pageIndex; i++) {
                arr.push({
                    date: '2023-06-23',
                    name: `刘先生${i}`,
                    province: '上海',
                    sex: i%2 ? '0' : '1',
                    checked: this.checkNameList[this.pageIndex] ? this.checkNameList[this.pageIndex].indexOf(`刘先生${i}`) !== -1 : false,
                    age: 20,
                    img: 'https://gss0.baidu.com/-Po3dSag_xI4khGko9WTAnF6hhy/zhidao/wh%3D450%2C600/sign=e58ae9feb1003af34defd464001aea6a/8601a18b87d6277f4d763bcf2f381f30e824fce5.jpg',
                    city: '广州市',
                    address: '天河区东圃镇2002号',
                    zip: 200333
                })
            }
            this.datalist = arr
        },
        pageChange(index) {
            this.pageIndex = index
            this.getdatalist(this.pageIndex)
        },
        dele(item) {
            const index = this.datalist.findIndex(it => it.name == item.name)
            if (index != -1) {
                this.datalist.splice(index, 1)
            }
        },
        toggleAllSelection(_, list) {
            this.checkNameList[this.pageIndex] = list.map(item => item.name)
        },
        toggleRowSelection(bool, list) {
            this.checkNameList[this.pageIndex] = list.map(item => item.name)
        },
        buttonEdit(item) {
            console.log(111111, item)
        }
    },
    created() {
        this.getdatalist(this.pageIndex)
    }
}

</script>

table 属性

参数 说明 类型 可选值 默认值 是否必须
data 显示的数据 array -- -- 必须
column 显示的列数据 array -- -- 必须
stripe 是否为斑马纹 table boolean - false
fit 列的宽度是否自撑开 boolean true,false false
show-header 是否显示表头 boolean true,false true
cell-style 单元格的 style 的回调方法,也可以使用一个固定的 Object 为所有单元格设置一样的 Style。 Function({row, column, rowIndex, columnIndex})/Object -- --
cell-header-style 头部单元格的 style 的回调方法,也可以使用一个固定的 Object 为所有单元格设置一样的 Style。 Function({ column, columnIndex})/Object -- --
formatter colomn =》formatter 必须设置为true,才有作用,进行格式化数据,进行数据的转换 Function({row, column, rowIndex, columnIndex})/Object -- --
border 是否带有纵向边框 boolean true,false true
highlight 是否要高亮当前行 boolean true,false false
show-summary 是否在表尾显示合计行 boolean true,false false
sum-text 合计行第一列的文本 String - 合计
summary-method 自定义的合计计算方法 Function({ columns, data }) - -
permissionBtn 是否动态控制按钮的显示隐藏 Function({ row, renders,index }) - -
isShowLoadMore 是否开启上拉加载 boolean true,false false
pullUpLoading 开启上拉加载后的返回函数,接收参数done是函数,done(type),type为空代表还有数据,继续开启上拉加载,type='ok',代表结束上拉加载 Function(done) -- --
showPaging 是否开启分页器 boolean true,false false
pageIndex 开启分页器后,当前页码 Number -- 1
pageTotal 开启分页器后,总页数 Number -- 0
thH 表头单元格高度 Number -- 40
tdH 表格单元格高度 Number -- 40
primaryColor 主题颜色(注意:只支持16进制的颜色值如 #000000) String -- 0 #f0ad4e
align 单元格对齐方式(left,center,right) String left,center,right left
关闭上拉加载的方式1:pullUpLoading((done)=>{
    done(type)
})
done 接收参数为 type ,type为空代表还有数据,可以继续加载,无数据的时候传入 'ok'代表结束

table 事件

参数 说明 类型 可选值 默认值 是否必须
事件名自定义 取决于type类型为operation的 renders参数里面 func 的参数名 Function (row,index)=>{} --
sortChange 取决于type类型为operation的 renders参数里面 func 的参数名 Function (column,model,index)=>{} --
currentChange 当表格的当前行发生变化的时候会触发该事件,如果要高亮当前行,请打开表格的 highlight属性,this.$refs.table.resetHighlight()清除选中 Function (row,index)=>{} --
toggleRowSelection 用于多选表格,切换某一行的选中状态,第一个参数代表选中状态,参数二代表选中的对象 Function (selected ,array)=>{} --
toggleAllSelection 用于多选表格,切换所有行的选中状态 ,第一个参数代表选中状态,参数二代表选中的对象 Function (selected ,array)=>{} --
rowClick 单击某行 ,第一个参数代表选中对象,参数二代表选中的index Function (row ,index)=>{} --
cellClick 单击单元格 ,当某个单元格被点击时会触发该事件 Function (row ,index,column)=>{} --
pullUpLoading 开启上拉加载后的返回函数,无参数 Function -- --
pageChange 开起分页paging时候,分页切换后的事件 返回切换后的页码 Function -- --

data 属性

参数 说明 类型 可选值 默认值
checked 是否被勾选 boolean true,false

column 属性

参数 说明 类型 可选值 默认值
name 属性值 string --
label 显示的标题 string --
width 列的宽度 number -- 100
disabled 是否禁用 boolean true,false false
fixed 列是否固定在左侧,true 表示固定在左侧 boolean true,false true
sorter 排序,当设置为custom的时候代表自定义排序,不会再触发默认排序,会触发table事件@sort-change,可以通过接口来进行排序 boolean true,false,'custom' false
emptyString 当值为空的时候默认显示的值 string --
filters 对象过滤的选项,对象格式,对象中的元素需要有 key 和 value 属性。 Object {key:value} --
align 对齐方式 String left/center/right left
type 为 operation 的时候代表为操作按钮,img的时候代表图片地址,index代表序列号 string operation,img,index --
renders type 为operation的时候 必传 Array {name:'名字',func:"父元素接收事件名",type:"按钮的类型",size:"大小"} --
groupTitle 分组标题(多个组标题以英文逗号分割开来),使用fixed配置后,groupTitle配置自动失效,具体使用见demo String --
type 为 operation 的时候代表为操作按钮
renders 代表传入的按钮  Array  =>[
    {
        name:'编辑',
        class:"", // 添加class
        type:'primary',代表按钮的类型  type 为custom的时候自定义按钮 其他类型取决于uniapp buttom组件按钮
        size:'mini',代表按钮的大小
        func:'edit' // func 代表操作按钮点击的事件名字 父元素接收的事件 父元素 @edit
        例如:// <next-table @edit=""/> 

    }
]

隐私、权限声明

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

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

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

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