Skip to content

UseElTable - 表格

组件介绍

UseElTable 是基于 Element Plus 的 ElTable 二次封装的表格组件,专注于标准化表格列配置、单元格数据格式化、统一操作栏管理,同时标准化扩展配置项管理,完整兼容原生 ElTable 核心属性、事件、插槽,可直接替换原生 ElTable 使用,大幅降低表格列定义、单元格格式化、操作按钮重复开发成本。

核心能力

  1. 标准化列配置管理

通过统一 columns 配置数组批量定义表格列,支持列标题、绑定字段、宽度、对齐、固定列、显隐控制、单元格格式化回调等配置,减少页面重复编写 el-table-column 模板代码,同时保留自定义插槽扩展能力适配特殊单元格渲染。

  1. 标准化单元格数据格式化能力

内置通用单元格数据转换逻辑,支持时间、金额、布尔、枚举映射等常用格式化规则,支持自定义格式化函数;空单元格统一配置兜底占位文本,避免原始空值展示不友好,格式化规则可通过全局扩展配置统一预设。

  1. 统一操作栏封装

内置 actionColumn 配置统一管理表格右侧操作列,操作按钮完整复用 UseElButton 全部内置防抖、二次确认能力,支持自定义操作栏宽度、对齐方式,统一项目内所有表格操作栏视觉与交互规范。

  1. 灵活的配置优先级管理

所有扩展配置遵循「组件局部配置 > 全局默认配置 > 内置默认值」优先级规则,组件传入的 extConfig 优先覆盖 defaultExtConfig 中的默认配置(如空数据兜底文本、操作栏默认宽度),未配置时自动兜底,兼顾全局统一与局部灵活调整。

  1. 原生表格完全兼容

底层完整透传 ElTable 全部原生 props、事件、插槽,原生多选、树形表格、展开行、合计行等高级特性全部保留,原有原生表格写法无需改动,可直接替换原生 ElTable,无迁移成本,同时补充原生表格缺失的列配置、格式化、统一操作栏能力。

  1. 统一加载与空状态管控

内置 loading、空数据展示相关扩展配置,支持自定义加载提示、空状态文案,未自定义时读取全局默认扩展配置兜底样式,统一项目内所有表格加载、无数据场景用户体验。

  1. 扩展配置标准化

通过 ExtConfig 接口标准化列、格式化、操作栏、空状态等扩展配置项,提供 defaultExtConfig 全局默认扩展配置,业务侧可全局统一覆盖通用表格配置,保证项目内表格扩展配置规范统一。

  1. 样式兼容与问题修复

内置配套样式兼容逻辑,修复表格搭配弹窗、抽屉组件时层级覆盖、滚动布局挤压、固定列样式错位等常见问题,保证表格在不同页面布局、嵌套容器下展示一致性。

  1. 安全的生命周期管理

组件挂载时初始化列配置、格式化缓存;卸载时自动解绑内部回调、清空临时状态缓存、移除页面监听,避免内存泄漏,保证组件在动态路由、弹窗、标签页频繁销毁重建场景下运行稳定。

使用

UseElTable

Props

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

/**
 * 组件props
 */
export default {
  /**
   * 表格列 必传
   * 注意:常规场景下列配置为静态值无需响应式;但本组件提供了 minWidth 自动计算能力并会修改列对象,
   * 因此 tableColumns 必须用 ref/reactive 包裹,否则视图无法同步更新最小宽度。
   */
  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 | useDict | useDict.key]: Array<{ label: string, value: any }> }
   */
  dictMap: {
    type: Object as PropType<DictMap>,
    default: () => ({})
  },
  /**
   * 字典属性 默认为{ value: 'value', label: 'label',children:'children' },可通过全局配置进行设置,为适配全局配置此处不再设置默认值
   */
  dictProps: {
    type: Object as PropType<DictProps>,
    default: () => ({})
  },
  /**
   * 当前页数
   */
  currentPage: {
    type: Number,
    default: 1
  },
  /**
   * 当前页码
   */
  pageSize: {
    type: Number,
    default: 10
  },
  /**
   * 当内容过长被隐藏时是否显示tooltip 默认是
   */
  showOverflowTooltip: {
    type: Boolean,
    default: true
  },
  /**
   * 表头列样式
   */
  headerCellStyle: {
    type: Object as PropType<Record<string, string>>,
    default: () => ({
      backgroundColor: 'var(--el-fill-color-light)',
      color: 'var(--el-table-header-text-color)'
    })
  },
  /**
   * 扩展配置 如【序号/复选单选/展开列/分页】等
   */
  extConfig: {
    type: Object as PropType<ExtConfig>,
    default: () => ({})
  }
}

Types

ts
import type { Component, ExtractPropTypes } from 'vue'
import type { EditAttrs, GlobalConfig, UseDict } from '@/types'
import componentProps from './props'

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

/**
 * emits类型
 */
export type Emits = {
  /** dictKeys事件 */
  (e: 'onDictKeys', value?: string[]): void
  /** 选中数据更新事件 v-model:selectionData */
  (e: 'update:selectionData', selectionData: 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
}

/**
 * EditableCell和SpanVue组件的props接口
 */
export interface EditableCellAndSpanVueProps {
  /** 行数据 */
  row: Record<string, any>
  /** 表格配置项 */
  item: TableColumn
  /** 合并后的props */
  mergedProps: Props
  /** 全局配置 */
  globalConfig: GlobalConfig
}

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

/**
 * 编辑配置接口
 */
export interface Edit {
  /** 组件 */
  component: Component
  /** 属性 */
  attrs?: EditAttrs
}

/**
 * tableColumn接口
 */
export interface TableColumn {
  /** 表格列的标签 */
  label?: string
  /**
   * 表格列的属性 多级表头的父级表头可不传
   */
  prop?: string
  /**
   * 使用字典
   * 如为boolean则将label作为dictMap的key
   * 如为string或dict.data为string则将dict或dict.data作为dictMap的key
   */
  useDict?: boolean | string | UseDict
  /** 表格列的宽度 */
  width?: string | number
  /** 表格列的最小宽度 */
  minWidth?: string | number
  /** 是否显示表格列 */
  show?: boolean
  /** 表格列的排列方式 */
  align?: string
  /** 表格列是否固定 */
  fixed?: boolean | string
  /** 表格列是否开启排序 */
  sortable?: boolean
  /** 表格列溢出是否显示省略号 */
  showOverflowTooltip?: boolean
  /** 表格列扩展配置 */
  extConfig?: {
    /** 表头配置 */
    header?: Header
    /** 编辑配置 */
    edit?: Edit
    /** 过滤器 */
    formatter?: Function
    /** 类名 */
    class?: string | string[]
    /** 样式 */
    style?: string | Record<string, unknown>
    /** 插槽 为true则在模板代码中添加`<template #prop="{ row }"></template>`即可,可传入string自定义具名插槽名称 */
    slot?: boolean | string
  }
  /** 表格列的子表格列 */
  children?: TableColumn[]
}

/**
 * 选择器配置项
 */
export interface Selection {
  /** 选择器的标签 type为CHECKBOXRADIO、RADIO时表头全选复选框的文本,不设置则为空显示 */
  label?: string
  /** 选择类型 可选值:CHECKBOX(复选) | CHECKBOXRADIO(复选框单选) | RADIO(单选) */
  type?: 'CHECKBOX' | 'CHECKBOXRADIO' | 'RADIO'
  /** 选择器的列宽 */
  width?: number
  /** 选中数据的唯一键 对应tableData中的唯一键 如id */
  key?: string
  /** 允许通过点击行触发单选/复选 默认否 PS:默认不高亮当前行,若需高亮当前行,只需给UseElTable组件设置highlightCurrentRow属性即可 */
  enableRowClick?: boolean
  /** 决定当前行复选框是否可以勾选 传入function,如果是简单禁用逻辑推荐优先使用disabledConfig配置项,复杂逻辑才建议使用本配置项 */
  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?: {
    /** 是否显示边框线 默认和el-table的border保持一致 */
    border?: boolean
    /** 额外表头类名 */
    class?: string
    /** 额外表头样式对象 */
    style?: { padding?: string; borderBottom?: string }
  }
  /** 模式配置项 */
  mode?: {
    /** 模式类型 可选值:PAGE(页面) | MODAL(弹窗) */
    type?: 'PAGE' | 'MODAL'
    /** 表格高度 不设置则默认高度自适应,该配置项设置的是UseELTable组件整体高度,非ElTable组件的高度 */
    height?: string
    /** 表格高度自适应表格底部预留偏移 该配置项由UseElConfigProvider组件统一提供默认值 */
    bottomOffset?: number
  }
  /** 选择器配置项 绑定v-model:selectionData="selectionData"则开启选择器 不显式设置type则默认为CHECKBOX复选 */
  selection?: Selection
  /** 序号列配置项 */
  index?: {
    /** 序号列表头文本 不显式设置则不显示序号列 */
    label?: string
    /** 是否连续的,默认是,即第二页的序号根据第一条最后一个序号进行延续 */
    isContinuous?: boolean
    /** 序号列的列宽 */
    width?: number
    /** 序号列的排列方式 */
    align?: string
    /** 序号列是否固定 */
    fixed?: boolean
  }
  /** 展开列配置项 */
  expand?: {
    /** 是否显示展开列 */
    show?: boolean
    /** 展开列宽度 */
    width?: number
  }
  /** 操作列配置项 */
  operation?: {
    /** 操作列文案 显式设置则开启操作列 */
    label?: string
    /** 操作列列宽 */
    width?: number
    /** 操作列是否固定 */
    fixed?: string
  }
  /** 分页器配置项 */
  pagination?: {
    /** 分页总数 为-1时不显示分页器 */
    total?: number
    /** 分页器与表格的间距 */
    marginTop?: number
    /** 分页器位置 */
    position?: 'left' | 'center' | 'right'
    /** 页码尺寸数组 */
    pageSizes?: number[]
    /** 是否显示分页器背景 */
    background?: boolean
    /** 分页器布局 */
    layout?: string
    /** 分页方法 显式设置则在组件内部自动处理分页逻辑 */
    func?: Function
  }
}

DefaultExtConfig

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

/**
 * 默认扩展配置
 */
export default {
  extraHeader: {
    style: {
      padding: '10px',
      borderBottom: '1px solid var(--el-border-color-light)'
    }
  },
  mode: { type: 'PAGE' },
  selection: { width: 40, enableRowClick: false, align: 'center', fixed: true },
  index: { width: 55, align: 'center', fixed: true, isContinuous: true },
  expand: { show: false },
  operation: { width: 128, fixed: '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

Expose

js
defineExpose({ validRequired })

基于 MIT 许可发布