FxTableV2 虚拟滚动表格
基于 FxFixedSizeGrid / FxDynamicSizeGrid(@fx/components)的虚拟滚动表格,只渲染可见区域的行列,适合大数据量(万级以上)表格。UI 框架无关:纯 Vue + @fx/styles token + useNamespace('table-v2')。
核心能力:固定列(左/右)、单列/多列排序、自定义单元格 / 表头渲染函数、固定底部、空数据、遮罩层、动态行高、手动滚动控制、多层表头、展开行、树形数据。
基础用法
columns 定义列(key / title / dataKey / width,其中 width 必填),data 为数据数组,width / height 必填。下方示例渲染 10000 行,仅可见行参与渲染。
固定列
fixed 属性开启固定列布局后,表格拆分为 main / left / right 三个区域同步滚动。列上设置 fixed: true(默认左侧)、fixed: FixedDir.LEFT 或 fixed: FixedDir.RIGHT 固定到对应方向。
排序
列上设置 sortable: true 后表头出现排序按钮。配合 sort-by(单列受控排序,{ key, order })与 column-sort 事件实现受控排序;排序方向取值用 SortOrder.ASC / SortOrder.DESC。
空数据
data 为空数组时显示空数据占位,可用 empty 具名插槽自定义内容。
固定底部
footer-height 设置底部高度(默认 0 即不显示),用 footer 具名插槽自定义底部内容,底部不参与纵向虚拟滚动。
自定义行样式
row-class 传入字符串或 ({ columns, rowData, rowIndex }) => string 回调,按行返回 className。
遮罩层
overlay 具名插槽覆盖在表格之上(绝对定位),常用于加载中遮罩。插槽内容自行定位。
手动滚动
通过 ref 拿到实例后调用 scrollTo / scrollToLeft / scrollToTop / scrollToRow 编程式滚动。
自适应容器
用 FxAutoResizer 包裹表格,通过默认插槽把容器实测的 { width, height } 传给表格,表格随容器尺寸变化自适应。
多列排序
sort-state(多列排序状态对象,key → order)配合 column-sort 事件实现多列受控排序,每列独立维护排序方向。
自定义单元格
列上设置 cellRenderer 自定义单元格渲染(返回 VNode),headerCellRenderer 自定义表头单元格。
行选择
在列上用 cellRenderer + FxCheckbox 渲染行选择框,headerCellRenderer 渲染表头全选框(含半选态)。
动态行高
设置 estimated-row-height 后启用动态行高模式,行高随单元格内容自适应(配合 cellRenderer 渲染不等高内容)。
跨表区同列高亮
cell-props 回调给每个单元格打标记 + 绑定 mouseenter / mouseleave,配合根 class 实现 main / left / right 三表区同一列联动高亮。
合并单元格
通过 row 具名插槽拿到 { cells, columns, rowData, rowIndex },用 cloneVNode 修改单元格 style(累加宽度 / 拉高高度)并置空被覆盖的 cell,实现列合并 / 行合并。
展开行详情
expand-column-key 指定展开列,行数据携带 children 作为展开内容;用 row 插槽判断展开行(rowData.detail)渲染详情,配合 estimated-row-height 让详情行自适应高度。
树形数据
扁平数据按 parentId 归集为树后传入,expand-column-key 指定展开列,v-model:expanded-row-keys 双向绑定展开状态,展开后子节点自动扁平化插入并按深度缩进。
筛选
用 headerCellRenderer 自定义表头,配合 FxPopover 渲染弹出式筛选面板:点击筛选图标弹出勾选条件与确认 / 重置按钮,确认后切换数据集。
单元格编辑
cellRenderer 根据行内 editing 标记在触发器(div)与编辑器(FxInput)间切换,点击进入编辑、失焦 / 回车退出。
多层表头
header-height 传数组开启多层表头,用 header 具名插槽自定义每一层(拿到 { cells, columns, headerIndex }),把相邻列合并成分组表头;固定列占位用 placeholderSign 识别。
粘性行
fixed-data 传入不参与滚动的固定行(粘在底部),配合 scroll 事件按滚动位置更新粘性行内容、row-class 标记样式。
FxTableV2 API
FxTableV2 Attributes
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| columns | 列定义 | Column[] | [] |
| data | 行数据 | Record<PropertyKey, any>[] | [] |
| data-getter | 自定义取值函数(优先于 dataKey) | (params) => any | — |
| fixed-data | 固定在底部不参与滚动的数据 | any[] | — |
| row-key | 行 key 字段名或取值函数 | KeyType | 'id' |
| width | 表格总宽度(必填) | number | — |
| height | 表格总高度(必填) | number | — |
| max-height | 最大高度(超出滚动) | number | — |
| row-height | 行高(固定行高模式) | number | 50 |
| estimated-row-height | 预估行高,设置后启用动态行高模式 | number | — |
| header-height | 表头高度,多层表头传数组 | number | number[] | 50 |
| footer-height | 底部高度 | number | 0 |
| fixed | 是否启用固定列布局(生成 main/left/right 三表区) | boolean | false |
| use-is-scrolling | 是否追踪滚动状态(注入 isScrolling) | boolean | false |
| indent-size | 缩进尺寸(树形 / 展开行) | number | 12 |
| icon-size | 展开图标尺寸 | number | 12 |
| h-scrollbar-size | 横向滚动条尺寸 | number | 6 |
| v-scrollbar-size | 纵向滚动条尺寸 | number | 6 |
| scrollbar-always-on | 滚动条常驻显示 | boolean | false |
| cache | 上下预渲染行数 | number | 2 |
| header-class | 表头 className | string | HeaderClassNameGetter | — |
| header-props | 表头额外 props | object | ExtractHeaderPropGetter | — |
| header-cell-props | 表头单元格额外 props | object | ExtractHeaderCellPropGetter | — |
| row-class | 行 className | string | RowClassNameGetter | — |
| row-props | 行额外 props | object | ExtractRowPropGetter | — |
| cell-props | 单元格额外 props | object | ExtraCellPropGetter | — |
| class | 根 className | string | — |
| style | 根 style | CSSProperties | — |
| expand-column-key | 展开行列 key | string | — |
| expanded-row-keys | 受控展开行 keys | KeyType[] | — |
| default-expanded-row-keys | 默认展开行 keys(非受控) | KeyType[] | — |
| sort-by | 单列排序状态(受控) | SortBy | {} |
| sort-state | 多列排序状态(受控) | SortState | — |
| row-event-handlers | 行事件处理集合 | RowEventHandlers | — |
FxTableV2 Slots
| 名称 | 说明 | 参数 |
|---|---|---|
| cell | 单元格默认内容(所有列共用) | { cellData, rowData, rowIndex, column, columnIndex } |
| header-cell | 表头单元格默认内容(所有列共用) | { column, columnIndex, headerIndex } |
| row | 自定义整行 | { columns, rowData, rowIndex } |
| header | 自定义整行表头 | { columns, headerIndex } |
| footer | 底部内容 | — |
| empty | 空数据占位 | — |
| overlay | 遮罩层(覆盖在表格之上) | — |
单列自定义优先用
column.cellRenderer/column.headerCellRenderer,cell/header-cell插槽对所有列生效。
FxTableV2 Events
| 事件名 | 说明 | 回调参数 |
|---|---|---|
| column-sort | 列排序触发 | { column, key, order } |
| scroll | 滚动触发 | { scrollLeft, scrollTop } |
| row-expand | 行展开 / 收起 | { expanded, rowData, rowIndex } |
| row-hover | 行 hover | { hovered, rowData, rowIndex } |
| row-height-change | 行高变化(动态行高) | RowHeightChangedParams |
| expanded-rows-change | 展开行 keys 变化 | KeyType[] |
| end-reached | 滚动到底部 | remainDistance: number |
| rows-rendered | 行渲染范围变化 | { rowCacheStart, rowCacheEnd, rowVisibleStart, rowVisibleEnd } |
FxTableV2 Exposes
| 名称 | 说明 | 参数 |
|---|---|---|
| scrollTo | 滚动到指定位置 | ({ scrollLeft?, scrollTop? }) |
| scrollToLeft | 滚动到横向位置 | (scrollLeft: number) |
| scrollToTop | 滚动到纵向位置 | (scrollTop: number) |
| scrollToRow | 滚动到指定行 | (row: number, strategy?: 'auto' | 'smart' | 'center' | 'start' | 'end') |
Column
列定义(width 必填)。
| 属性 | 说明 | 类型 |
|---|---|---|
| key | 列唯一标识(兼做排序 key) | KeyType |
| data-key | 数据字段名(默认渲染取值用) | KeyType |
| title | 列标题 | string |
| width | 列宽(px,必填) | number |
| min-width | 最小列宽 | number |
| max-width | 最大列宽 | number |
| align | 排列方向 | 'left' | 'center' | 'right' |
| fixed | 固定列:true 固定左侧,或指定方向 | true | FixedDir |
| flex-grow | 剩余空间分配 | number |
| flex-shrink | 空间收缩 | number |
| sortable | 是否可排序 | boolean |
| hidden | 是否隐藏 | boolean |
| class | 单元格 className(字符串或 getter) | string | ClassNameGetter |
| header-class | 表头单元格 className | string | HeaderClassGetter |
| style | 列自定义样式 | CSSProperties |
| cell-renderer | 单元格自定义渲染函数 | CellRenderer |
| header-cell-renderer | 表头单元格自定义渲染函数 | HeaderCellRenderer |
内置常量
排序方向与固定列方向用内置常量取值(避免硬编码字符串)。
ts
import { SortOrder, FixedDir, Alignment } from '@fx/components'
SortOrder.ASC // 'asc'
SortOrder.DESC // 'desc'
FixedDir.LEFT // 'left'
FixedDir.RIGHT // 'right'
Alignment.LEFT // 'left'
Alignment.CENTER // 'center'
Alignment.RIGHT // 'right'类型声明
展开查看完整类型定义
ts
type KeyType = string | number | symbol
type RowCommonParams = {
rowData: any
rowIndex: number
}
type ColumnCommonParams<T> = {
columns: Column<T>[]
column: Column<T>
columnIndex: number
}
type CellRendererParams<T> = {
cellData: T
} & RowCommonParams &
ColumnCommonParams<T>
type HeaderCellRendererParams<T> = {
headerIndex: number
} & ColumnCommonParams<T>
type ClassNameGetterParams<T> = {
cellData: T
} & RowCommonParams &
ColumnCommonParams<T>
type DataGetterParams<T> = {
columns: Column<T>[]
column: Column<T>
columnIndex: number
} & RowCommonParams
type DataGetter<T> = (params: DataGetterParams<T>) => T
type ClassNameGetter<T> = (params: ClassNameGetterParams<T>) => string
type HeaderClassGetter<T> = (
params: ColumnCommonParams<T> & { headerIndex: number },
) => string
type CellRenderer<T> = (params: CellRendererParams<T>) => VNode
type HeaderCellRenderer<T> = (params: HeaderCellRendererParams<T>) => VNode
type HeaderClassNameGetter<T> = (params: {
columns: Column<T>[]
headerIndex: number
}) => string
type RowClassNameGetter<T> = (
params: { columns: Column<T>[] } & RowCommonParams
) => string
type ExtraCellPropGetter<T> = (
params: ColumnCommonParams<T> &
RowCommonParams & { cellData: T; rowData: any }
) => any
type ExtractHeaderPropGetter<T> = (params: {
columns: Column<T>[]
headerIndex: number
}) => any
type ExtractHeaderCellPropGetter<T> = (
params: ColumnCommonParams<T> & { headerIndex: number }
) => any
type ExtractRowPropGetter<T> = (
params: { columns: Column<T>[] } & RowCommonParams
) => any
type ColumnSortParams<T> = {
column: Column<T>
key: KeyType
order: SortOrder
}
type ScrollPos = {
scrollLeft: number
scrollTop: number
}
type CellSlotProps<T> = {
class: string
columns: Column<T>[]
column: Column<T>
columnIndex: number
cellData: T
rowData: any
rowIndex: number
isScrolling: boolean
}
type HeaderSlotProps = {
cells: VNode[]
columns: Column<any>[]
headerIndex: number
}
type HeaderCellSlotProps = {
class: string
columns: Column<any>[]
column: Column<any>
columnIndex: number
headerIndex: number
style: CSSProperties
headerCellProps?: any
sortBy: SortBy
sortState?: SortState | undefined
onColumnSorted: (e: MouseEvent) => void
}
type RowSlotProps = {
cells: VNode[]
columns: Column<any>[]
depth: number
style: CSSProperties
rowData: any
rowIndex: number
isScrolling: boolean
}
type RowEventHandlerParams = {
rowKey: KeyType
event: Event
} & RowCommonParams
type RowEventHandler = (params: RowEventHandlerParams) => void
type RowEventHandlers = {
onClick?: RowEventHandler
onContextmenu?: RowEventHandler
onDblclick?: RowEventHandler
onMouseenter?: RowEventHandler
onMouseleave?: RowEventHandler
}
type RowExpandParams = {
expanded: boolean
rowKey: KeyType
} & RowCommonParams
type RowHoverParams = {
event?: MouseEvent
hovered: boolean
rowKey: KeyType
} & Partial<RowCommonParams>
type RowsRenderedParams = {
rowCacheStart: number
rowCacheEnd: number
rowVisibleStart: number
rowVisibleEnd: number
}
type SortBy = { key: KeyType; order: SortOrder }
type SortState = Record<KeyType, SortOrder>
// 内置常量(const 对象,非 enum)
const SortOrder = {
ASC: 'asc',
DESC: 'desc',
} as const
const Alignment = {
LEFT: 'left',
CENTER: 'center',
RIGHT: 'right',
} as const
const FixedDir = {
LEFT: 'left',
RIGHT: 'right',
} as const常见问题
如何在首列渲染勾选框?
由于虚拟表格允许自定义单元格渲染,你可以像自定义单元格示例那样,通过 cellRenderer 自行渲染勾选框(FxCheckbox),并自行维护选中状态。参考行选择示例。
为什么虚拟表格比 FxTable 提供的功能少?
虚拟表格有意提供更少的开箱即用功能,让用户按需实现自己的业务逻辑。集成过多功能会使代码难以维护,对大多数用户而言基础功能已足够。部分关键特性尚未开发。
FxAutoResizer API
容器尺寸自适应组件,配合 FxTableV2 让表格填满父容器并随容器尺寸变化自适应。
FxAutoResizer Attributes
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| disable-width | 是否禁用宽度自适应 | boolean | false |
| disable-height | 是否禁用高度自适应 | boolean | false |
FxAutoResizer Slots
| 名称 | 说明 | 参数 |
|---|---|---|
| default | 自定义内容(通常放 FxTableV2) | { width, height } |
