likes
comments
collection
share

使用 TSX 对 el-table 表格组件的封装实践

作者站长头像
站长
· 阅读数 9

背景

最近在准备新项目,公司之前的项目主要使用 vue2 + element ui,利用这次机会决定尝试一下新的技术栈,于是从零搭建了一个 vue3 + vite + ts + element plus 的项目模板,方便快速开始新的项目。

如果你对项目感兴趣,欢迎关注我的 GitHub 仓库 Ares-admin 。如果有任何问题或建议,请随时联系我。

我在公司主要参与中后台项目的开发,经常需要处理报表数据,并频繁使用表格组件。然而 element ui 中表格组件在使用上不够方便,为了能够提高开发效率,并有更多时间划水,就想对表格组件进行一些封装。

使用 TSX 对 el-table 表格组件的封装实践

需求设计

在之前的 vue2 项目中,我根据业务对 el-table 组件进行了一些封装尝试,来提升开发体验和效率。但是由于经验不足,导致一些地方设计不合理。这次我认真思考和调研相关内容后,总结了一些常见的表格封装设计方案:

  1. 通过 schema 来描述表格的列信息
  2. 将查询表单、分页组件、操作按钮封装到表格组件内
  3. 为表格组件增加一些业务逻辑

通过定义 columns 属性来配置表格列信息,可以让表格结构更加清晰,易于扩展,使用起来也更方便,之前的老项目也采用类似的封装方案。

ant design 等主流组件库都采用了类似的设计思想。

不过,将表单和业务操作封装到表格组件内,我个人不是特别认同,虽然可以提高一定的开发效率,但也带来了一些问题和挑战。这种方式增加了组件的复杂度和使用成本,维护起来也会更加困难。

另外,这种设计缺乏灵活性,难以根据不同场景进行调整和扩展。当需要在不同的页面或场景下使用不同的表单或操作逻辑时,由于这些逻辑已经封装在表格组件内部,就无法轻松地进行调整和定制。这可能会导致需要增加额外的代码来满足不同的需求,增加了维护成本。

如果前期设计不好,后期可能会有一堆坑。

综合这些考虑,最终采用以下封装方案:

  1. 将表格列信息通过 columns 进行配置,使表格结构更清晰、使用更方便
  2. 使用 TSX 封装组件,方便代码复用,提升开发体验
  3. 只保留分页器逻辑在表格组件内

其他功能组件和逻辑如果有需要,可以通过组合的方式进行使用。考虑到业务中需要自定义列的功能,我将自定义列逻辑也封装到表格组件中了。

代码实现

  • 定义 columns
参数说明类型
需要继承 el-table-column 所有属性
hidden是否隐藏列boolean
children多级表头Array
能根据需要灵活扩展 ...
  • 事件处理
事件名说明回调参数
需要继承 el-table 所有事件
change分页排序回调pageSize, pageNum, prop, order, type
column-change自定义列回调columns
能根据需要灵活扩展 ...

使用 TSX 对 el-table 表格组件的封装实践

基本结构

function render() {
  return (
    <div class="x-table">
      <ElTable
        {...tableProps}
        v-slots={extraSlots}
      >
        {
          props.columns.map((item) => {
            if (Array.isArray(item.children)) {
              return renderColumnChildren(item, item.children)
            }
            return renderTableColumn(item)
          })
        }
      </ElTable>
      {showPagination.value && renderPagination()}
      {!isUndefined(props.visibleColumn) && renderCustomColumn()}
    </div>
  )
}

表格列渲染

function renderTableColumn(column: XTableColumn) {
  const columnSlots: {
    default?: (scope: Record<string, any>) => any
    header?: (scope: Record<string, any>) => any
  } = {}
  
  const slot = getSlot(column)
  const headerSlot = getSlot(column, 'header')

  if (slot) {
    columnSlots.default = scope => slot(scope)
  }

  if (headerSlot) {
    columnSlots.header = scope => headerSlot(scope)
  }

  return (
    <ElTableColumn {...getColumnProps(column)} >
      {columnSlots}
    </ElTableColumn>
  )
}

自定义列渲染

function renderCustomColumn() {
  const customColumnProps = {
    columns: props.columns,
    visible: props.visibleColumn,
    onChange: handleColumnChange,
    onVisibleChange: handleVisibleChange,
  }

  return (
    <XColumn {...customColumnProps} />
  )
}

使用示例

const columns = [
  {
    type: 'selection',
  },
  {
    label: '日期',
    prop: 'date',
    align: 'left',
    sortable: 'custom',
    formatter(row) {
      return dayjs(row.date).format('YYYY-MM-DD')
    },
  },
  {
    label: '地址',
    prop: 'address',
    hidden: true,
    children: [
      {
        label: '区域',
        prop: 'area',
      },
    ],
  },
  {
    label: '操作',
    prop: 'action',
    fixed: 'right',
  },
]
<x-table
  v-model:visible-column="dialogVisible"
  :columns="columns"
  :data-source="tableData"
  :total="tableTotal"
  :page-size="form.pageSize"
  :page-num="form.pageNum"
  @change="handleTableChange"
  @column-change="handleColumnChange"
  @selection-change="handleSelectionChange"
  @header-dragend="handleHeaderDragend"
>
  <template #name="{ row }">
    <el-button>{{ row.name }}</el-button>
  </template>
  
  <template #name-header>
    <span>表头插槽</span>
  </template>

  <template #action>
    <el-button type="primary">新增</el-button>
    <el-button>修改</el-button>
  </template>
</x-table>

// 修改自定义列
function handleColumnChange(cols: XTableColumn[]) {
  columns.value = cols
}

// 表格多选
function handleSelectionChange(value: XTableData[]) {
  console.log('selection change =>',value)
}

// 分页和排序
function handleTableChange(data: XTableChangeData) {
  const { pageSize, pageNum, sort, prop, type } = data
  state.pageNum = pageNum
  state.pageSize = pageSize
  state.sort = sort
  state.prop = prop
}

完整的代码可以在我的 GitHub 仓库 Ares-admin 中查看。

总结

本文分享了一些关于如何封装 el-table 组件的思路和简单功能实现。在工作中通过总结和思考,我们可以封装一些业务组件来提高开发效率,减少重复的代码编写。在封装组件时,应该根据具体业务需求和团队特点,选择合适的封装方式。

最后,欢迎大家在评论区讨论学习,分享自己的经验和更好的想法,帮助我们一起成长。