跳转到内容

表格 table

数据展示组件。三层同源:无头内核给出解剖与状态机,Vue 组件与自定义元素只是它的两层外壳,行为完全一致。

示例

基础用法

columns 是列号与列宽的唯一事实源,rows 是行序与行号的唯一事实源,标记只管长相

团队成员
姓名
部门
职级
赵一
平台研发
P6
钱二
前端体验
P7
孙三
基础架构
P6
李四
质量保障
P5

排序

列上标了 sortable 才认排序把手;按住 Shift 点是追加到排序链,裸点是整条链换成这一列

姓名
部门
职级
赵一
平台研发
P6
钱二
前端体验
P7
孙三
基础架构
P6
李四
前端体验
P5
排序链:(无)

多选

selectionMode 默认 none,声明 multiple 才有选择机制;选择列也要在 columns 里占一条,否则右侧列号串位

姓名
部门
赵一
平台研发
钱二
前端体验
孙三
基础架构
李四(禁用)
质量保障
选中:u2

行展开

行上标了 expandable 才认展开把手与左右方向键;详情行占一个真实行号,收起只加 hidden 不卸载内部节点

订单号
金额
XH-2026-0001
¥ 1,280
明细:键盘 ×1、鼠标 ×2
展开:o1

密度

size 只落成 root 的 data-size,换的是单元格纵向内边距与字号;三档并排,差别在行高上

sm 紧凑
姓名
职级
赵一
P6
钱二
P7
孙三
P6
李四
P5
周五
P7
缺省
姓名
职级
赵一
P6
钱二
P7
孙三
P6
李四
P5
周五
P7
lg 宽松
姓名
职级
赵一
P6
钱二
P7
孙三
P6
李四
P5
周五
P7

空态与加载态

两个状态节点常挂着只靠 hidden 显隐:表体为空且在取数时露加载态,取数完了没有行才露空态

任务
负责人
还没有任务,点「取数」拉一份。

脚注合计

footer 把行号空间的最后一行留给脚注;脚注单元格不属于任何数据行,也就没有选中与禁用可言

采购清单
条目
数量
金额
键盘
2
¥ 1280
鼠标
3
¥ 447
显示器支架
1
¥ 320
合计
6
¥ 2047

表头吸顶与列吸附

root 自己就是那个滚动容器:stickyHeader 钉住表头,列上标 sticky 的钉住那一列

姓名
部门
城市
分机
邮箱
员工 1
平台研发
杭州
8100
member1@example.com
员工 2
前端体验
上海
8101
member2@example.com
员工 3
基础架构
北京
8102
member3@example.com
员工 4
质量保障
成都
8103
member4@example.com
员工 5
平台研发
杭州
8104
member5@example.com
员工 6
前端体验
上海
8105
member6@example.com
员工 7
基础架构
北京
8106
member7@example.com
员工 8
质量保障
成都
8107
member8@example.com
员工 9
平台研发
杭州
8108
member9@example.com
员工 10
前端体验
上海
8109
member10@example.com
员工 11
基础架构
北京
8110
member11@example.com
员工 12
质量保障
成都
8111
member12@example.com
员工 13
平台研发
杭州
8112
member13@example.com
员工 14
前端体验
上海
8113
member14@example.com
员工 15
基础架构
北京
8114
member15@example.com
员工 16
质量保障
成都
8115
member16@example.com

单选

selectionMode 给 single:选中集合最多一个元素,点已选中的那行再点一次就清空,焦点行按空格同理

套餐
价格
入门版
¥ 0 / 月
团队版
¥ 99 / 月
企业版
¥ 399 / 月
已选:p2

跨列单元格

colspan 从它自己那一列往后算,报成 aria-colspan;1 与省略同义,所以只在真跨了列时写

交付单量
小组
上半年
下半年
平台研发
42
51
前端体验
36
39
全年
168

单元格就地编辑

表体的方向键与 Home/End 是挂在 body 上的冒泡监听,可编辑控件上掐断冒泡这些键就回归输入框自己

采购清单
条目
数量
备注
键盘
鼠标
显示器支架
合计 6 件

多行表头与表头分组

表头写几行就是几行;分组格的跨列数与两行表头的行号由标记自报,columns 仍只登记叶子列

季度交付单量
上半年
下半年
小组
Q1
Q2
Q3
Q4
平台研发
12
15
18
21
前端体验
9
11
14
16
基础架构
7
8
10
12

列过滤

过滤把手是列标题里的一段内容,过滤结果就是宿主算好后传进来的那份 rows;表头是表体的兄弟,把手上的按键不会被表体收走

姓名
部门
城市
赵一
平台研发
杭州
钱二
前端体验
上海
孙三
基础架构
北京
李四
前端体验
杭州
周五
质量保障
成都
吴六
平台研发
上海
命中 6 / 6 人 · 部门:不限 · 城市:不限

树形表格

rows 按契约就是一条已摊平的可见行序列:层级三件套逐行自报,缩进落在首格的内边距上

组织
负责人
研发中心
赵一
服务端组
周五
展开:rd

拖拽调列宽

列宽的事实源是 columns[].width,把手只是列标题里的一段标记:按下量起点,移动改宽度,连接层随即写进列标题与整列单元格

拖动列标题右侧那条竖线
姓名
部门
城市
赵一
平台研发
杭州
钱二
前端体验
上海
孙三
基础架构
北京
李四
质量保障
成都
列宽:姓名 120px · 部门 150px · 城市 120px

只渲窗口内的行

全量 rows 照常交给 root(那只是行序与行号的元信息,不产生 DOM),标记里只渲可见那一段,首尾用两块空白撑出真实滚动高度

编号
姓名
部门
#1
员工 1
平台研发
#2
员工 2
前端体验
#3
员工 3
基础架构
#4
员工 4
质量保障
#5
员工 5
平台研发
#6
员工 6
前端体验
#7
员工 7
基础架构
#8
员工 8
质量保障
#9
员工 9
平台研发
#10
员工 10
前端体验
#11
员工 11
基础架构
#12
员工 12
质量保障
#13
员工 13
平台研发
#14
员工 14
前端体验
#15
员工 15
基础架构
#16
员工 16
质量保障
#17
员工 17
平台研发
#18
员工 18
前端体验
共 2000 行,此刻在 DOM 里的是第 1 – 18 行

产物

自定义元素<xh-table>
Vue 组件XhTableBody XhTableCaption XhTableCell XhTableColumnHeader XhTableEmpty XhTableExpandTrigger XhTableExpandedRow XhTableFooter XhTableHeader XhTableLoadingState XhTableRoot XhTableRow XhTableRowSelectTrigger XhTableSelectAllTrigger XhTableSortTrigger
组合式函数useTable
状态机tableMachine
皮肤@xihan-ui/styles/table.css

解剖

部件名即 data-part 属性值,也是皮肤的选择器。加粗的是必备部件,不渲染它组件不工作(Web Components 适配器会在诊断通道上报 wc.missing-part)。

data-scope="table"root · header · body · footer · row · column-header · cell · caption · select-all-trigger · row-select-trigger · sort-trigger · expand-trigger · expanded-row · empty · loading-state

Props

属性类型必填说明
columnsTableColumnDef[]列定义,列号与列总数的唯一事实源。缺省为空表。
rowsTableRowDef[]行定义,行序与行号的唯一事实源。缺省为空表。
sortTableSortDescriptor[]排序链。给定即受控:cell 直读 prop,写只发 onSortChange 不落内部值。
defaultSortTableSortDescriptor[]
selectionTableSelection选中集合。给定即受控,语义同上。
defaultSelectionTableSelection
expandedstring[]展开集合。给定即受控,语义同上。
defaultExpandedstring[]
selectionModeTableSelectionMode默认 none:不声明则没有选择机制,行也不报 aria-selected。
loadingboolean数据在路上:root 报 aria-busy,表体为空时加载态节点显形。
emptyboolean显式声明表体为空;缺省按 rows 是否为空推导。
stickyHeaderboolean表头吸顶:只落 data-sticky,钉住的实现归皮肤。
footerboolean表格带脚注行。行号空间的最后一行留给它,aria-rowcount 也把它算进去。
loopboolean上下键走到首尾是否回绕,默认 false。
dirDirection文字方向,默认 ltr;只对调左右方向键的「展开/收起」语义。
sizeSize密度:sm / md / lg。只换单元格的纵向内边距与字号,列宽算法不受影响。
onSortChange(details: TableSortChangeDetails) => void
onSelectionChange(details: TableSelectionChangeDetails) => void
onExpandedChange(details: TableExpandedChangeDetails) => void

状态机

状态idle

事件SORT.SET · SORT.TOGGLE · SELECTION.SET · ROW.SELECT · SELECTION.ALL_TOGGLE · EXPANDED.SET · ROW.EXPAND · ROW.COLLAPSE · ROW.EXPAND_TOGGLE · ROW.FOCUS · TABLE.BLUR

connect API

useTable 产出的对象。getXxxProps() 铺到对应部件的宿主元素上,其余是可读状态与操作入口。

成员类型说明
columnsreadonly TableColumnDef[]作者给的列定义。
rowsreadonly TableRowDef[]作者给的行定义。
visibleRowsreadonly TableVisibleRow[]展开摊平后的可见行序列(详情行插在它所属数据行之后)。
sortTableSortDescriptor[]
selectionTableSelection
selectionStateTableSelectionState全选把手的三态,只按可选行(未禁用)算。
selectionModeTableSelectionMode
expandedValuestring[]
focusedRowstring | null焦点锚点;焦点不在表体里时为 null。
loadingboolean
emptyboolean表体为空(显式声明或 rows 为空)。
rowCountnumberaria-rowcount:表头行 + 可见行 + 脚注行。
columnCountnumberaria-colcount:列定义的条数。
isSelected(value: string) => boolean
isExpanded(value: string) => boolean
sortDirection(value: string) => TableSortDirection | null该列当前的排序方向;不参与排序时为 null。
sortPriority(value: string) => number该列在排序链里的优先级,1 起算;不参与排序时为 0。
setSort(next: TableSortDescriptor[]) => void
toggleSort(value: string, options?: { append?: boolean }) => void
setSelection(next: TableSelection) => void
selectRow(value: string) => void
toggleSelectAll() => void
setExpandedValue(next: string[]) => void
expandRow(value: string) => void
collapseRow(value: string) => void
toggleExpandRow(value: string) => void
getRootProps() => T['element']
getCaptionProps() => T['element']
getHeaderProps() => T['element']
getBodyProps() => T['element']
getFooterProps() => T['element']
getHeaderRowProps() => T['element']表头那一行:恒占行号空间的第 1 行。
getFooterRowProps() => T['element']脚注那一行:占行号空间的最后一行。
getRowProps(props: TableRowProps) => T['element']
getColumnHeaderProps(props: TableColumnProps) => T['element']
getCellProps(props: TableCellProps) => T['element']
getSelectAllTriggerProps() => T['element']
getRowSelectTriggerProps(props: TableRowProps) => T['element']
getSortTriggerProps(props: TableColumnProps) => T['element']
getExpandTriggerProps(props: TableRowProps) => T['element']
getExpandedRowProps(props: TableRowProps) => T['element']
getEmptyProps() => T['element']
getLoadingStateProps() => T['element']

键盘

规格出处:W3C APG

按键生效条件行为
Tab / Shift+Tabfocus outside the table body表体只占一个 Tab 位:焦点进入锚点行,无锚点时先落 body 再由它转投;再按一次 Tab 整体离开表体
ArrowDownfocus in table body焦点移到下一个可见数据行(禁用行跳过;详情行不是落点;loop 默认关,末行不回绕)
ArrowUpfocus in table body焦点移到上一个可见数据行(禁用行跳过;loop 默认关,首行不回绕)
Homefocus in table body焦点移到首个可见数据行
Endfocus in table body焦点移到末个可见数据行
Spacefocus on row, selectionMode 非 none 且该行未禁用切换焦点行的选中(单选替换、复选增删);选不动时不吞这个键,页面照常滚动
ArrowRightfocus on 可展开且收起的行(dir=rtl 时改由 ArrowLeft 承担)就地展开当前行,焦点不动;不可展开、已展开或禁用的行上什么都不做且不吞键
ArrowLeftfocus on 可展开且已展开的行(dir=rtl 时改由 ArrowRight 承担)就地收起当前行,焦点不动;其余情形什么都不做且不吞键
Enter / Spacefocus on sort-trigger, 该列 sortable排序方向按 升序 → 降序 → 不排序 循环;按住 Shift 是追加到排序链而不是替换整条链
Enter / Spacefocus on select-all-trigger, selectionMode=multiple当前可选行全选中就整段清空,否则整段选上;三态由 aria-checked 报出(半选为 mixed)

Released under The MIT License