76 lines
5.2 KiB
Markdown
76 lines
5.2 KiB
Markdown
# 071 DataTable · 数据表格
|
||
|
||
## 用途
|
||
|
||
按列展示数据行,只构建可见行。提供排序、筛选、多选、列宽、列顺序、列显隐与自定义单元格。行与列都以稳定 ID 保存状态:排序、筛选或调整列之后,选择仍指向同一条数据。默认使用本地排序与筛选(仅在数据、排序或筛选条件变化时计算一次并缓存);`Controlled` 模式下按调用方顺序显示,调用方在 `SortChanged()` 时自行查询。
|
||
|
||
## 最小示例
|
||
|
||
```go
|
||
type relic struct {
|
||
ID int
|
||
Name string
|
||
Age int
|
||
}
|
||
rows := []relic{{1, "Censer", 300}, {2, "Altar", 120}, {3, "Bell", 80}}
|
||
var state data.DataTableState[int]
|
||
opts := data.DataTableOptions[relic, int]{
|
||
Key: func(r relic) int { return r.ID },
|
||
Columns: []data.DataColumn[relic]{
|
||
{ID: "name", Title: "Name", Sortable: true,
|
||
Text: func(r relic) string { return r.Name },
|
||
Compare: func(a, b relic) int { return cmp.Compare(a.Name, b.Name) }},
|
||
{ID: "age", Title: "Age", Width: 80, Align: ui.End, Sortable: true,
|
||
Text: func(r relic) string { return strconv.Itoa(r.Age) },
|
||
Compare: func(a, b relic) int { return cmp.Compare(a.Age, b.Age) }},
|
||
},
|
||
}
|
||
view := func(c *ui.Context) {
|
||
core.Use(c, core.Settings{})
|
||
data.DataTable(c, &state, rows, opts).Element.Size(360, 240)
|
||
}
|
||
ui.Render(view, 400, 300, 1)
|
||
```
|
||
|
||
## 参数
|
||
|
||
`func DataTable[R any, K comparable](c *ui.Context, s *DataTableState[K], rows []R, opts DataTableOptions[R, K]) DataTableView`。表格上方有“列”按钮,弹层中每列提供显隐勾选框、升序/降序排序、左移/右移与减小/增大列宽(每次 16 DIP),这是排序、列顺序与列宽的键盘入口。另有纯函数 `DataTableRows(rows, cols, sort, filter) []int`(本地排序筛选辅助,返回行下标)。
|
||
|
||
`DataTableState` 字段:`Sort ui.SortOrder`、`Filter string`(不区分大小写包含匹配全部列的 `Text`)、`Hidden map[string]bool`(按列 ID 隐藏)、`Layout ui.TableLayout`(用户拖动得到的列顺序与列宽,可保存恢复)、`Selection ui.Selection[K]`(多选)。方法:`Selected()`、`Rows()`(筛选后行数)。
|
||
|
||
| 字段 | 类型 | 默认值 | 含义 |
|
||
| --- | --- | --- | --- |
|
||
| `Columns` | `[]DataColumn[R]` | 必填 | 列定义:`ID`(必填、唯一)、`Title`、`Width`(0 表示分配剩余宽度)、`MinWidth`、`Align`、`Sortable`、`Compare`(本地排序比较函数)、`Text`(必填,纯文本,用于筛选与默认单元格)、`Cell`(自定义单元格)。 |
|
||
| `Key` | `func(R) K` | 必填 | 行的稳定 ID;重复时 panic。 |
|
||
| `Multiple` | `bool` | false | 多选:首列显示勾选框,点击勾选框增减,Cmd/Ctrl、Shift 点击与 Cmd/Ctrl+A。 |
|
||
| `Controlled` | `bool` | false | 受控模式:不在本地排序筛选。 |
|
||
| `Version` | `int` | 0 | 原地修改 `rows` 后递增,通知缓存失效;换新切片无需递增。 |
|
||
| `Status` | `DataStatus` | `DataReady` | 加载状态:`DataLoading` 首次加载(无行时显示加载标记)、`DataLoadingMore` 追加加载(行下方显示“正在加载更多”)、`DataFailed` 失败(显示错误与“重试”)。 |
|
||
| `Error` | `string` | 内置“加载失败” | `DataFailed` 时显示的具体错误。 |
|
||
| `Empty` | `string` | 内置“暂无数据” | 无数据时的说明文字。 |
|
||
|
||
## 状态
|
||
|
||
默认、表头悬停、排序箭头(升/降)、行悬停、选中行(Selection 背景 + Text 文字;单选首列 AccentText 菱形,多选首列勾选框)、聚焦(表格外焦点环)、加载、追加加载、失败、空、无匹配。数据行与表头高度不低于当前密度的数据行高(28/36/44 DIP),并随文本缩放增高;多选勾选框保留完整的 28 × 28 DIP 命中区域。
|
||
|
||
## 事件
|
||
|
||
`Changed()`:选择变化(含勾选框);`Submitted()`:Enter 或双击;`SortChanged()`:点击表头或列菜单改变了 `State.Sort`;`Retried()`:点击“重试”。
|
||
|
||
## 键盘操作
|
||
|
||
- ↑/↓、Home/End:移动选择;Enter:提交;输入字母按首个可见列定位。
|
||
- 多选:Shift/Cmd(Ctrl) 配合点击或方向键;Cmd/Ctrl+A 全选。
|
||
- 表头:点击排序、再次点击反向;拖动表头边缘调整列宽,双击适配内容;拖动表头移动列。
|
||
- 列菜单:Tab 到“列”按钮,Enter 打开;Tab 在各列的勾选框与排序、移动、列宽按钮间移动,Enter/Space 执行;Escape 关闭。
|
||
- 多选勾选框可用 Tab 到达,Space 切换。
|
||
|
||
## 限制
|
||
|
||
- MyGo `ui.Table` 只按调用方给出的顺序显示(docs/ui/table.md “Sorting”),因此本组件在本地完成排序,或在受控模式下交给调用方。
|
||
- 表头内容只能是 `Title` 文字且只响应指针(ui/table.go `tableHeader`),因此全选使用 Cmd/Ctrl+A,键盘排序与调整列通过列菜单。
|
||
- 共享列宽的列在列菜单中从 max(MinWidth, 120) DIP 开始调整。
|
||
- 单选的跟随由组件按键重新定位,跨越任意距离都有效;多选的键盘锚点在大规模重排后由 MyGo 重置(ui/list.go:30)。
|
||
- 删除的数据行对应的选择会在数据变化时清除;被筛选隐藏的行保留选择。
|
||
- 10 万行 × 10 列:测试统计“最后一帧构建过单元格的行数”不超过 16,并断言重复帧不再调用比较函数(data_table_test.go)。
|