Appearance
AtTable
沈孟平
最后更新于 2026-04-26 14:03:19
一个经典的组件,一直在迭代,从未满足所有需求~
Warning
本表格默认开启 remote 属性,如果需要前端排序,请设置 remote 属性 为 false。并且关闭 remote 属性后,会有如下问题:
- 在非异步状况下,总页数
page-count是由数据的数量决定的,即使传入page-count也不会生效,如果你希望指定总页数,需要设定remote属性。
核心能力
AtTable 不是简单包一层 NDataTable,而是把中后台列表页最常见的“取数 + 分页 + 排序 + 工具区 + 列配置”收敛到了一个组件里:
- 对接
listApi自动拉取列表数据 - 按约定解析分页字段和总数字段
- 支持远程排序参数映射
- 支持列显示配置、全屏、刷新、尺寸切换
- 支持选择列状态管理
- 支持
fitContent自动算列宽 - 支持通过
needRowSpan做行合并
数据约定
默认情况下,组件会按下面的路径去理解接口返回结果:
ts
{
data: [], // 列表数据,对应 pagerKeys.list
meta: {
total: 0, // 总条数,对应 pagerKeys.total
},
}如果你的接口字段不一致,不需要在每个表格里重复转换,可以优先通过 AtConfigProvider 统一配置 pagerKeys 和 sorterKeys。
使用建议
- 只要你用到了选择、默认展开、保留选中数据等能力,就务必传入稳定的
row-key。 - 如果列里存在
fitContent,其余非自适应列请显式设置width,否则横向滚动宽度无法正确计算。 - 需要真正的前端本地排序时,请显式把继承自
NDataTable的remote设为false。
代码演示
基础示例
自适应容器高度,并且表格内滚动,隐藏分页,默认会开启分页。
默认筛选请求
通过控制台可以查看列表请求已经有默认筛选项了
自适应列宽
开启自适应列宽(有一列存在 fitContent)后,scrollX 由内部计算,注意:此时非自适应列宽的 column 的 width 必须存在。注意,此时每一列的 key 不能重复,否则计算会失效。
大数据量
如果右侧要有固定列,则左侧必须也要有,否则表格布局会错乱。如果不需要左侧固定,可以虚拟一个假的列:{ key: '-1', fixed: 'left', width: 0, render: () => '' }。
TIP
如果开启了虚拟滚动,但是列数不足,则会导致列内容挤在一起无法占满表格宽度,请手动判断是否满足横向虚拟滚动开启条件。
API
TIP
AtTableProps 继承自 NaiveUI DataTable Props
AtTable Props
| 名称 | 类型 | 默认值 | 说明 | 版本 |
|---|---|---|---|---|
| list-api | function | 必填项 | 获取表格数据的接口函数 | |
| columns | AtTableColumns | 必填项 | 表格列配置 | |
| pager-keys | PagerKeys | undefined | 分页相关数据获取的路径字符串或者参数名称 可以在全局配置 | |
| pagination | DataTableProps['pagination'] | undefined | 分页配置 | |
| sorter-keys | SorterKeys | undefined | 排序参数配置, 参考类型声明 | |
| selection | TableSelectionColumn | undefined | 选择列配置 | |
| right-utils | RightUtils | ['size', 'reload', 'fullscreen', 'setting'] | 右侧功能区, 参考类型声明 | |
| size | DataTableProps['size'] | 'medium' | 表格尺寸 | |
| deep-reactive | boolean | false | 对 data 是否采用 ref 包裹还是 shallowRef,一般如果不需要行内编辑,shallowRef 即可,不用深度侦听对象,明显性能会好一些 | |
| default-expand-all | boolean | false | 自动处理异步数据展开 | |
| auto-fetch | boolean | true | 是否自动调取接口获取数据 | |
| header-cls | string | undefined | 表格头部类名 | |
| outer-bordered | boolean | true | 表格外边框 | |
| table-title | string | undefined | 表格标题 | |
| wrapper-cls | string | undefined | 包装器类名 |
AtTable Events
| 名称 | 参数 | 说明 | 版本 |
|---|---|---|---|
| columns-config-change | (keys: any[]) | 列配置变化时触发 | v0.12.41 |
| update:page | (page: number) | 页码变化时触发 | v0.12.45 |
| update:page-size | (pageSize: number) | 分页大小变化时触发 | v0.12.45 |
| data-loaded | (payload: DataLoadedPayload) | 列表数据加载成功后触发,可通过 payload.source 区分触发来源 | v0.13.2 |
AtTable Expose
| 名称 | 类型 | 说明 | 版本 |
|---|---|---|---|
| data-table-ref | Ref<DataTableInst> | NDataTable 的 ref 实例,可调用其原生方法,如 scrollTo、clearFilters 等 | v0.13.1 |
| loading | Ref<boolean> | 表格是否 loading,可对其赋值,更改表格 loading 状态 | |
| data | Ref<any[]> | 表格当前数据 | |
| api-origin-data | any | 接口原始数据 | |
| cached-params | any | 缓存的参数 | |
| columns | Ref<DataTableColumns> | 表格列配置 | |
| pagination | PaginationProps | 分页信息与控制 | v0.12.45 |
| filter | (filters: any) => void | 筛选表格数据 | |
| refresh | () => void | 保持当前参数刷新表格 | |
| get-selected-data | <T>() => { count: number, checkedKeys: DataTableRowKey[], checkedRows: T[] } | 获取当前选中数据 | |
| set-checked-keys | (keys: DataTableRowKey[]) => void | 手动设置选中行数据 | v0.12.4 |
| clear-selection | () => void | 对选中数据进行操作后,建议调用此方法 | v0.12.4 |
| calc-col-width-and-scroll-x | () => void | 手动调用表头宽度计算,通常用于无数据但设置了 fitContent 导致的样式错误问题 | |
| get-columns-config | () => any[] | 获取显示的列配置 | v0.12.41 |
| set-columns-config | (keys: any[] | 'all') => void | 设置需要显示的列, 传入'all'代表重置列配置 | v0.12.41 |
AtTable Slots
| 名称 | 参数 | 说明 | 版本 |
|---|---|---|---|
| title | () | 表格标题 | |
| extra | () | 表格头部额外内容 | |
| extra-right | () | 表格头部右侧额外内容 | |
| empty | () | 空状态内容 |
类型声明
显示类型声明
typescript
/** 分页参数 */
export interface PagerKeys {
page?: string
pageSize?: string
total?: string
list?: string
}
/** 排序参数 */
export interface SorterKeys {
field: { sortField: string, orderField: string }
order: { ascend: string, descend: string }
}
export type RightUtils = ('size' | 'fullscreen' | 'reload' | 'setting')[]
export type AtTableColumns<T = any> = (Omit<DataTableBaseColumn<T>, 'key'> & {
key: keyof T | 'operation'
fitContent?: boolean
/** 可以接受一个比较方法,自己判断是否需要合并 */
needRowSpan?: boolean | ((row: any, key: string, compareValue: any) => boolean)
children?: AtTableColumns
})[]
/** `data-loaded` 事件的触发来源 */
export type DataLoadedSource = 'init' | 'page' | 'page-size' | 'sort' | 'filter' | 'refresh'
/** `data-loaded` 事件载荷 */
export interface DataLoadedPayload<T = any> {
/** 当前页表格数据(已应用 rowSpan 等转换) */
data: T[]
/** 接口返回的原始数据 */
originData: any
/** 当前分页信息 */
pagination: { page: number, pageSize: number, itemCount: number }
/** 触发本次加载的来源 */
source: DataLoadedSource
}