Skip to content

Table 表格 ​

简介 ​

在ElTable基础上,新增分页、查询表单、工具栏等功能,预设了默认的使用方式,简化样板代码。

代码演示 ​

基础使用 ​

要建立一个增删改查表格,可通过以下步骤:

  • 首先在 ConfigProvider 中配置全局列表接口结构;
  • 使用 useTable 配置表格属性,简化表格组件方法的调用;
  • 使用 columns 属性配置表格列;
  • 使用 actionColumn 属性配置操作列;
  • 使用 formProps 属性配置查询表单;
  • 使用 api 属性建立与服务器的联系;
  • 使用对话框等组件创建“新增/编辑”表单。

单元格渲染器 ​

绝大多数列不用写插槽,也不用写 format:给 renderer 一个类型名即可,组件会按类型挑对应的组件渲染。字符串是简写,需要传属性时改成对象写法:

ts
{ prop: 'status', label: '状态', renderer: { type: 'tag', options: statusOptions } }

renderer 本身默认就是 'text',所以 renderer: 'text' 不用写;对象写法里 type 同样可以省,省了按 text 处理 —— 只想加个占位或兜底字段时直接写 { empty: '—' } 就行。

同一份 columns 里可以混用。下面这张表从左到右依次是 click(订单号,点了有回调)、tag(状态,颜色由 options 决定)、boolean(发货要求,真值才染成标签)、longtext(收货地址)、datetime(付款时间)、switch(到货通知,切换即调用 api)。media / date 见基础使用。

几个值得先知道的点:

  • tag 的 options 直接吃后台常见的那份 [{ label, value, tag }],值比较对 '1' 与 1 是宽松的;
  • boolean 的判定不是 Boolean(value),0 / '0' / false 都算假 —— 后端把枚举回成字符串时也不会把「关闭」显示成「开启」;
  • longtext 默认 3 行截断且可展开,单元格里不需要额外的全文出口;
  • switch 的 api 抛错时开关会弹回原位、不留下假的选中态。

空值与兜底 ​

所有渲染器都支持 empty 与 fallback 两个通用选项:前者是空值时的占位,后者按顺序从当前行取第一个非空字段。

fallback 里不用把 prop 再写一遍 —— 单元格的值本来就是按 prop 取的,只有它为空时才顺着 fallback 往下找,所以那里只列备用路径。

示例里同样是「备注」的两列,没配置的那列空值就是一片空白,配了 empty 的显示破折号;最后一列的 prop 是 contact.mobile,为空时退到 mobile,两者都空才显示占位文案。

金额 ​

金额类有三个类型,区别只在「值本身是什么单位」:

类型值的单位显示
usd美元$1,280.00
cny人民币元¥1,280.00
cent美分除以 100 后按美元显示

千分位和币种符号交给 Intl:人民币默认 zh-CN(¥),美元默认 en-US($),要统一成同一种语言就传 props.locale。小数位默认 2 位,用 props.precision 改。

两个边界:0 会正常显示成 ¥0.00,不会被当成空值;非数字的值原样输出,不会渲染出 NaN。

操作按钮的声明 ​

操作列上的按钮可以直接用 actions 声明,不必再开一个插槽手写 <co-table-action>——底层渲染的就是它,所以 TableActionItemProps 的字段(label / icon / type / popconfirm / dropdown …)全都照旧。

actions 推荐写成工厂 (row, $index) => [...]:它在每次渲染时求值,每一项都能按当前行决定,工厂里读到的响应式值(权限码、开关状态…)也会被收集、跟着更新。

ts
actionColumn: {
  label: '操作',
  fixed: 'right',
  minWidth: 210,
  actions: (row) => [
    {
      visible: hasCode(AuthCode.USER_UPDATE),
      label: '编辑',
      icon: 'bi bi-pencil',
      onClick: () => upsert.edit(row),
    },
    {
      label: '删除',
      icon: 'bi bi-trash',
      type: 'danger',
      popconfirm: {
        title: `确定删除「${row.nickname}」?`,
        confirm: () => deleteUser(row.id).then(reload),
      },
    },
  ],
}

写成静态数组(actions: [...])也能用,但数组只在 setup 阶段求值一次:visible: hasCode(...) 会变成快照,权限后到时不会重算。确实不需要行数据时再这么写。

按钮之间的分割线默认显示,用 divider: false 关掉;不传则走全局配置 config.tableAction.divider。

筛选条件与 url 同步 ​

useTable 把「筛选模型 → 请求参数 → url」这条链路收在一起,顺序由内部保证(模型先于首次请求存在、formSchemes 在 computed 里求值、重查回调等表格挂载之后再注册):

  • formSchemes:筛选表单的方案,推荐写成工厂 (model) => [...] —— 工厂里能拿到筛选模型,读到的响应式值也会被收集;各项的 modelValue 同时就是该字段的初值(与 FormQuery 的 schemes 约定一致);
  • urlFields:声明哪些筛选字段与 url 双向同步(不叫 fields,因为 fields 在 cosey 里已经分别指表单暴露的 FormItemContext[]、导出时的列 prop,而同一个 useTable 的返回值上还有 getFieldsValue() —— 那是查询表单的全部字段值);
  • 第二个返回值上的 model:表格之外也能直接读写筛选条件。

带条件跳转时,接收页声明 urlFields,url 上的条件会在第一次请求之前落到模型(而不是先拉全量再补一次):

ts
const [tableProps] = useTable<CommentQuery>({
  api: getComments,
  columns: [...],
  // 初值写在各项的 modelValue 上
  formSchemes: (model) => [
    { prop: 'content', label: '评论内容', modelValue: '' },
    // url 上是 ?postId=18,模型里的字段也叫 postId
    pinnedSelectScheme({ prop: 'postId', label: '所属文章', select: postSelectConfig, model }),
  ],
  urlFields: { postId: parseNumber },
});

跳转方用 compactQuery 拼 url,没有值的字段不会留下 ?postId=:

ts
router.push({ path: '/blog/comments', query: compactQuery({ postId: row.id }) });

url 参数名与模型字段名不一致时用 param 声明(给数组表示兼容多个别名):

ts
urlFields: {
  postTypeId: { param: 'type', parse: parseNumber },
}

关联字段用 pinnedSelectScheme 而不是直接写 fieldProps:co-remote-select 只在挂载时按 immediate 拉一次数据来解析当前值的标签,而 url 变化时组件不会重建,标签会退化成裸 id(例如显示 17)。它给 scheme 挂了一个以当前值为内容的 key,值一变就重建 form-item,重新解析出标签。

批量操作条 ​

开启 batch 后,勾选任意行会在表格上方出现一条操作栏,内容由 #batch 插槽提供,插槽参数里有 count / rows / clear。

选中态不在表格里时(例如 表格选择器 自己维护选中集),用 batch.rows 把数据交过去,操作栏就只看这份数据,不再依赖勾选列。

行内编辑 ​

给列加 editable 即把该单元格换成 co-field,值变化会直接写回 row[prop],同时触发 cell-change。默认点击单元格不触发行选中,可用 editable.stopPropagation: false 关掉。

行拖拽 ​

开启 row-draggable 后,表格最左侧会出现一列拖拽手柄,只有按住手柄才能拖动行(整行不可拖,避免误触和选不中文字),本地数据会立即重排并抛出 row-move({ from, to, rows })。顺序要持久化时,记得在事件里提交给服务端。

落点以行的中线为界:拖到上半部分插到该行之前,下半部分插到该行之后。高亮线(行上边框 / 下边框)画的就是真实的插入位置,拖到最后一行下半部分时线在表格底部。

拖拽只对普通表格生效。树形数据、el-table-v2 这类行数与数据条数不一致的场景会自动失效。

列配置持久化 ​

传 table-key 后,「列设置」里调整的顺序与显隐会记在本地存储里,刷新或下次进入同一表格时自动恢复;点「恢复默认」会清掉记录。

vue
<co-table table-key="user-list" :columns="columns" />

API ​

useTable ​

useTable(options) 配置表格属性,返回 [tableProps, expose]:表格属性直接 v-bind 给 co-table,组件方法与筛选模型在第二个元素上。

options 可以是对象,也可以是 getter / computed —— 后者让整份属性跟随响应式数据重算(例如列里用到 t()、权限码):

ts
const [tableProps, { reload }] = useTable(() => ({
  api: getRoles,
  columns: [{ prop: 'name', label: t('rbac.roleName') }],
}));
属性描述类型默认值
api请求数据的函数(...args: any[]) => Promise<any>-
columns定义表格列;传函数可跟随响应式数据重算MaybeRefOrGetter<TableColumnProps[]>[]
actionColumn定义表格操作列MaybeRefOrGetter<TableColumnProps>-
formSchemes筛选表单的方案;各项的 modelValue 即该字段初值TableSchemes-
urlFields与 url 双向同步的筛选字段Partial<Record<keyof T & string, QueryFieldConfig>>-
onQueryChangeurl 上的筛选条件变化后怎么重查:'submit' 回到第 1 页、'reload' 保留当前页'submit' | 'reload''submit'
formProps查询表单的其它配置;model / resetValues 由内部接管,schemes 也可写在这里(顶层 formSchemes 优先)FormQueryProps-
其它透传给 co-table,如 pagination / toolbarConfig / height--

返回 UseTableResult(元组):

位置属性描述类型
0tableProps表格属性,v-bind 给 co-tableComputedRef<TableProps>
1model筛选模型T
1queryFilterurl 同步能力(fieldKeys / queryParams / onChange)QueryFilter
1其它展开 TableExpose,如 reload / submit / getPaginationTableExpose

兼容:formProps.model 仍可传入,此时模型由调用方提供(必须是 reactive 对象), 同名 formSchemes 项的 modelValue 不再生效;正常用法不需要传,控制台会给出提示。

TableSchemes ​

ts
type TableSchemes<T> =
  | FormQueryScheme[] // 静态
  | MaybeRefOrGetter<FormQueryScheme[]> // 跟随响应式数据
  | ((model: T) => FormQueryScheme[]); // 推荐:能拿到筛选模型

QueryFieldConfig ​

ts
type QueryFieldConfig<T = any> =
  | ((value: RawQueryValue) => T | undefined) // 解析函数
  | {
      parse?: (value: RawQueryValue) => T | undefined; // url → 模型
      format?: (value: T) => QueryInput; // 模型 → url
      param?: string | string[]; // url 参数名,数组表示兼容多个别名
    };

内置解析器:parseNumber、parseString、parseStringArray。

QueryFilter ​

属性描述类型
fieldKeys受 url 管辖的字段名(模型字段名)(keyof T & string)[]
queryParams模型字段名 → url 主参数名Partial<Record<keyof T & string, string>>
onChange注册「url 上的条件变了」之后的回调,通常传表格的 submit(handler: () => void | Promise<void>) => void
resetValues交给 formProps.resetValues,点「重置」时连 url 一起清空() => Record<string, undefined>

pinnedSelectScheme ​

生成关联筛选字段的 scheme,让「当前值不在选项里」时也能正确显示标签。

属性描述类型
prop字段名,同时是筛选模型的 keystring
label筛选栏里的标签string
select远程选择器配置object(含 api / props / keys 等)
model筛选模型object

withPinnedValue ​

给远程选择器配置包一层:挂载即加载一次,当前值不在结果里就单独取回来置顶。

参数描述类型
config远程选择器配置object
getPinnedValue读取当前值,支持数组() => string | number | (string | number)[] | null
optionsvalueKey / matchParamsobject

TableProps ​

继承 element-plus 的 Table 属性 并有以下属性:

属性描述类型默认值
api请求数据的函数(...args: any[]) => Promise<any>-
immediate是否在挂载后立即请求数据booleantrue
columns定义表格列TableColumnProps[][]
action-column定义表格操作列TableColumnProps-
pagination设置分页属性或隐藏分页组件boolean | PaginationPropstrue
get-expose获取 TableExpose,会在组件创建后调用(expose: TableExpose) => void-
form-props自定义查询表单属性FormQueryProps-
transform-params请求之前对参数进行处理(params: Record<string, any>) => any-
transform-response请求之后对返回值进行处理(res: any) => any-
parallel-fetch会和 api 同时请求,并接收相同的参数,并影响加载状态。常用于获取统计数据。(...args: any[]) => Promise<any> | any-
toolbar-config设置工具栏按钮;不传时继承全局 TableConfig['toolbarConfig']ToolbarConfig | booleantrue
keys定义接口相关的键名TableConfig['keys']defaultTableConfig['keys']
stats-columns定义统计列MaybeRef<TableStatisticsColumn[]>-
stats-data统计数据MaybeRef<Record<string, any>>-
summary-properties设置默认合计时仅展示指定列数据string[]-
transform-summary转换合计列数据(sums: any[]) => any[]-
split分隔表格主体和头部等,显示分隔条booleanfalse
table-key表格标识,设置后列顺序/显隐会持久化到本地存储string-
batch批量操作条,选中行后显示;内容由 #batch 插槽提供boolean |false
row-draggable行拖拽排序booleanfalse

TableConfig ​

供 ConfigProvider 的 table 属性使用,设置表格的全局默认值。

ts
interface TableConfig {
  keys?: {
    list?: string;
    total?: string;
    page?: string;
    pageSize?: string;
    orderBy?: string;
    orderType?: string;
    asc?: string;
    desc?: string;
  };
  pagination?: Partial<PaginationProps>;
  toolbarConfig?: ToolbarConfig | boolean;
  height?: number | string;
  split?: boolean;
}

toolbarConfig 与表格上的 toolbar-config 逐项合并,优先级为 默认值 → 全局 → 表格:

vue
<!-- 全局隐藏导出和全屏按钮 -->
<ConfigProvider :table="{ toolbarConfig: { export: false, fullScreen: false } }">
  <!-- 继承全局:只剩刷新和列设置 -->
  <co-table :columns="columns" :api="listApi" />
  <!-- 某张表仍要导出:局部覆盖 -->
  <co-table :columns="columns" :api="listApi" :toolbar-config="{ export: true }" />
  <!-- 整条工具栏关掉 -->
  <co-table :columns="columns" :api="listApi" :toolbar-config="false" />
</ConfigProvider>

全局设为 false 时所有表格默认不显示工具栏,单张表格传 true 或对象即可打开。

defaultTableConfig ​

ts
const defaultTableConfig = {
  keys: {
    /**
     * 响应数据对象中“列表数据”的 key
     */
    list: 'list',

    /**
     * 响应数据对象中“总记录数”的 key
     */
    total: 'total',

    /**
     * 请求url查询参数中“当前页数”的参数名
     */
    page: 'page',

    /**
     * 请求url查询参数中“每页条数”的参数名
     */
    pageSize: 'pageSize',

    /**
     * 请求url查询参数中“排序列”的参数名
     */
    orderBy: 'orderBy',

    /**
     * 请求url查询参数中“排序方向”的参数名
     */
    orderType: 'orderType',

    /**
     * 排序方向中“升序”的值
     */
    asc: 'asc',

    /**
     * 排序方向中“降序”的值
     */
    desc: 'desc',
  },
  pagination: {
    layout: 'prev, pager, next, sizes, jumper, total',
    currentPage: 1,
    pageSize: 10,
  },
  toolbarConfig: true,
  height: undefined,
  split: false,
};

ToolbarConfig ​

属性描述类型默认值
reload是否显示重载按钮booleantrue
export是否显示导出按钮,传对象可指定导出文件名boolean |true
fullScreen是否显示全屏按钮booleantrue
setting是否显示列设置按钮booleantrue

TableStatisticsColumn ​

属性描述类型
label显示的标题VNodeChild
prop字段名称 对应列内容的字段名string
format用来格式化内容(value: any) => any

TableSlots ​

继承 element-plus 的 Table 插槽 和以下插槽,并能定义 TableColumnProps["slots"] 中同名的插槽。

插槽描述属性
toolbar-left自定义工具栏左边的内容-
toolbar-right自定义工具栏右边的内容-
before-body自定义主体前面内容(工具栏上面)-
before-body-plain自定义主体前面内容(工具栏上面),没有包裹容器-
before-table自定义表格前面内容(工具栏下面)-
before-table-plain自定义表格前面内容(工具栏下面),没有包裹容器-
batch批量操作条的内容
submit查询区的提交按钮,透传给内嵌的 FormQuery{ submitting: boolean; submit: () => any | Promise<any> }
reset查询区的重置按钮,透传给内嵌的 FormQuery
button整块替换查询区的按钮区,优先于 submit / reset,透传给内嵌的 FormQuery{ submitting: boolean; submit: () => any | Promise<any>; reset: () => any }

TableEmits ​

继承 element-plus 的 Table 事件,并在 row-draggable 开启时提供:

事件描述类型
row-move行拖拽结束后触发({ from: number; to: number; rows: any[] }) => void
cell-change行内编辑的值发生变化时触发({ row: any; prop: string; value: any; index: number }) => void

TableExpose ​

继承 element-plus 的 Table Exposes ,以及 FormQueryExpose,并支持以下属性。

属性描述类型
reload刷新表格数据() => Promise<void>
expandAll展开所有() => void
collapseAll折叠所有() => void
getFetchParams获取接口请求参数() => Record<string, any>
getFullFetchParams获取接口所有请求参数,包括分页() => Record<string, any>
setData设置表格数据(data: any[]) => void
getData获取表格数据() => any[]
getRootEl获取根元素() => HTMLElement | null
getPagination获取分页数据() => { page: number; pageSize: number; }
reset重置表单(values?: Record<PropertyKey, any>) => Promise<void>

TableColumnProps ​

继承 element-plus 的 Table-column 属性,并支持以下属性。

属性描述类型默认值
slots定义插槽或声明插槽名称TableColumnPropsSlots-
renderer使用内置渲染器进行渲染RendererType'text'
hidden是否隐藏当前列booleanfalse
columns定义嵌套的表格列TableColumnProps[]-
tooltip设置列头提示框string-
format格式化数据(cellValue: any, row: any, column: TableColumnCtx<any>, index: number) => VNode | string-
actions操作按钮,与 slots 同时存在时以它为准TableColumnActions-
divider操作按钮之间的分割线,仅在 actions 下生效boolean-
editable行内编辑,传 true 使用默认输入框boolean | TableColumnEditable-

TableColumnEditable ​

ts
interface TableColumnEditable {
  /** 编辑器类型,默认 input,可选值同 co-field 的 type */
  type?: FieldType;
  /** 传给编辑器的属性,不要传 modelValue */
  props?: Record<string, any>;
  /** 点击编辑器时阻止行点击/选中,默认 true */
  stopPropagation?: boolean;
  /** 值变化回调;不传时直接写回 row[prop] */
  onChange?: (value: any, row: any, index: number) => void;
}

TableColumnActions ​

ts
type TableColumnActions =
  | TableActionProps['actions'] // 数组:直接渲染
  | ((row: any, $index: number) => TableActionProps['actions']); // 工厂:推荐,可按行决定

工厂在每次渲染时求值,因此 visible / onClick / popconfirm 里都能用当前行,读到的响应式值也会跟着更新。

TableColumnPropsSlots ​

当值设置为非对象时,表示设置默认插槽。各个插槽的值可以是字符串或函数,如果是字符串,则从 TableSlots 中匹配对应的插槽内容。

ts
type TableColumnPropsSlots =
  | string
  | ((props: { row: any; column: any; $index: number }) => any)
  | {
      default?: string | ((props: { row: any; column: any; $index: number }) => any);
      header?: string | ((props: { column: any; $index: number }) => any);
      filterIcon?: string | ((props: { filterOpened: boolean }) => any);
    };

RendererType ​

每个值都包含“字符串”和“对象”类型,对象类型用于自定义配置,例如:

ts
{
  renderer: 'longtext';
}
ts
{
  renderer: {
    type: 'longtext',
    props: {
      rows: 2
    }
  }
}

所有类型都支持下面两个通用选项,可以写在任意一种对象写法里:

参数描述类型默认值
empty空值(null / undefined / '' / [])时的占位,0 与 false 不算空string-
fallback兜底字段,主字段为空时按顺序从当前行取第一个非空值,路径写法同 lodash.getstring[]-

对象写法里的 type 可以省,省了按 text 处理,所以只配置通用选项时写 { empty: '—' } 即可;fallback 也不必重复 prop,那里只列备用字段。

text ​

原样输出文本,也是 renderer 的默认值(不写就是它)。

date ​

格式化为 YYYY-MM-DD。

datetime ​

格式化为 YYYY-MM-DD HH:mm:ss。

usd ​

按美元显示,带 $ 和千分位。值本身是美元,非数字原样输出。

参数描述类型
props小数位与符号语言

cny ​

按人民币元显示,带 ¥ 和千分位。

参数描述类型
props小数位与符号语言

cent ​

值是美分:除以 100 后按美元显示,其余与 usd 一致。后端金额用整数分存储时用这个,避免前端各处自己除。

参数描述类型
props小数位与符号语言

media ​

使用 MediaCard 组件渲染。

参数描述类型
props自定义组件的 propsMediaCardProps

mediagroup ​

使用 MediaCardGroup 组件渲染。

参数描述类型
props自定义组件的 propsMediaCardGroupProps

tag ​

使用 ElTag 组件渲染。值可以是单个,也可以是数组(每一项一个标签)。

参数描述类型
options值 → 文案 + 颜色的映射,未命中时回落原值{ label: string | number; value: string | number | boolean; tag?: TagProps['type'] }[]
path每一项是对象时,从这个路径取文案string | string[]
props自定义组件的 propsElTagProps

boolean ​

把值渲染成「是 / 否」文案,也可以顺手染成标签。

参数描述类型
trueText真值文案,默认取 co.common.yesstring
falseText假值文案,默认取 co.common.nostring
trueTag真值的标签颜色,传了才渲染成标签ElTagProps['type']
falseTag假值的标签颜色,传了才渲染成标签ElTagProps['type']

真值判定为 true / 1 / '1',其余都算假。

longtext ​

使用 TypographyParagraph 组件渲染,默认 3 行截断且可展开。

参数描述类型
props自定义组件的 propsTypographyParagraphProps(可覆盖 ellipsis)

switch ​

使用 ElSwitch 组件渲染。

参数描述类型
api更新数据接口(value: any, row: any) => Promise<any>
props自定义组件的 propsSwitchProps

click ​

使用 ElLink 组件渲染。

参数描述类型
props自定义组件的 propsLinkProps
format格式化内容TableColumnProps['format']
onClick点击时的回调(params: { row: any; value: any; index: number; column: TableColumnCtx<any>; }) => void

TableActionProps ​

属性描述类型默认值
actions定义操作按钮TableActionItemAtom[] | TableActionItemAtom[][][]
divider是否显示分割线booleantrue

TableActionItemAtom ​

ts
type TableActionItemAtom =
  | TableActionItemProps
  | null
  | undefined
  | boolean
  | TableActionItemAtom[];

TableActionItemProps ​

继承 element-plus 的 Button Attributes 并有以下属性:

属性描述类型默认值
label按钮内容string-
popconfirm定义气泡确认框PopconfirmProps & { confirm?: (event: MouseEvent) => any; cancel?: (event: MouseEvent) => void; }-
onClick点击按钮时出发(event: MouseEvent) => void-
visible是否显示按钮booleantrue
icon按钮前面的iconIconProps['name']-

TableActionConfig ​

ts
interface TableActionConfig {
  itemProps?: TableActionItemProps;
}

基于 MIT 许可发布