更新记录
                                                                                                    
                                                    
                                                        1.4.3(2025-10-10)
                                                        
                                                    
                                                    修复自定义按钮事件
                                                                                                    
                                                    
                                                        1.4.2(2025-08-29)
                                                        
                                                    
                                                    修复动态设置主题问题
                                                                                                    
                                                    
                                                        1.4.1(2025-08-28)
                                                        
                                                    
                                                    fix bug
                                                                                                                                                    查看更多
                                                                                                
                                            
                                                                                                                                                        平台兼容性
                                                                                                                                                                                                                                                                                                                                uni-app
                                                                                                                                                                                                                                    
| Vue2 | 
Vue3 | 
Chrome | 
Safari | 
app-vue | 
app-nvue | 
Android | 
iOS | 
鸿蒙 | 
| √ | 
√ | 
√ | 
√ | 
√ | 
- | 
- | 
- | 
- | 
                                                                                                                                                            
| 微信小程序 | 
支付宝小程序 | 
抖音小程序 | 
百度小程序 | 
快手小程序 | 
京东小程序 | 
鸿蒙元服务 | 
QQ小程序 | 
飞书小程序 | 
快应用-华为 | 
快应用-联盟 | 
| √ | 
√ | 
√ | 
√ | 
- | 
- | 
- | 
√ | 
- | 
√ | 
√ | 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                其他
                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            
                                            介绍
基于uni-app开发的一个普通的表格组件,功能有固定首列和表头、排序、操作按钮、
table 表格 无限分组表头、内容最大行数显示控制、 固定表头、固定首列、多列 上拉加载更多、 排序、自适应列宽、多选checkbox、编辑、删除、按钮、合计 多页功能
已用于生产环境
遇到问题或有建议可以加入Q1<已满>()反馈qQ : 455-948-571
遇到问题或有建议可以加入Q2()反馈qQ :578-935-043
如果觉得组件不错,给五星鼓励鼓励咯!
注意本插件依赖于scss的编译,如果没有使用scss请手动改源码去掉scss的语法方可使用。如果有疑问请加入加入Q()
注意
作者不介意你对组件源码进行改造使用,为了开源更加高效,谢谢你的配合;为了节省不必要的沟通浪费,以下情况请不要再反馈给作者,请自行解决;
在这感各位的理解,我支持开源,但是作者时间有限;谢谢各位的配合;在这里期望我写的小小插件能为你提供便捷;
1.如果你对源码进行了修改使用,请不需要对作者做任何的反馈,作者确实没有空陪你做技术分析解答;
2.如果你引入插件,连插件是否有正常被uniapp框架识别解析都不清楚,请你换个插件使用;
3.如果你引入插件,针对自己项目进行功能改造的,请自行仔细阅读源码并了解其原理,自行改造;这里作者不愿意浪费过多时间进行技术解答;  
使用
(支持vue2/vue3 app 微信小程序 H5)
从uniapp插件市场导入
h5在线体验
h5在线体验, 点击体验
微信小程序在线体验

APP预览
| 功能预览(滚动表头完美同步) | 
动态分组表头/分页/排序/合计/fixed等功能 | 
  | 
  | 
H5预览
| 动态分组表头/分页/排序/合计/fixed等功能 | 
  | 
微信小程序预览
| 动态无限级分组表头/分页/排序/合计/固定列/下拉加载更多等功能 | 
  | 
预览
| 功能预览 | 
动态无限级分组表头/分页/排序/合计/固定列/下拉加载更多等功能 | 
  | 
  | 
| 分页功能演示 | 
表格单元格th,td高度设置,最大显示行数控制 | 
  | 
  | 
| 高级导航分页功能演示 | 
  | 
示例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"
    :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)
}
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:(item, index) => {
                    console.log(111111, item)
                } // func 代表子元素点击的事件 父元素接收的事件 父元素 @edit
            },
            {
                name:'删除',
                type:'warn',
                func:(item, idx) => {
                    const index = unref(datalist).findIndex(it => it.name == item.name)
                    if (index != -1) {
                        unref(datalist).splice(index, 1)
                    }
                }
            },
        ]
    }
])
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"
            :pagging="pagging"
            :showPaging="true"
            :border="true"
            align="center"
            :thLines="2"
            :tdLines="2"
            :tdH="60"
            :isShowLoadMore="false"
            :highlight="true"
            :pullUpLoadingCall="pullUpLoading"
            :data="datalist" />
    </view>
</template>
<script setup lang="ts">
import {ref, unref, onMounted, nextTick } from "vue"
const datalist = ref([])
const checkNameList = ref([])
const tableWidth = ref(0)
const tableHeight = ref(0)
const pagging = ref({
    uiStyle: 'simple',
    current: 1,
    pages: 5,
    pageSize: 30
})
function getdatalist(pageIndex) {
    const arr = []
    for(let i = pagging.value.pageSize*(pageIndex-1) + 1; i < pagging.value.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)[pagging.value.current] ? unref(checkNameList)[pagging.value.current].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 = pagging.value.pageSize*(pageIndex-1) + 1; i < pagging.value.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)[pagging.value.current] ? unref(checkNameList)[pagging.value.current].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)
    pagging.value.current = index
    _getdatalist(pagging.value.current)
}
function toggleAllSelection(_, list) {
    unref(checkNameList)[pagging.value.current] = list.map(item => item.name)
}
function toggleRowSelection(bool, list) {
    unref(checkNameList)[pagging.value.current] = list.map(item => item.name)
}
function getSummaries(columns, data) { // 自定义合计配置
    return ['自定义1','自定义2','自定义3', '自定义4', '自定义5', '自定义6', '自定义7', '自定义8', '自定义9', '自定义10', '自定义11', '自定义12', '自定义13']
}
function pullUpLoading(callback) {
    console.log(1111111111111, pageIndex.value)
    pageIndex.value = pageIndex.value + 1
    setTimeout(() => {
        if(pageIndex.value > 3) {
            callback('ok')
        } else {
            getdatalist(pagging.value.current)
            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:(item, index) => {
                console.log(111111, item)
            } // func 代表子元素点击的事件 父元素接收的事件 父元素 @edit
        },
        {
            name:'删除',
            type:'warn',
            func:(item, idx) => {
                const index = unref(datalist).findIndex(it => it.name == item.name)
                if (index != -1) {
                    unref(datalist).splice(index, 1)
                }
            }
        }
    ]},
])
onMounted(() => {
})
getdatalist(pagging.value.current)
</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"
    align="center"
    :thLines="2"
    :tdLines="2"
    :tdH="60"
    @edit="buttonEdit"
    @dele="dele"
    :data="datalist"></next-x-table>
<script>
export default {
    data () {
        return {
            pagging: {
                current: 1,
                pages: 5,
                pageSize: 10
            },
            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: {
        getSummaries(columns, data) { // 自定义合计配置
            return ['自定义1','自定义2','自定义3', '自定义4', '自定义5', '自定义6', '自定义7', '自定义8', '自定义9', '自定义10', '自定义11', '自定义12', '自定义13']
        },
        getdatalist(pageIndex) {
            const arr = []
            for(let i = this.pagging.pageSize*(pageIndex-1) + 1; i < this.pagging.pageSize*pageIndex; i++) {
                arr.push({
                    date: '2023-06-23',
                    name: `刘先生${i}`,
                    province: '上海',
                    sex: i%2 ? '0' : '1',
                    checked: this.checkNameList[this.pagging.current] ? this.checkNameList[this.pagging.current].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.pagging.current = index
            this.getdatalist(this.pagging.current)
        },
        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.pagging.current] = list.map(item => item.name)
        },
        toggleRowSelection(bool, list) {
            this.checkNameList[this.pagging.current] = list.map(item => item.name)
        },
        buttonEdit(item) {
            console.log(111111, item)
        }
    },
    created() {
        this.getdatalist(this.pagging.current)
    }
}
</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 | 
否 | 
| pullUpLoadingCall | 
开启上拉加载后的返回函数,接收参数done是函数,done(type),type为空代表还有数据,继续开启上拉加载,type='ok',代表结束上拉加载 | 
Function(done) | 
-- | 
-- | 
否 | 
| showPaging | 
是否开启分页器 | 
boolean | 
true,false | 
false | 
否 | 
| thH | 
表头单元格高度 | 
Number | 
-- | 
40 | 
否 | 
| tdH | 
表格单元格高度 | 
Number | 
-- | 
40 | 
否 | 
| primaryColor | 
主题颜色(注意:只支持16进制的颜色值如 #000000) | 
String | 
-- | 
0 | 
#f0ad4e | 
| align | 
单元格对齐方式(left,center,right) | 
String | 
left,center,right | 
left | 
否 | 
| thLines | 
表头th文本显示的行数,如果设置,超出此行数,将会显示省略号 | 
Number | 
1-5 | 
1 | 
否 | 
| tdLines | 
表格td文本显示的行数,如果设置,超出此行数,将会显示省略号 | 
Number | 
1-5 | 
1 | 
否 | 
| pagging | 
分页器配置 | 
Object | 
-- | 
{ uiStyle: 'simple',current: 1,pages: 0 } | 
必须 | 
pagging 补充说明
// pagging的配置说明
// 简单模式uiStyle = 'simple'配置
{
    uiStyle: 'simple', // ui的展现模式
    current: 1, // 当前页码
    pages: 0, // 总页数
}
// 高级模式uiStyle = 'high'配置
{
    uiStyle: 'high', // ui的展现模式
    current: 1, // 当前页码
    pages: 0, // 总页数
    total: 0, // 总条数
    navNum: 5, // 导航页签的数量
    shwoTotal: true, // 是否展示总条数
}
关闭上拉加载的方式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=""/> 
    }
]