# 介绍

agel-table (opens new window)是 element-ui table 的二次封装,保持灵活性,极简的思想,更少的代码,更多的功能,更快速的开发 ⬆⬆⬆ npm (opens new window) download (opens new window)

# 特性

该组件的思想就是以一个 table 对象来做所有的操作,哪怕页上多个列表也不用在 data 定义一堆 data1,data2,loading1,loading2 ... 等变量,更加简单明了,适用于 vue2+elementUI。

  • 保持灵活性,极简的思想,更少的代码,更多的功能,更快速的开发
  • 支持 element-ui table 组件的所有 api, slot, event, method
  • 纯数据配置
  • 集成分页组件
  • 菜单列
  • 动态显隐列
  • 数据代理
  • 自动合并相同行
  • 虚拟滚动支持大数据渲染 10w+
  • 跟随容器大小自适应高度

# 安装

npm install agel-table --save # yarn add agel-table

# 创建表格

# 接口代理

这是一个基础的查询表格例子,表格通过一个 table 对象渲染,组件渲染完成之后会注入默认方法和属性到 table 中,方便你通过 table 直接进行所有操作。

  • 表格 page 对象用于配置 Pagination 分页组件的属性,当分页变化会自动同步修改。【可全局配置】

  • 表格 menu 对象用于配置 菜单列,进行编辑删除等,按需使用。【可全局配置】

  • 设置 request 开启接口代理,使用 table.getData 进行主动触发,当分页排序变化时自动触发。

  • 表格 query 对象默认存在四个查询属性,分别为 currentPage pageSize orderColumn order ,当分页排序发生变化时对会自动同步对应数据到 query 对象中。

点击查看代码
<template>
  <div class="demo">
    <p><code v-show="queryString">{{queryString}}</code></p>
    <p>
      <el-input v-model="table.query.name" style="width:100px;margin-right:10px;"></el-input>
      <el-button icon="el-icon-search" @click="()=>table.getData({ currentPage: 1 })">查询</el-button>
    </p>
    <agel-table v-model="table"></agel-table>
  </div>
</template>
 
<script>
export default {
  data() {
    return {
      table: {
        border: true,
        data: [],
        height:300,
        // 该对象放置table 对象的查询参数,默认有 currentPage,pageSize,orderColumn,order
        query: { name: "小虎" },
        // 默认排序列
        defaultSort: { prop: "date", order: "descending" },
        // 分页组件在此配置,建议配置在全局,页面可省略
        page: {
          enable: true,
          currentPage: 1,
          pageSize: 5,
          pageSizes: [5, 10, 15, 20],
        },
        // 菜单列配置
        menu: {
          enable: true,
          fixed: "right",
          onEdit: ({ row }) => {
            this.$message.info("编辑", row.date);
          },
          onDel: ({ row }) => {
            this.$message.info("删除", row.date);
          },
        },
        // 表格列配置
        columns: [
          { label: "日期", prop: "date", width: 200, sortable: "custom" },
          { label: "姓名", prop: "name", width: 200 },
          { label: "地址", prop: "address", minWidth: 300 },
        ],
        // 接口函数
        request: (query, done, err) => {
          // query == this.table.query
          this.getHttpData(query)
            .then((res) => done({ data: res.data, total: res.total }))
            .catch(err);
        },
      },
    };
  },
  computed: {
    queryString() {
      return  JSON.stringify(this.table.query);
    },
  },
  // table.getData 只能在 mounted 生命周期之后调用
  mounted() {
    this.table.getData();
  },
  methods: {
    getHttpData(query) {
      // 模拟一个 http 请求
      return new Promise((reslove) => {
        setTimeout(() => {
          let data = [];
          for (let i = 0; i < query.pageSize; i++) {
            let index = (query.currentPage - 1) * query.pageSize + (i + 1);
            data.push({
              date: "2016-05-02",
              name: "王小虎" + index,
              address: "上海市" + index,
            });
          }
          reslove({ data: data, total: 100 });
        }, 1000);
      });
    },
  },
};
</script>

# 数据配置

这是一个复杂的例子,下面的 Demo 展示了 element-ui 官网 el-table 的大多数例子:

基础表格带斑马纹表格带边框表格带状态表格固定列固定表头单选多选排序表尾合计行自定义索引树形数据与懒加载分页菜单列
点击查看代码
<template>
  <div class="demo">
    <div style="margin-bottom:10px">
      <el-button @click="clearSelection">清空选中</el-button>
      <el-button @click="updateLabel">修改列信息</el-button>
    </div>
    <agel-table v-model="table"> </agel-table>
  </div>
</template>
 
<script>
export default {
  data() {
    let data = [];
    for (let i = 0; i < 10; i++) {
      data.push({
        date: "2016-05-01 10:20",
        name: "王小虎" + i,
        sex: i % 2 == 0 ? "男" : "女",
        address: "上海市",
        hasChildren: i == 0,
      });
    }
    return {
      table: {
        data,
        border: true,
        stripe: true,
        height: 400,
        lazy: true,
        highlightCurrentRow: true,
        defaultSort: { prop: "name", order: "ascending" },
        rowKey: "name",
        treeProps: { children: "children", hasChildren: "hasChildren" },
        showSummary: true,
        summaryMethod: () => ["这", "是", "一", "个", "合", "计"],
        rowClassName: ({ rowIndex }) => (rowIndex == 0 ? "success-row" : ""),
        load: (tree, treeNode, resolve) => {
          setTimeout(() => {
            resolve([
              {
                date: "2016-05-01 10:20",
                name: "王小虎",
                sex: "男",
                address: "上海市普陀区金沙江路 1517 弄",
              },
            ]);
          }, 1000);
        },
        columns: [
          {
            type: "selection",
            width: 50,
            align: "center",
            fixed: true,
          },
          {
            label: "#",
            type: "index",
            align: "center",
            width: 50,
            index: (index) => "#" + (index + 1),
          },
          { label: "日期", prop: "date", width: 200 },
          {
            label: "配送信息",
            children: [
              {
                label: "姓名",
                prop: "name",
                width: 80,
                sortable: true,
              },
              {
                label: "性别",
                prop: "sex",
                width: 80,
                filters: [
                  { text: "男", value: "男" },
                  { text: "女", value: "女" },
                ],
                filterMethod: (value, row) => row.sex === value,
              },
              {
                label: "地址",
                minWidth: 300,
                prop: "address",
              },
            ],
          },
        ],
        on: {
          "selection-change": () => {
            this.$message.success("选择项发生变化");
          },
        },
      },
    };
  },
  methods: {
    clearSelection() {
      this.table.getRef().clearSelection();
    },
    updateLabel() {
      this.table.getCol("address").label = "地址" + Math.random();
    },
  },
};
</script>
<style>
.el-table .success-row {
  background: #f0f9eb;
}
</style>

# 自定义列

设置 column.slotColumn 支持配置自定义列,支持渲染函数

设置 column.slotHeader 支持配置自定义表头,支持渲染函数

点击查看代码
<template>
  <agel-table v-model="table">
    <template v-slot:dateHeader>
      <el-tag>模板自定义列-表头</el-tag>
    </template>
    <template v-slot:date="props">
      <el-input v-model="props.row.date"></el-input>
    </template>
    <template v-slot:expand="props">
      <div style="text-align:center">{{props.row.date}}=>template展开行内容</div>
    </template>
    <template v-slot:append>
      <p slot="append" style="text-align:center">最后一行 slot append...</p>
    </template>
  </agel-table>
</template>
 
<script>
export default {
  data() {
    return {
      table: {
        border: true,
        columns: [
          {
            label: "展开行",
            type: "expand",
            width: 80,
            slotColumn: "expand",
          },
          {
            minWidth: 200,
            slotColumn: (h, { row }) => {
              return <el-tag>{row.name}</el-tag>;
            },
            slotHeader: () => {
              return <el-tag>render函数自定义列-表头</el-tag>;
            },
          },
          {
            minWidth: 200,
            slotColumn: "date",
            slotHeader: "dateHeader",
          },
        ],
        data: [
          { date: "2016-05-02", name: "王小虎", address: "上海市" },
          { date: "2016-05-04", name: "王小虎", address: "上海市" },
        ],
      },
    };
  },
};
</script>

# 动态显隐

设置 column.display 控制是否显示隐藏,支持函数配置

点击查看代码
<template>
  <div class="demo">
    <div style="margin-bottom:10px">
      <el-checkbox v-for="item in table.columns" v-if="item.prop" :key="item.prop" v-model="item.display">{{item.label}}</el-checkbox>
    </div>
    <agel-table v-model="table"></agel-table>
  </div>
</template>
 
<script>
export default {
  data() {
    return {
      address: true,
      table: {
        columns: [
          {
            type: "selection",
            display: () => {
              return this.table.columns[1].display;
            },
          },
          { label: "日期", prop: "date", width: 200, display: true },
          {
            label: "姓名",
            prop: "name",
            width: 200,
            display: true,
          },
          {
            label: "地址",
            prop: "address",
            minWidth: 300,
            display: true,
          },
        ],
        data: [],
      },
    };
  },
};
</script>

# 自动合并

设置 merge 可开启自动合并单元格。

点击查看代码
<template>
  <div>
    <p>设置 <code>merge.auto</code> 将自动纵向合并相同行</p>
    <agel-table v-model="table"></agel-table>
    <p>也可设置指定某一列 <code>columnb.merge</code> 进行合并 </p>
    <agel-table v-model="table2"></agel-table>
    <p>也可设置合并方向,<code>merge.direction</code><code>horizontal</code>横向合并相同列</p>
    <agel-table v-model="table3"></agel-table>
  </div>
</template>
 
<script>
export default {
  data() {
    const data = [
      { date: "2016-05-02", name: "王小虎", address: "上海市1" },
      { date: "2016-05-02", name: "王小虎", address: "上海市2" },
      { date: "2016-05-02", name: "王小虎", address: "上海市3" },
    ];
    return {
      table: {
        border: true,
        merge: { enable: true, auto: true },
        columns: [
          { label: "日期", prop: "date", width: 200 },
          { label: "姓名", prop: "name", width: 200 },
          { label: "地址", prop: "address", minWidth: 300 },
        ],
        data,
      },
      table2: {
        border: true,
        merge: { enable: true },
        columns: [
          { label: "日期", prop: "date", width: 200, merge: true },
          { label: "姓名", prop: "name", width: 200 },
          { label: "地址", prop: "address", minWidth: 300 },
        ],
        data,
      },
      table3: {
        border: true,
        merge: { enable: true, auto: true, direction: "horizontal" },
        columns: [
          { label: "日期1", prop: "date1", width: 200 },
          { label: "日期2", prop: "date2", width: 200 },
          { label: "地址", prop: "address", minWidth: 300 },
        ],
        data: [
          { date1: "2016-05-02", date2: "2016-05-02", address: "上海市1" },
          { date1: "2016-05-02", date2: "2016-05-02", address: "上海市2" },
          { date1: "2016-05-02", date2: "2016-05-03", address: "上海市2" },
        ],
      },
    };
  },
};
</script>

# 虚拟滚动

设置 virtual 可开启虚拟滚动,纯文本渲染效率最佳 😄

只需要设置好 rowHieght,表格会自动设置固定行高,不会被 CSS 样式表所影响。

支持多选列,索引列,固定列,排序,在组件内部做了兼容,不支持过滤、树形、合并单元格。

点击查看代码
<template>
  <div class="demo">
    <el-row style="margin-bottom:10px">
      <el-input-number v-model="number" :min="1" :max="100000" :step="100" placeholder="数据条数"></el-input-number>
      <el-button @click="setData">加载大数据</el-button>
      <el-input-number v-model="rowIndex" :min="1" :max="table.data.length" placeholder="指定跳转行数"></el-input-number>
      <el-button @click="jump">跳转到指定行数</el-button>
    </el-row>
    <agel-table v-model="table"></agel-table>
  </div>
</template>
 
<script>
export default {
  data() {
    return {
      number: 1000,
      rowIndex: 100,
      table: {
        border: true,
        height: 200,
        virtual: { enable: true, rowHeight: 35 },
        columns: [
          {
            type: "selection",
            width: 60,
            align: "center",
            selectable: (row, index) => {
              // console.log(index)
              return index > 2;
            },
          },
          { label: "#", type: "index", width: 50, align: "center" },
          { label: "姓名", prop: "name", width: 200 },
          { label: "随机数", prop: "address", minWidth: 100, sortable: true },
        ],
        data: [],
      },
    };
  },
  mounted() {
    this.setData();
  },
  methods: {
    setData() {
      let data = [];
      for (let i = 0; i < this.number; i++) {
        // 冻结对象可获得更好的性能
        data.push({
          name: "王小虎" + (i + 1) + "号",
          address: Math.random() * 100,
        });
      }
      this.table.data = data;
    },
    jump() {
      console.log(this.table.data[this.rowIndex])
      this.table.virtualScrollToRow(this.table.data[this.rowIndex]);
    },
  },
};
</script>

# 自适应高

设置 resize 属性可开启自适应高度,请指定 relative 参照物,否则默认取的 table.offsetParent

DEMO展示请点击

点击查看代码
<template>
  <div class="box border" style="margin-top:0px;height: 80vh;padding: 0px 20px">
    <div class="head border" style="padding:20px 0px">
      <el-input placeholder="姓名" style="width:100px"></el-input>
    </div>
    <agel-table  class="body" v-model="table"></agel-table>
    <div class="foot border" style="padding:20px 0px">table 底部容器</div>
  </div>
</template>
 
<script>
export default {
  data() {
    return {
      table: {
        resize: {
          enable: true,
          relative: ".box",
          offset: () => {
            return document.querySelector(".foot").offsetHeight;
          },
        },
        columns: [
          { label: "改变浏览器高度,table 会自适应容器高度", minWidth: 300 },
        ],
        data: [],
      },
    };
  },
};
</script>
<style>
.border {
  box-sizing: border-box;
  border: 1px solid #409EFF;
}
</style>

# 全局配置

  • 所有属性均可全局配置,配置将被继承到每个表格上;
  • 强烈建议分页与菜单列相关的属性建议配置在全局,在局部页面根据需求进行覆盖。
import agelTable from "agel-table"

const tableConfig = {
  table: {
    border: true,
    highlightCurrentRow: true,
  },
  column:{
    width:100,
  },
  menu:{
    width:100,
    editRender: ({ h, clickEvent }) =>  h("el-button", { on: clickEvent }, '编辑'),
    delRender: ({ h, clickEvent }) => h("el-button", { on: clickEvent }, '删除')
  },
  page: { 
     enable: true, 
     height: 45, 
     layout: "total, prev, pager, next, jumper, sizes", 
     background: true 
  },
  // query 别名
  queryProps: {
    currentPage: "page",
    pageSize: "size",
    orderColumn: 'sortProp',
    order:"sortOrder"
  },
  // table empty 插槽
  slotEmpty: function (h) {
    return h('el-empty', { props: { description: "暂无数据" } });
  }
}

Vue.use(agelTable,tableConfig)

// use 注册组件 OR component 注册组件

Vue.prototype.$agelTableConfig = tableConfig;
Vue.component('agel-table', agelTable);

# 表格配置

# table

表格属性配置。

属性 类型 默认值 说明
...... ...... ...... All Element-ui Table Attributes (opens new window)
loading Boolean false 是否开启加载状态
data Array [ ] 数据
columns Array/Object [ ] 列配置
query Object { } 查询参数,默认包含分页排序参数
on Object { } table 和 page 组件的 Event 事件
request Function - 接口数据代理函数
page Object - 分页配置
menu Object - 菜单列配置
merge Object - 自动合并单元格
virtual Object - 大数据虚拟滚动
resize Object - 随窗口大小自适应高度

# column

表格列扩展属性。

属性 类型 默认值 说明
...... ...... ...... All Element-ui Table-column Attributes (opens new window)
display Boolean/Function true 是否显示该列
merge Boolean false 该列相同行是否自动合并
children Array - 配置多级表头
slotColumn String/Function - 自定义表列的插槽名称 / slotColumn(h,scope)
slotHeader String/Function - 自定义表头的插槽名称 / slotHeader(h,scope)

# page

开启分页配置,基础属性建议配置在全局。

属性 类型 默认值 说明
...... ...... All Element-ui Pagination Attributes ......
enable Boolean false 是否开启分页
height Nnmber 45 占据高度
justify String flex-end 对齐方式
layout String 'total, sizes, prev, pager, next, jumper' 组件布局
pageSizes Array [10, 20, 50, 100] 页码选项设置
pageSize Nnmber 20 每页显示条目个数
currentPage Nnmber 1 当前页
total Nnmber 0 总条目数

开启菜单列,editRender delRender 等基础属性建议配置在全局。

属性 类型 默认值 说明
...... ...... ...... All Element-ui Table-column Attributes (opens new window)
enable Boolean false 是否开启菜单列
insertIndex Number - 菜单列插入位置,默认在结尾
onEdit Function - 菜单编辑按钮点击回调,设置后显示编辑按钮
onDel Function - 菜单删除按钮点击回调,设置后显示删除按钮
editRender Function - 自定义编辑按钮, editRender({h,clickEvent})
delRender Function - 自定义删除按钮, delRender({h,clickEvent})
menuRender Function - 自定义菜单按钮, menuRender({h,scope,menu})

# merge

开启自动合并单元格。

属性 类型 默认值 说明
enable Boolean false 是否开启合并单元格
auto Boolean false 是否自动合并相同单元格
direction String vertical 合并方向,可选 vertical horizontal

# virtual

开启虚拟滚动。

属性 类型 默认值 说明
enable Boolean false 是否开启虚拟滚动
rowHeight Number 0 行高度

# resize

开启高度自适应容器。

属性 类型 默认值 说明
enable Boolean false 是否开启自适应
relative Stribg/Dom table.offsetParent 自适应参照物元素或者选择器
offset Number/Function 0 calcHeight 偏移高度

# methods

和传统的通过 $refs.table.xxx() 来调用组件方法有所不同,在 ageltable 中方法会自动注入到 table 对象中,可以直接通过 table.xxx() 来调用。

属性 参数 说明 备注
getRef - 获取组件实例
getCol prop 获取 column 列对象
getData {currentPage} 触发调用 request request 配置 done 参数可用
resizeTable - 刷新自适应表格 resize 开启可用
getVirtualRowIndex - 获取虚拟滚动中当前 Index virtual 开启可用
virtualScrollToRow index/row 滚动到指定行 virtual 开启可用