Skip to content

UseElTable - 表格

组件介绍

UseElTable 是基于 Element Plus 的 el-table 核心能力二次封装的通用表格组件。该组件通过抽象「选择器、序号列、操作列、分页、自适应」等高频配置项,将原生表格的零散配置收敛为标准化的全局 / 局部配置,解决了原生表格配置繁琐、自适应适配复杂、分页与表格联动冗余等问题,同时保留原生 el-table 的全量能力,适用于中后台系统各类数据列表场景。

核心亮点

  1. 配置化驱动,大幅降低开发成本
  2. 内置高频功能,无需重复开发
  3. 高扩展性与自定义能力
  4. 统一交互与样式规范
  5. 自动化分页联动,减少逻辑冗余
  6. 全量保留原生能力,无功能阉割

解决痛点

  1. 原生 el-table 重复代码过多
  • 痛点:每个表格页面都需编写大量 el-table、el-table-column 标签,列的样式、对齐方式、宽度等需重复配置,开发效率低且易出错。
  • 解决方案:通过配置项 tableColumns 集中定义所有列属性,模板仅需引入组件并传入配置,减少 80% 以上的重复代码,且配置项可复用、可维护。
  1. 高频功能需手动封装,逻辑不统一
  • 痛点:多选 / 单选、分页联动、空状态、加载状态等功能,每个页面需手动编写逻辑,不同开发者实现方式不同,导致代码混乱、维护成本高。
  • 解决方案:组件内置这些高频功能,通过简单配置即可使用,且逻辑统一,无需重复开发。
  1. 自定义交互与通用逻辑冲突
  • 痛点:原生 el-table 自定义列(如操作按钮组、自定义模板)时,需与通用逻辑(如选中行、排序)兼容,易出现交互异常。
  • 解决方案:组件预留插槽、自定义事件等扩展入口,自定义内容可与通用逻辑解耦,同时提供默认的兼容处理,确保自定义开发不破坏原有功能。
  1. 跨页面表格样式 / 交互不一致
  • 痛点:中后台系统多个页面的表格,因无统一封装,样式(如行高、表头样式)、交互(如排序触发方式、选中行样式)不一致,影响用户体验。
  • 解决方案:组件内置统一的默认样式和交互规则,同时支持通过配置项覆盖,既保证一致性,又满足个性化样式需求。

使用

全配置前端分页

该例演示复选+序号+前端分页等全配置

PS:1、表格数据由mockjs生成,非真实数据;2、由于ElementPlus组件库语言默认为英语,故在本例中组件层语言为英语,如需设置为中文需使用UseElConfigProvider组件提供的语言配置能力

CheckBox单选

该例演示CheckBox单选+同步设置选中数据

Radio单选

该例演示Radio单选+异步设置选中数据

Dict字典

该例演示Dict字典的各种使用

Slot插槽及其它

该例演示Slot插槽及其它常用配置

树形表格

该例演示树形表格(含懒加载)

额外表头

Types

js
import type { ExtractPropTypes } from 'vue'
import type { Dict } from '@/types/dict'
import componentProps from './props'

/**
 * 选择器配置项
 */
export interface Selection {
  label?: string
  type?: string
  width: number
  key?: string
  enableRowClick: boolean
  selectable?: Function
  disabledConfig?:
    | Record<string, string | number | string[] | number[]>
    | Array<{ key: string; value: string | number | string[] | number[] }>
  align: string
  fixed: boolean
}

/**
 * UseElTable的extConfig接口
 */
export interface ExtConfig {
  // 额外表头配置项
  extraHeader: {
    class?: string
    style: { padding: string; borderBottom: string }
  }
  // 选择器配置项
  selection: Selection
  // 序号列配置项
  index: { label?: string; width: number; align: string; fixed: boolean }
  // 自适应配置项
  adaptive: { disabled: boolean; extraHeight: number; autoMinWidth: boolean }
  // 展开列配置项
  expand: { show: boolean; width?: number }
  // 操作列配置项
  operation: { label?: string; width: number; fixed: string }
  // 分页器配置项
  pagination: {
    total: number
    marginTop: number
    position: string
    pageSizes: number[]
    background: boolean
    layout: string
    func?: Function
  }
}

/**
 * 表头配置项
 */
export interface Header {
  // 表头具名插槽 为true则在模板代码中添加`<template #prop-header="{ item }"></template>`即可,可传入string自定义具名插槽名称
  slot?: boolean | string
  // 表头是否必填,必填表头默认在表头名称前添加*号
  required?: boolean
  // 必填表头*号位置,默认为before
  asteriskPosition?: 'before' | 'after'
}

/**
 * tableColumn的extConfig接口
 */
export interface TableColumnExtConfig {
  // 表头配置项
  header?: Header
  // 字典
  dict?: Dict
  // 过滤器
  formatter?: Function
  // 类名
  class?: string | any[]
  // 样式
  style?: object
  // 插槽 为true则在模板代码中添加`<template #prop="{ row }"></template>`即可,可传入string自定义具名插槽名称
  slot?: boolean | string
}

/**
 * tableColumn接口
 */
export interface TableColumn {
  label: string
  prop?: string //多级表头的父级表头可不传
  width?: string | number
  minWidth?: string | number
  show?: boolean
  align?: string
  fixed?: boolean | string
  sortable?: boolean
  showOverflowTooltip?: boolean
  extConfig?: TableColumnExtConfig
  children?: TableColumn[]
}

/**
 * 表头列样式接口
 */
export interface HeaderCellStyle {
  backgroundColor?: string
  color?: string
  [key: string]: any
}

/**
 * props类型
 */
export type Props = ExtractPropTypes<typeof componentProps>

/**
 * emits接口
 */
export interface Emits {
  /**
   * 选中数据更新事件(支持 v-model:selectionData)
   */
  (e: 'update:selectionData', selectionData: any[] | any): void

  /**
   * 当前行更新事件(支持 v-model:currentRow)
   */
  (e: 'update:currentRow', currentRow: any): void

  /**
   * 当前页码更新事件(支持 v-model:currentPage)
   */
  (e: 'update:currentPage', currentPage: number): void

  /**
   * 每页条数更新事件(支持 v-model:pageSize)
   */
  (e: 'update:pageSize', pageSize: number): void
}

DefaultExtConfig

js
import type { ExtConfig } from './types'

/**
 * 默认扩展配置
 */
export default {
  /**
   * 额外表头配置项
   * class:额外表头类名
   * style:额外表头样式对象
   */
  extraHeader: {
    style: {
      padding: '10px',
      borderBottom: '1px solid var(--el-border-color-light)'
    }
  },
  /**
   * 选择器配置项
   * 绑定v-model:selectionData="selectionData"则开启选择器 不显式设置type则默认为CHECKBOX复选
   * type:选择类型 可选值:CHECKBOX(复选) | CHECKBOXRADIO(复选框单选) | RADIO(单选)
   * label:type为CHECKBOXRADIO、RADIO时表头全选复选框的文本 不设置则为空显示
   * key:选中数据的唯一键 对应tableData中的唯一键 如id
   * selectable:决定当前行复选框是否可以勾选 传入function,如果是简单禁用逻辑推荐优先使用disabledConfig配置项,复杂逻辑才建议使用本配置项
   * disabledConfig:禁用配置 可传对象或对象数组
   * enableRowClick:允许通过点击行触发单选/复选 默认否 PS:默认不高亮当前行,若需高亮当前行,只需给UseElTable组件设置highlightCurrentRow属性即可
   * width:选择器的列宽 默认为40px
   * align:选择器的排列方式 默认为center居中
   * fixed:是否固定 默认为true
   */
  selection: { width: 40, enableRowClick: false, align: 'center', fixed: true },
  /**
   * 序号列配置项
   * label:序号列表头文本 不显式设置则不显示序号列
   * width、align、fixed同选择器配置项
   */
  index: { width: 55, align: 'center', fixed: true },
  /**
   * 展开列配置项
   */
  expand: { show: false },
  /**
   * 自适应配置项
   * disabled:是否禁用高度自适应 默认不禁用
   * extraHeight:开启高度自适应后的额外高度(margin+padding) 非显式设置则为全局配置的【表格底部额外高度】,可手动显式设置额外高度
   * autoMinWidth:是否开启自动最小宽度 默认开启
   */
  adaptive: { disabled: false, extraHeight: 20, autoMinWidth: true },
  /**
   * 操作列配置项
   * label:操作列文案 显式设置则开启操作列
   * width:操作列宽度 默认为128px
   * fixed:固定方向 默认固定在右侧
   */
  operation: { width: 128, fixed: 'right' },
  /**
   * 分页器配置项
   * total:分页总数 为-1时不显示分页器
   * marginTop:分页器与表格的间距 默认20px
   * func:分页方法 显式设置则在组件内部自动处理分页逻辑
   * position:分页器位置 默认为right 可选值:left | center | right
   */
  pagination: {
    total: -1,
    marginTop: 20,
    position: 'right',
    pageSizes: [10, 20, 30, 40, 50],
    background: true,
    layout: 'total, sizes, prev, pager, next, jumper'
  }
} satisfies ExtConfig

ComponentProps

js
import type { PropType } from 'vue'
import type { ExtConfig, HeaderCellStyle, TableColumn } from './types'
import type { DictMap, DictProps } from '@/types/dict'

/**
 * 组件props
 */
export default {
  /**
   * 表格列 必传
   */
  tableColumns: {
    type: Array as PropType<TableColumn[]>,
    required: true
  },
  /**
   * 表格数据 必传
   */
  tableData: {
    type: Array as PropType<Record<string, unknown>[]>,
    required: true
  },
  /**
   * 表格loading
   */
  tableLoading: {
    type: Boolean,
    default: false
  },
  /**
   * 表格列排列方式 默认center
   */
  tableColumnAlign: {
    type: String,
    default: 'center',
    validator: (val: string) => ['left', 'center', 'right'].includes(val)
  },
  /**
   * 字典映射 一般数据结构为{ tableColumns[].label: Array<{ label: string, value: any }> },亦可自定义key,然后通过optionKey与其匹配
   */
  dictMap: {
    type: Object as PropType<DictMap>,
    default: () => ({})
  },
  /**
   * 字典属性 默认为{ value: 'value', label: 'label' },可通过全局配置进行设置,为适配全局配置此处不再设置默认值
   */
  dictProps: {
    type: Object as PropType<DictProps>,
    default: () => ({})
  },
  /**
   * 字典数据是否转换为字符串 可通过全局配置进行设置
   */
  dictDataToString: {
    type: Boolean,
    default: false
  },
  /**
   * 选中的数据 通过属性v-model:selectionData绑定
   * type为CHECKBOX时为Array,type为CHECKBOXRADIO和RADIO时为Object
   * 为精确判断组件是否绑定v-model:selectionData属性,故不在此处显式指定,而是通过proxy.$attrs['selectionData']获取
   */
  // selectionData:{
  //   type: [Array, Object] as PropType<Record<string, unknown>[] | Record<string, unknown>>,
  //   default: undefined
  // },
  /**
   * 当内容过长被隐藏时是否显示tooltip 默认是
   */
  showOverflowTooltip: {
    type: Boolean,
    default: true
  },
  /**
   * 表头列样式
   */
  headerCellStyle: {
    type: Object as PropType<HeaderCellStyle>,
    default: () => ({ backgroundColor: '#F0F2F6', color: '#000' })
  },
  /**
   * 主题模式
   */
  themeMode: {
    type: String,
    default: '',
    validator: (val: string) => ['', 'light', 'dark'].includes(val)
  },
  /**
   * 扩展配置 如【序号/复选单选/展开列/分页】等
   */
  extConfig: {
    type: Object as PropType<ExtConfig>,
    default: () => ({})
  }
}

Props && Emits

js
const props = defineProps(componentProps)

const emits = defineEmits<Emits>()

基于 MIT 许可发布