# @apigo.cc/datatable `@apigo.cc/datatable` 是建立在 `@apigo.cc/base` 上的同步数据表格组件,重点不是“能显示表格”,而是“能直接被页面首屏加载并完成渲染、筛选、编辑和保存”。 ## 目录 - [先看依赖](#先看依赖) - 同步加载顺序 - [全局对象](#全局对象) - 字段类型注册入口 - [``](#datatable) - 高性能表格组件 - [字段定义](#字段定义) - [内置编辑能力](#内置编辑能力) - [筛选、排序、固定列](#筛选排序固定列) - [复制粘贴与批量编辑](#复制粘贴与批量编辑) - [自定义字段类型](#自定义字段类型) - [推荐示例](#推荐示例) ## 先看依赖 `datatable` 依赖 `state`、`bootstrap` 和 `base`,加载顺序仍然必须是同步的。 ### 推荐加载顺序 ```html ``` ## 全局对象 ### `DataTable` `DataTable` 是本包挂到全局的对象。 ```html ``` ### 方法 | 方法 | 说明 | |---|---| | `registerFieldType(config)` | 注册字段类型 | | `getFieldTypes()` | 获取当前所有已注册字段类型 | ## `` `` 负责渲染、编辑、筛选、排序和虚拟滚动。内部会用到 ``, ``, ``,这些都来自 `base`。 ### 绑定方式 | 名称 | 写法示例 | 说明 | |---|---|---| | `editable` | `` | 开启编辑、删除、字段管理、保存按钮 | | `$.state.fields` | `$.state.fields="window.fields"` | 字段定义数组 | | `$.state.list` | `$.state.list="window.list"` | 数据行数组 | ### 事件 | 事件 | 触发时机 | `detail` | |---|---|---| | `save` | 点击底部保存按钮 | `{ list, fields }` | | `savefields` | 字段新增 / 编辑 / 删除后 | `fields` | | `remove` | 删除行后 | `{ items }` | ### 常用状态 | 名称 | 说明 | |---|---| | `editable` | 是否开启编辑模式 | | `list` | 当前生效的数据列表,排序和筛选后会更新这里 | | `fields` | 字段定义数组 | | `_renderedList` | 当前 DOM 实际渲染的片段 | | `prevHeight` | 虚拟滚动顶部占位高度 | | `postHeight` | 虚拟滚动底部占位高度 | | `_listStartIndex` | 当前渲染片段在原列表中的起始索引 | | `selectedRowCount` | 当前选中行数 | | `_originalList` | 原始数据快照,排序 / 筛选前的数据源 | | `sortConfig` | `{ fieldId, direction }` | | `filterConfig` | `{ [fieldId]: { mode, value, value2, selectedValues } }` | | `activeFieldId` | 当前打开列菜单的字段 id | | `activeField` | 当前打开列菜单的字段对象 | | `activeModes` | 当前列允许的筛选模式 | | `_columnStats` | 当前列高频值统计 | | `_internalUpdate` | 内部同步标记,避免列表回写时触发二次筛选 | | `_appliedHash` | 内部哈希缓存 | | `_fieldsDirty` | 字段变更标记 | | `_masterCellNodes` | 用于动态重建列模板的内部缓存 | | `isDirty` | 是否存在未保存修改 | | `isBulkEdit` | 是否处于批量编辑状态 | ### 方法 | 方法 | 写法示例 | 说明 | |---|---|---| | `addRow()` | `table.addRow()` | 新增一行空数据 | | `deleteSelectedRow()` | `table.deleteSelectedRow()` | 删除当前选区对应的行 | | `saveChanges()` | `table.saveChanges()` | 派发 `save` 事件 | | `addField()` | `table.addField()` | 新增字段 | | `editField()` | `table.editField()` | 编辑当前打开菜单的字段 | | `deleteField()` | `table.deleteField()` | 删除当前打开菜单的字段 | | `showColumnMenu(field, event)` | `table.showColumnMenu(field, event)` | 打开列菜单 | | `hideColumnMenu()` | `table.hideColumnMenu()` | 关闭列菜单 | | `editCell(row, field, cellNode)` | `table.editCell(row, field, cellNode)` | 打开单元格编辑器 | | `onScroll()` | `table.onScroll()` | 滚动时刷新虚拟列表 | ### 推荐示例 ```html
``` ## 字段定义 `fields` 是表格最重要的配置。每个字段都建议写完整,不要只写一个 `id` 就了事。 ### 字段结构 | 字段 | 写法示例 | 说明 | |---|---|---| | `id` | `id: 'name'` | 列唯一标识,同时也是数据行中的字段名 | | `name` | `name: 'Name'` | 表头显示名 | | `type` | `type: 'string'` | 列的原始类型 | | `memo` | `memo: 'User name'` | 字段备注,字段编辑器可见 | | `isIndex` | `isIndex: true` | 是否索引字段 | | `settings` | `settings: { width: 160, formType: 'text' }` | 编辑与展示配置 | | `formatter` | `formatter: v => String(v)` | 自定义显示格式 | ### `settings` 字段 | 字段 | 写法示例 | 说明 | |---|---|---| | `width` | `width: 160` | 列宽,默认 `150` | | `pinned` | `pinned: 'left'` | 固定列,`left` 或 `right` | | `formType` | `formType: 'select'` | 编辑器类型 | | `options` | `options: [{ label: 'A', value: 'a' }]` | 选择类选项 | | `decimals` | `decimals: 2` | 数字小数位 | | `prefix` | `prefix: '$'` | 数字前缀 | | `suffix` | `suffix: '%'` | 数字后缀 | | `thousandSep` | `thousandSep: true` | 数字千分位 | | `labelOn` | `labelOn: 'YES'` | `switch` 为真时显示 | | `labelOff` | `labelOff: 'NO'` | `switch` 为假时显示 | | `format` | `format: 'YYYY-MM-DD'` | 日期时间格式说明 | | `placeholder` | `placeholder: 'Enter value'` | 文本占位符 | ### 字段示例 ```js { id: 'score', name: 'Score', type: 'number', settings: { width: 120, formType: 'number', decimals: 2, prefix: '$', thousandSep: true }, formatter: value => value == null ? '' : String(value) } ``` ## 内置编辑能力 ### 单元格编辑支持的 `formType` `editCell()` 会把 `settings.formType || type || 'text'` 交给 `AutoForm`,因此这些类型都能直接编辑: - `text` - `number` - `select` - `checkbox` - `radio` - `switch` - `date` - `datetime` - `textarea` - `TagsInput` - `DatePicker` - `ColorPicker` - `IconPicker` ### 字段编辑器默认可选类型 字段管理弹窗里,默认由 `DataTable.getFieldTypes()` 提供的类型是: - `text` - `number` - `select` - `checkbox` - `switch` - `datetime` - `textarea` ### 重要区别 - `date`、`radio` 这些类型在单元格编辑里是可用的 - 但默认字段编辑器列表里不一定出现 - 如果你要让它们出现在字段管理弹窗里,直接用 `registerFieldType()` 补进去 ### 默认字段类型的格式化规则 | 类型 | 显示规则 | |---|---| | `text` | 原样字符串化 | | `number` | 支持 `decimals`、`prefix`、`suffix`、`thousandSep` | | `select` | 显示匹配项的 `label` | | `checkbox` | 把多选项 label 用逗号拼接 | | `switch` | `true` 显示 `labelOn` 或 `Yes`,`false` 显示 `labelOff` 或 `No` | | `datetime` | 默认字符串化 | | `textarea` | 默认字符串化 | ## 筛选、排序、固定列 ### 排序 - 点击表头右侧下拉按钮 - 选择 `ASC` 或 `DESC` - 排序状态写在 `state.sortConfig` ### 筛选 列菜单支持三类筛选: - 文本类:`contains`, `equals`, `starts`, `ends` - 数字类:`=`, `>`, `<`, `between` - 值列表:直接勾选统计出来的高频值 ### 支持的列筛选模式 | `formType` | 可用模式 | |---|---| | `text` | `contains`, `equals`, `starts`, `ends` | | `textarea` | `contains`, `equals`, `starts`, `ends` | | `number` | `=`, `>`, `<`, `between` | | `date` | `=`, `>`, `<`, `between` | | `select` | `contains`, `equals` | | `TagsInput` | `contains`, `equals`, `starts`, `ends` | | `switch`, `checkbox`, `radio` | 选项勾选,不走文本筛选模式 | ### 固定列 字段上设置: ```js settings: { pinned: 'left' } ``` 或: ```js settings: { pinned: 'right' } ``` 就会固定在对应侧边。 ## 复制粘贴与批量编辑 ### 选择 - 鼠标拖选单元格区域 - `selectedRowCount` 会同步更新 - 选区会用于复制、粘贴和批量编辑 ### 复制 - `Ctrl/Cmd + C` 把选区复制成 TSV - 带引号、换行、制表符的值会自动转义 ### 粘贴 - `Ctrl/Cmd + V` 会把剪贴板 TSV 回填到选区 - 单格选中时会把后续数据按行列铺开 ### 批量编辑 - 在已选中的区域内双击一个单元格 - 如果选区跨多行,会进入批量编辑 - 保存时会把新值写入范围内每一行 ## 自定义字段类型 ### `DataTable.registerFieldType(config)` ```js DataTable.registerFieldType({ value: 'MyTags', label: '{#Tags#}', typeForDB: 'v4096', schema: [ { name: 'placeholder', label: 'Placeholder', type: 'text' } ], formatter: val => Array.isArray(val) ? val.join(' | ') : val }) ``` ### `schema` 说明 `schema` 的格式和 `base` 里的 `AutoForm.schema` 一样,所以这里也能用: - `name` - `label` - `type` - `setting` - `options` - `if` ### 适合注册的场景 - 你要让字段编辑器认识一个新的 `formType` - 你要给某种值定义专用 `formatter` - 你要把扩展控件复用到表格内联编辑里 ## 推荐示例 ### 可编辑表格 ```html
``` ### 只读大表 + 虚拟滚动 ```html
``` ### 自定义字段类型 ```html ``` ### 备注 - 字段定义和数据列表建议放在 JS 里 - 外层容器最好明确给高度 - 需要保存时,监听 `save`;需要字段变更时,监听 `savefields`