2026-07-07 00:17:32 +08:00
|
|
|
|
# @apigo.cc/datatable
|
2026-05-17 17:03:21 +08:00
|
|
|
|
|
2026-07-07 00:17:32 +08:00
|
|
|
|
`@apigo.cc/datatable` 是建立在 `@apigo.cc/base` 上的同步数据表格组件,重点不是“能显示表格”,而是“能直接被页面首屏加载并完成渲染、筛选、编辑和保存”。
|
2026-05-17 17:03:21 +08:00
|
|
|
|
|
2026-07-07 00:17:32 +08:00
|
|
|
|
## 目录
|
2026-05-17 17:03:21 +08:00
|
|
|
|
|
2026-07-07 00:17:32 +08:00
|
|
|
|
- [先看依赖](#先看依赖) - 同步加载顺序
|
|
|
|
|
|
- [全局对象](#全局对象) - 字段类型注册入口
|
|
|
|
|
|
- [`<DataTable>`](#datatable) - 高性能表格组件
|
|
|
|
|
|
- [字段定义](#字段定义)
|
|
|
|
|
|
- [内置编辑能力](#内置编辑能力)
|
|
|
|
|
|
- [筛选、排序、固定列](#筛选排序固定列)
|
|
|
|
|
|
- [复制粘贴与批量编辑](#复制粘贴与批量编辑)
|
|
|
|
|
|
- [自定义字段类型](#自定义字段类型)
|
|
|
|
|
|
- [推荐示例](#推荐示例)
|
|
|
|
|
|
|
|
|
|
|
|
## 先看依赖
|
|
|
|
|
|
|
|
|
|
|
|
`datatable` 依赖 `state`、`bootstrap` 和 `base`,加载顺序仍然必须是同步的。
|
|
|
|
|
|
|
|
|
|
|
|
### 推荐加载顺序
|
2026-05-17 17:03:21 +08:00
|
|
|
|
|
|
|
|
|
|
```html
|
2026-07-07 00:17:32 +08:00
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/@apigo.cc/state@1.0.23/dist/state.min.js"></script>
|
|
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/@apigo.cc/bootstrap@1.0.9/dist/bootstrap.min.js"></script>
|
|
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/@apigo.cc/base@1.0.23/dist/base.min.js"></script>
|
|
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/@apigo.cc/datatable@1.0.16/dist/datatable.min.js"></script>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 全局对象
|
|
|
|
|
|
|
|
|
|
|
|
### `DataTable`
|
|
|
|
|
|
|
|
|
|
|
|
`DataTable` 是本包挂到全局的对象。
|
|
|
|
|
|
|
|
|
|
|
|
```html
|
|
|
|
|
|
<script>
|
|
|
|
|
|
console.log(DataTable.getFieldTypes())
|
2026-05-17 17:03:21 +08:00
|
|
|
|
</script>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-07-07 00:17:32 +08:00
|
|
|
|
### 方法
|
|
|
|
|
|
|
|
|
|
|
|
| 方法 | 说明 |
|
|
|
|
|
|
|---|---|
|
|
|
|
|
|
| `registerFieldType(config)` | 注册字段类型 |
|
|
|
|
|
|
| `getFieldTypes()` | 获取当前所有已注册字段类型 |
|
|
|
|
|
|
|
|
|
|
|
|
## `<DataTable>`
|
|
|
|
|
|
|
|
|
|
|
|
`<DataTable>` 负责渲染、编辑、筛选、排序和虚拟滚动。内部会用到 `<AutoForm>`, `<Modal>`, `<Resizer>`,这些都来自 `base`。
|
|
|
|
|
|
|
|
|
|
|
|
### 绑定方式
|
|
|
|
|
|
|
|
|
|
|
|
| 名称 | 写法示例 | 说明 |
|
|
|
|
|
|
|---|---|---|
|
|
|
|
|
|
| `editable` | `<DataTable 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
|
|
|
|
|
|
<script>
|
|
|
|
|
|
window.fields = [
|
|
|
|
|
|
{ id: 'id', name: 'ID', type: 'string', settings: { width: 70, formType: 'text', pinned: 'left' } },
|
|
|
|
|
|
{ id: 'name', name: 'Name', type: 'string', settings: { width: 160, formType: 'text', pinned: 'left' } },
|
|
|
|
|
|
{ id: 'role', name: 'Role', type: 'string', settings: { width: 120, formType: 'select', options: [{ label: 'Admin', value: 'admin' }, { label: 'Editor', value: 'editor' }] } },
|
|
|
|
|
|
{ id: 'active', name: 'Active', type: 'boolean', settings: { width: 90, formType: 'switch', labelOn: 'YES', labelOff: 'NO' } },
|
|
|
|
|
|
{ id: 'score', name: 'Score', type: 'number', settings: { width: 120, formType: 'number', prefix: '$', decimals: 2, thousandSep: true } },
|
|
|
|
|
|
{ id: 'tags', name: 'Tags', type: 'object', settings: { width: 180, formType: 'TagsInput' } },
|
|
|
|
|
|
{ id: 'created', name: 'Created', type: 'date', settings: { width: 140, formType: 'date' } },
|
|
|
|
|
|
{ id: 'actions', name: 'Actions', type: 'string', settings: { width: 120, formType: 'text', pinned: 'right' }, formatter: () => 'Edit' }
|
|
|
|
|
|
]
|
2026-05-17 17:03:21 +08:00
|
|
|
|
|
2026-07-07 00:17:32 +08:00
|
|
|
|
window.list = [
|
|
|
|
|
|
{ id: 1, name: 'Alice', role: 'admin', active: true, score: 12800.5, tags: ['Vue', 'AI'], created: '2026-06-01', actions: '' },
|
|
|
|
|
|
{ id: 2, name: 'Bob', role: 'editor', active: false, score: 980.25, tags: ['Docs'], created: '2026-06-02', actions: '' }
|
|
|
|
|
|
]
|
2026-05-17 17:03:21 +08:00
|
|
|
|
|
2026-07-07 00:17:32 +08:00
|
|
|
|
DataTable.registerFieldType({
|
|
|
|
|
|
value: 'TagsInput',
|
|
|
|
|
|
label: '{#Tags#}',
|
|
|
|
|
|
typeForDB: 'v4096',
|
|
|
|
|
|
schema: [{ name: 'placeholder', label: 'Placeholder', type: 'text' }],
|
|
|
|
|
|
formatter: val => Array.isArray(val) ? val.join(', ') : val
|
|
|
|
|
|
})
|
2026-05-17 17:03:21 +08:00
|
|
|
|
|
2026-07-07 00:17:32 +08:00
|
|
|
|
</script>
|
2026-05-17 17:03:21 +08:00
|
|
|
|
|
2026-07-07 00:17:32 +08:00
|
|
|
|
<div style="height: 600px" class="border rounded overflow-hidden bg-body">
|
|
|
|
|
|
<DataTable editable
|
|
|
|
|
|
$.state.fields="window.fields"
|
|
|
|
|
|
$.state.list="window.list"
|
|
|
|
|
|
$onsave="UI.toast('save: ' + JSON.stringify(event.detail.list))"
|
|
|
|
|
|
$onsavefields="UI.toast('fields changed')"
|
|
|
|
|
|
$onremove="UI.toast('removed: ' + event.detail.items.length)">
|
|
|
|
|
|
</DataTable>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
```
|
2026-05-17 17:03:21 +08:00
|
|
|
|
|
2026-07-07 00:17:32 +08:00
|
|
|
|
## 字段定义
|
2026-05-27 23:27:45 +08:00
|
|
|
|
|
2026-07-07 00:17:32 +08:00
|
|
|
|
`fields` 是表格最重要的配置。每个字段都建议写完整,不要只写一个 `id` 就了事。
|
2026-05-17 17:03:21 +08:00
|
|
|
|
|
2026-07-07 00:17:32 +08:00
|
|
|
|
### 字段结构
|
2026-05-17 17:03:21 +08:00
|
|
|
|
|
2026-07-07 00:17:32 +08:00
|
|
|
|
| 字段 | 写法示例 | 说明 |
|
|
|
|
|
|
|---|---|---|
|
|
|
|
|
|
| `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)` | 自定义显示格式 |
|
2026-05-17 17:03:21 +08:00
|
|
|
|
|
2026-07-07 00:17:32 +08:00
|
|
|
|
### `settings` 字段
|
2026-05-17 17:03:21 +08:00
|
|
|
|
|
2026-07-07 00:17:32 +08:00
|
|
|
|
| 字段 | 写法示例 | 说明 |
|
|
|
|
|
|
|---|---|---|
|
|
|
|
|
|
| `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'` | 文本占位符 |
|
2026-05-17 17:03:21 +08:00
|
|
|
|
|
2026-07-07 00:17:32 +08:00
|
|
|
|
### 字段示例
|
2026-05-17 17:03:21 +08:00
|
|
|
|
|
2026-07-07 00:17:32 +08:00
|
|
|
|
```js
|
2026-05-17 17:03:21 +08:00
|
|
|
|
{
|
2026-07-07 00:17:32 +08:00
|
|
|
|
id: 'score',
|
|
|
|
|
|
name: 'Score',
|
|
|
|
|
|
type: 'number',
|
|
|
|
|
|
settings: {
|
|
|
|
|
|
width: 120,
|
|
|
|
|
|
formType: 'number',
|
|
|
|
|
|
decimals: 2,
|
|
|
|
|
|
prefix: '$',
|
|
|
|
|
|
thousandSep: true
|
|
|
|
|
|
},
|
|
|
|
|
|
formatter: value => value == null ? '' : String(value)
|
2026-05-17 17:03:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-07-07 00:17:32 +08:00
|
|
|
|
## 内置编辑能力
|
|
|
|
|
|
|
|
|
|
|
|
### 单元格编辑支持的 `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
|
|
|
|
|
|
<script>
|
|
|
|
|
|
window.fields = [
|
|
|
|
|
|
{ id: 'id', name: 'ID', type: 'string', settings: { width: 70, formType: 'text', pinned: 'left' } },
|
|
|
|
|
|
{ id: 'name', name: 'Name', type: 'string', settings: { width: 160, formType: 'text', pinned: 'left' } },
|
|
|
|
|
|
{ id: 'role', name: 'Role', type: 'string', settings: { width: 120, formType: 'select', options: [{ label: 'Admin', value: 'admin' }, { label: 'Editor', value: 'editor' }] } },
|
|
|
|
|
|
{ id: 'active', name: 'Active', type: 'boolean', settings: { width: 90, formType: 'switch', labelOn: 'YES', labelOff: 'NO' } },
|
|
|
|
|
|
{ id: 'score', name: 'Score', type: 'number', settings: { width: 120, formType: 'number', prefix: '$', decimals: 2, thousandSep: true } },
|
|
|
|
|
|
{ id: 'created', name: 'Created', type: 'date', settings: { width: 140, formType: 'date' } }
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
window.list = [
|
|
|
|
|
|
{ id: 1, name: 'Alice', role: 'admin', active: true, score: 12800.5, created: '2026-06-01' },
|
|
|
|
|
|
{ id: 2, name: 'Bob', role: 'editor', active: false, score: 980.25, created: '2026-06-02' }
|
|
|
|
|
|
]
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<div style="height: 560px" class="border rounded overflow-hidden">
|
|
|
|
|
|
<DataTable editable
|
|
|
|
|
|
$.state.fields="window.fields"
|
|
|
|
|
|
$.state.list="window.list"
|
|
|
|
|
|
$onsave="UI.toast('save')"
|
|
|
|
|
|
$onremove="UI.toast('remove: ' + event.detail.items.length)">
|
|
|
|
|
|
</DataTable>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 只读大表 + 虚拟滚动
|
|
|
|
|
|
|
|
|
|
|
|
```html
|
|
|
|
|
|
<script>
|
|
|
|
|
|
window.fields = [
|
|
|
|
|
|
{ id: 'id', name: 'ID', type: 'string', settings: { width: 70, formType: 'text', pinned: 'left' } },
|
|
|
|
|
|
{ id: 'name', name: 'Name', type: 'string', settings: { width: 160, formType: 'text', pinned: 'left' } },
|
|
|
|
|
|
{ id: 'role', name: 'Role', type: 'string', settings: { width: 120, formType: 'select', options: [{ label: 'Admin', value: 'admin' }, { label: 'Editor', value: 'editor' }] } },
|
|
|
|
|
|
{ id: 'active', name: 'Active', type: 'boolean', settings: { width: 90, formType: 'switch', labelOn: 'YES', labelOff: 'NO' } },
|
|
|
|
|
|
{ id: 'score', name: 'Score', type: 'number', settings: { width: 120, formType: 'number', prefix: '$', decimals: 2, thousandSep: true } },
|
|
|
|
|
|
{ id: 'created', name: 'Created', type: 'date', settings: { width: 140, formType: 'date' } }
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
window.bigList = Array.from({ length: 2000 }, (_, i) => ({
|
|
|
|
|
|
id: i + 1,
|
|
|
|
|
|
name: 'User ' + (i + 1),
|
|
|
|
|
|
role: i % 2 ? 'admin' : 'editor',
|
|
|
|
|
|
active: i % 3 === 0,
|
|
|
|
|
|
score: Math.round(Math.random() * 10000) / 100,
|
|
|
|
|
|
created: '2026-06-01'
|
|
|
|
|
|
}))
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<div style="height: 560px" class="border rounded overflow-hidden">
|
|
|
|
|
|
<DataTable
|
|
|
|
|
|
$.state.fields="window.fields"
|
|
|
|
|
|
$.state.list="window.bigList">
|
|
|
|
|
|
</DataTable>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 自定义字段类型
|
|
|
|
|
|
|
|
|
|
|
|
```html
|
|
|
|
|
|
<script>
|
|
|
|
|
|
DataTable.registerFieldType({
|
|
|
|
|
|
value: 'TagsInput',
|
|
|
|
|
|
label: '{#Tags#}',
|
|
|
|
|
|
typeForDB: 'v4096',
|
|
|
|
|
|
schema: [{ name: 'placeholder', label: 'Placeholder', type: 'text' }],
|
|
|
|
|
|
formatter: val => Array.isArray(val) ? val.join(', ') : val
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
window.fields = [
|
|
|
|
|
|
{ id: 'tags', name: 'Tags', type: 'object', settings: { width: 180, formType: 'TagsInput' } }
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
window.list = [
|
|
|
|
|
|
{ id: 1, tags: ['Vue', 'AI'] }
|
|
|
|
|
|
]
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<DataTable $.state.fields="window.fields" $.state.list="window.list"></DataTable>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 备注
|
2026-05-17 17:03:21 +08:00
|
|
|
|
|
2026-07-07 00:17:32 +08:00
|
|
|
|
- 字段定义和数据列表建议放在 JS 里
|
|
|
|
|
|
- 外层容器最好明确给高度
|
|
|
|
|
|
- 需要保存时,监听 `save`;需要字段变更时,监听 `savefields`
|