前言

使用配置描述数据来替代 vue 模板的组件写法,基础能力完全与 el-table 组件保持一致。并提供了一些方便和自定义的 api,加快书写。

代码实现

<script>
import { formatDate } from 'element-ui/lib/utils/date-util';

function isObject (obj) {
  return Object.prototype.toString.call(obj) === '[object Object]';
};

function isNil (val) {
  return val === undefined || val === null;
};

function getEnumValue (value, _enum) {
  const valueEnum = _enum || undefined;
  return valueEnum && valueEnum[value] ? valueEnum[value] : value;
};

export default {
  name: 'ElProTable',

  props: {
    data: {
      type: Array,
      default: () => ([])
    },
    columns: {
      type: Array,
      default: () => ([])
    },
    loading: Boolean
  },

  mounted () {
    this.mountTableMethods();
  },

  methods: {
    // 在 this 上挂载 table 组件实例方法
    mountTableMethods() {
      const ElTableMethodKeys = ['clearSelection', 'toggleRowSelection', 'toggleAllSelection', 'toggleRowExpansion', 'setCurrentRow', 'clearSort', 'clearFilter', 'doLayout', 'sort'];
      Object.entries(this.$refs.table).forEach(([key, item]) => {
        if (ElTableMethodKeys.includes(key)) {
          this[key] = item;
        }
      });
    },

    generateValue ({ prop, valueType, enum: _enum }) {
      if (!prop) return null;
      return (scoped) => {
        const row = scoped.row || {};
        let value = row[prop];
        switch (valueType) {
          case 'date':
            value = formatDate(value);
            break;
          case 'dateTime':
            value = formatDate(value, 'yyyy-MM-dd HH:mm:ss');
            break;
          case 'text':
          default: value = getEnumValue(value, _enum);
        };
        return value;
      };
    },

    renderColumns (h, columns) {
      const $scopedSlots = this.$scopedSlots;
      return columns.map((item, i) => {
        if (!isObject(item)) return null;
        const key = !isNil(item.prop) ? item.prop : i;
        const { render, renderHeader, slot, slotHeader, columns: _columns, ...props } = item;
        const scopedSlots = {
          default: render ? (scoped) => render(h, scoped) : $scopedSlots[slot] || this.generateValue(item),
          header: renderHeader ? (scoped) => renderHeader(h, scoped) : $scopedSlots[slotHeader] || null
        };
        // 通过递归处理多级表头的情况
        return <el-table-column key={ key } { ...{ props } } scopedSlots={ scopedSlots }>
          { Array.isArray(_columns) ? this.renderColumns(h, _columns) : null }
        </el-table-column>;
      });
    }
  },

  render (h) {
    return (
      <div class="el-pro-table">
        <el-table
          ref="table"
          { ...{ props: this.$attrs } }
          { ...{ on: this.$listeners } }
          data={ this.data }
          v-loading={ this.loading }
        >
          { this.renderColumns(h, this.columns) }
        </el-table>
      </div>
    );
  }
};
</script>

基本使用

<template>
  <el-pro-table :data="dataSource" :columns="columns" />
</template>

<script>
export default {
  data () {
    return {
      columns: [
        {
          prop: 'name',
          label: 'Name'
        },
        {
          prop: 'age',
          label: 'Age'
        },
        {
          prop: 'address',
          label: 'Address'
        }
      ],
      dataSource: [
        { name: 'Yu Lou', age: 32, address: 'New York No. 1 Lake Park' },
        { name: 'Jim Green', age: 42, address: 'London No. 1 Lake Park' },
        { name: 'Joe Black', age: 32, address: 'Sidney No. 1 Lake Park' },
      ]
    }
  }
}
</script>

使用 valueType

封装了一些常用的值类型来减少重复的 render 操作,通过在 columns 数据项中配置一个 valueType 即可展示格式化的数据。

<template>
  <el-pro-table :data="dataSource" :columns="columns" />
</template>

<script>
export default {
  data () {
    return {
      columns: [
        {
          prop: 'title',
          label: 'Title'
        },
        {
          prop: 'createTime',
          label: 'Time',
          valueType: 'dateTime'
        },
        {
          prop: 'state',
          label: 'State'
        },
      ],
      dataSource: [
        { title: 'title 1', createTime: 1616495706550, state: 'open' },
        { title: 'title 2', createTime: 1616405700550, state: 'closed' },
      ]
    }
  }
}
</script>

使用 enum

通过在 columns 数据项中配置 enum 对象,将状态值转为对应的描述。

<template>
  <el-pro-table :data="dataSource" :columns="columns" />
</template>

<script>
export default {
  data () {
    return {
      columns: [
        {
          prop: 'title',
          label: 'Title'
        },
        {
          prop: 'state',
          label: 'State',
          enum: {
            open: '未解决',
            closed: '已解决'
          }
        },
      ],
      dataSource: [
        { title: 'title 1', state: 'open' },
        { title: 'title 2', state: 'closed' },
      ]
    }
  }
}
</script>

自定义列

render 写法

通过给 columns 数据的项,设置一个函数 render,可以自定义渲染当前列,包括渲染自定义组件,它基于 Vue 的 Render 函数。

render 函数传入两个参数,第一个是 h,第二个是对象,包含 row、column 和 $index,分别指当前行数据,当前列数据,当前是第几行。

<template>
  <el-pro-table :data="dataSource" :columns="columns" />
</template>

<script>
export default {
  data () {
    return {
      columns: [
        {
          prop: 'name',
          label: 'Name'
        },
        {
          prop: 'age',
          label: 'Age'
        },
        {
          prop: 'address',
          label: 'Address'
        },
        {
          label: 'Action',
          render (h, { row }) {
            return (
              <div>
                <el-button type="text">Invite { row.name }</el-button>
                <el-button type="text">Delete</el-button>
              </div>
            )
          }
        }
      ],
      dataSource: [
        { name: 'Yu Lou', age: 32, address: 'New York No. 1 Lake Park' },
        { name: 'Jim Green', age: 42, address: 'London No. 1 Lake Park' },
        { name: 'Joe Black', age: 32, address: 'Sidney No. 1 Lake Park' },
      ]
    }
  }
}
</script>

slot-scope 写法

在 columns 的某列声明 slot 后,就可以在 Table 的 slot 中使用 slot-scope。

slot-scope 的参数有 3 个:当前行数据 row,当前列数据 column,当前行序号 $index。

<template>
  <el-pro-table :data="dataSource" :columns="columns">
    <template slot-scope="{ row }" slot="action">
      <el-button type="text">Invite {{ row.name }}</el-button>
      <el-button type="text">Delete</el-button>
    </template>
  </el-pro-table>
</template>

<script>
export default {
  data () {
    return {
      columns: [
        {
          prop: 'name',
          label: 'Name'
        },
        {
          prop: 'age',
          label: 'Age'
        },
        {
          prop: 'address',
          label: 'Address'
        },
        {
          label: 'Action',
          slot: 'action'
        }
      ],
      dataSource: [
        { name: 'Yu Lou', age: 32, address: 'New York No. 1 Lake Park' },
        { name: 'Jim Green', age: 42, address: 'London No. 1 Lake Park' },
        { name: 'Joe Black', age: 32, address: 'Sidney No. 1 Lake Park' },
      ]
    }
  }
}
</script>

自定义表头

render 写法

通过给 columns 数据的项,设置一个函数 renderHeader,可以自定义渲染当前列表头,包括渲染自定义组件,它基于 Vue 的 Render 函数。

renderHeader 函数传入两个参数,第一个是 h,第二个是对象,包含 column 和 $index,分别指当前列数据,当前是第几行。

<template>
  <el-pro-table :data="tableData" :columns="columns" />
</template>

<script>
export default {
  data () {
    return {
      search: '',
      columns: [
        {
          prop: 'date',
          label: 'Date'
        },
        {
          prop: 'name',
          label: 'Name'
        },
        {
          align: 'right',
          renderHeader: (h, scoped) => {
            return <el-input value={ this.search } on-input={ this.onInput } size="small" placeholder="Search" />
          },
          render (h) {
            return <el-button size="small">Edit</el-button>
          }
        }
      ],
      dataSource: [
        { date: '2016-05-03', name: 'Yu Lou' },
        { date: '2016-05-02', name: 'Jim Green' },
        { date: '2016-05-04', name: 'Joe Black' }
      ]
    }
  },
  computed: {
    tableData () {
      const { dataSource, search } = this
      return dataSource.filter(data => !search || data.name.toLowerCase().includes(search.toLowerCase()))
    }
  },
  methods: {
    onInput (value) {
      this.search = value
    }
  }
}
</script>

slot-scope 写法

在 columns 的某列声明 slotHeader 后,就可以在 Table 的 slot 中使用 slot-scope。

slot-scope 的参数有 3 个:当前行数据 row,当前列数据 column,当前行序号 $index。

<template>
  <el-pro-table :data="tableData" :columns="columns">
    <template slot-scope="scope" slot="search">
      <el-input v-model="search" size="small" placeholder="Search" />
    </template>
    <template slot-scope="scope" slot="action">
      <el-button size="small">Edit</el-button>
    </template>
  </el-pro-table>
</template>

<script>
export default {
  data () {
    return {
      search: '',
      columns: [
        {
          prop: 'date',
          label: 'Date'
        },
        {
          prop: 'name',
          label: 'Name'
        },
        {
          align: 'right',
          slotHeader: 'search',
          slot: 'action'
        }
      ],
      dataSource: [
        { date: '2016-05-03', name: 'Yu Lou' },
        { date: '2016-05-02', name: 'Jim Green' },
        { date: '2016-05-04', name: 'Joe Black' }
      ]
    }
  },
  computed: {
    tableData () {
      const { dataSource, search } = this
      return dataSource.filter(data => !search || data.name.toLowerCase().includes(search.toLowerCase()))
    }
  }
}
</script>

多级表头

在 columns 配置项中可以内嵌 columns,以渲染多级表头。

<template>
  <el-pro-table :data="dataSource" :columns="columns" />
</template>

<script>
export default {
  data () {
    return {
      columns: [
        {
          prop: 'date',
          label: '日期',
          width: '150'
        },
        {
          label: '配送信息',
          columns: [
            {
              prop: 'name',
              label: '姓名',
              width: '120'
            },
            {
              label: '地址',
              columns: [
                {
                  prop: 'province',
                  label: '省份',
                  width: '120'
                },
                {
                  prop: 'city',
                  label: '市区',
                  width: '120'
                },
                {
                  prop: 'address',
                  label: '详细地址'
                },
                {
                  prop: 'zip',
                  label: '邮编',
                  width: '120'
                }
              ]
            },
          ]
        }
      ],
      dataSource: [
        { date: '2016-05-03', name: 'Yu Lou', province: '浙江省', city: '杭州市', address: 'Fu Ding No. 1 Lake Park', zip: 200333 },
        { date: '2016-05-02', name: 'Yu Lou', province: '浙江省', city: '杭州市', address: 'Fu Ding No. 1 Lake Park', zip: 200333 },
        { date: '2016-05-04', name: 'Yu Lou', province: '浙江省', city: '杭州市', address: 'Fu Ding No. 1 Lake Park', zip: 200333 },
        { date: '2016-05-01', name: 'Yu Lou', province: '浙江省', city: '杭州市', address: 'Fu Ding No. 1 Lake Park', zip: 200333 },
        { date: '2016-05-08', name: 'Yu Lou', province: '浙江省', city: '杭州市', address: 'Fu Ding No. 1 Lake Park', zip: 200333 },
        { date: '2016-05-08', name: 'Yu Lou', province: '浙江省', city: '杭州市', address: 'Fu Ding No. 1 Lake Park', zip: 200333 },
        { date: '2016-05-08', name: 'Yu Lou', province: '浙江省', city: '杭州市', address: 'Fu Ding No. 1 Lake Park', zip: 200333 }
      ]
    }
  },
}
</script>

Table Props

参数 说明 类型 可选值 默认值
columns 表格列的配置描述,详见 column 配置 array - -
data 显示的数据 array - -
loading 是否加载中 boolean - false
... 其他 el-table 组件支持的属性 - - -

Column

列描述数据对象。column 支持 el-table-column 已有的 props 配置,但是也提供了一些方便和自定义的 api,加快书写:

参数 说明 类型 可选值 默认值
render 自定义渲染列,使用 Vue 的 Render 函数。传入两个参数,第一个是 h,第二个为对象,包含 row、column 和 index,分别指当前行数据,当前列数据,当前行索引,详见示例 function - -
renderHeader 自定义列头显示内容,使用 Vue 的 Render 函数。传入两个参数,第一个是 h,第二个为对象,包含 column 和 index,分别指当前行数据,当前列数据,当前行索引,详见示例 function - -
slot 与 slot-scope 结合使用,自定义渲染列 string - -
slotHeader 与 slot-scope 结合使用,自定义列头 string - -
valueType 当前列值的类型,详见 valueType 配置 string text / date / dateTime text
enum 当前列值的枚举 object - -

render、slot、valueType、enum 同时配置时,会有渲染优先级。render 渲染级别最高。一般情况下:render > slot > valueType / enum。配置自定义 header 时同理。

ValueType

封装了一些常用的值类型来减少重复的 render 操作,配置一个 valueType 即可展示格式化响应的数据。

属性 描述
text 普通的文本类型
date 当数据是日期类型的返回时,会自动将格式转换为 '2020-10-20'
dateTime 当数据是日期类型的返回时,会自动将格式转换为 '2020-10-20 19:30:00'
... 陆续添加中

Table Events

事件名称 说明 回调参数
... el-table 组件支持的事件 -

Table Methods

支持 el-table 所有的 methods.