356 lines
11 KiB
Markdown
356 lines
11 KiB
Markdown
# `bricks.Tree` 与 `bricks.TreeNode` 技术文档
|
||
|
||
> **版本:1.0**
|
||
> **模块:Tree 组件(树形结构 UI 控件)**
|
||
|
||
---
|
||
|
||
## 概述
|
||
|
||
`bricks.Tree` 是一个基于 `bricks.js` 框架的可交互树形组件,支持异步数据加载、节点展开/折叠、选中/勾选操作、动态增删改节点等功能。它适用于展示层级数据结构(如文件系统、组织架构等),并提供完整的前端交互逻辑和后端通信能力。
|
||
|
||
该组件由两个核心类构成:
|
||
|
||
- `bricks.Tree`: 树的容器类,继承自 `bricks.VScrollPanel`,负责管理整体布局、数据获取、工具栏及事件分发。
|
||
- `bricks.TreeNode`: 单个树节点类,继承自 `bricks.VBox`,表示树中的一个节点,包含图标、文本、子节点区域等元素。
|
||
|
||
---
|
||
|
||
## 类定义
|
||
|
||
### `bricks.TreeNode`
|
||
|
||
#### 继承关系
|
||
```js
|
||
class TreeNode extends bricks.VBox
|
||
```
|
||
|
||
#### 构造函数
|
||
```js
|
||
constructor(tree, parent_node, data)
|
||
```
|
||
|
||
##### 参数说明:
|
||
| 参数 | 类型 | 描述 |
|
||
|--------------|----------|------|
|
||
| `tree` | `bricks.Tree` | 当前节点所属的树实例 |
|
||
| `parent_node`| `bricks.TreeNode` 或 `null` | 父节点引用;根节点为 `null` |
|
||
| `data` | `Object` | 节点绑定的数据对象 |
|
||
|
||
##### 初始化行为:
|
||
- 设置默认选项(宽度 100%,高度自适应)
|
||
- 初始化节点状态字段(是否为叶子节点、是否已加载子节点等)
|
||
- 设置图标 URL(根据类型或默认配置)
|
||
- 创建主内容行(`HBox`),绑定点击事件
|
||
- 异步创建节点内容(图标、复选框、文本或自定义视图)
|
||
- 若非叶子节点,则创建子节点容器(`VBox`),初始隐藏
|
||
|
||
---
|
||
|
||
### `bricks.Tree`
|
||
|
||
#### 继承关系
|
||
```js
|
||
class Tree extends bricks.VScrollPanel
|
||
```
|
||
|
||
#### 构造函数
|
||
```js
|
||
constructor(options)
|
||
```
|
||
|
||
##### 参数说明(`options` 对象):
|
||
| 属性名 | 类型 | 必需 | 默认值 | 描述 |
|
||
|--------|------|------|--------|------|
|
||
| `row_height` | String | 否 | `'35px'` | 每行节点的高度 |
|
||
| `idField` | String | 是 | - | 数据中标识唯一 ID 的字段名 |
|
||
| `textField` | String | 是 | - | 显示节点文本的字段名 |
|
||
| `is_leafField` | String | 否 | `'is_leaf'` | 判断是否为叶子节点的字段名 |
|
||
| `typeField` | String | 否 | - | 区分节点类型的字段名(用于图标定制) |
|
||
| `data` | Array | 否 | - | 静态数据源(本地 JSON 数组) |
|
||
| `dataurl` | String | 否 | - | 动态获取子节点数据的 API 地址 |
|
||
| `method` | String | 否 | `'GET'` | 请求数据时使用的 HTTP 方法 |
|
||
| `params` | Object | 否 | `{}` | 发送请求时携带的额外参数 |
|
||
| `node_view` | WidgetDesc \| Function | 否 | - | 自定义节点渲染模板(可通过 `widgetBuild` 解析) |
|
||
| `checkField` | String | 否 | - | 控制节点勾选状态的字段名 |
|
||
| `editable` | Object | 否 | - | 编辑功能配置(增删改 URL 和字段) |
|
||
| `node_typeicons` | Object | 否 | 内置默认图标 | 不同类型节点的图标配置 |
|
||
| `multitype_tree` | Boolean | 否 | `false` | 是否启用多类型树显示 |
|
||
|
||
##### 初始化行为:
|
||
- 创建滚动面板
|
||
- 构建标题与描述(预留接口)
|
||
- 创建工具栏(如果启用了编辑模式或自定义工具)
|
||
- 初始化容器用于放置节点
|
||
- 如果有 `dataurl`,则异步加载根节点数据;否则使用本地 `data` 构建树
|
||
|
||
---
|
||
|
||
## 主要方法
|
||
|
||
### `bricks.TreeNode` 方法
|
||
|
||
| 方法名 | 说明 |
|
||
|-------|------|
|
||
| `getValue()` | 返回当前节点及其所有子节点的完整数据结构(递归) |
|
||
| `get_id()` | 获取节点 ID(通过 `idField` 字段) |
|
||
| `selected(flg)` | 设置节点选中状态样式 |
|
||
| `toggleExpandCollapse(event)` | 切换展开/收起状态(异步) |
|
||
| `expand()` | 展开节点(若未加载过子节点且存在 `dataurl`,会先请求数据) |
|
||
| `collapse()` | 收起节点 |
|
||
| `create_triple()` | 创建带状态切换功能的 SVG 图标(展开/收起) |
|
||
| `change_node_type()` | 根据是否为叶子节点设置图标行为 |
|
||
| `create_node_content(widget)` | 渲染节点内部内容(图标、复选框、文本或自定义组件) |
|
||
| `update_content()` | 更新节点显示内容(主要用于刷新文本或视图) |
|
||
| `setup_icon_urls()` | 根据节点类型配置图标路径(支持按 type 定制) |
|
||
|
||
---
|
||
|
||
### `bricks.Tree` 方法
|
||
|
||
| 方法名 | 说明 |
|
||
|-------|------|
|
||
| `getValue()` | 获取整棵树的数据结构(含所有子节点) |
|
||
| `get_id()` | 获取树根节点 ID |
|
||
| `create_toolbar()` | 创建顶部工具栏(添加、编辑、删除按钮及其他命令) |
|
||
| `toolbar_command(event)` | 工具栏命令处理器(响应用户操作) |
|
||
| `add_new_node()` | 打开模态窗添加新节点 |
|
||
| `new_node_inputed(event)` | 处理新增节点表单提交 |
|
||
| `append_new_subnode(node, data)` | 在指定节点下追加子节点 |
|
||
| `build_subnode(node, data)` | 构建并插入一个 `TreeNode` 实例 |
|
||
| `delete_node()` | 删除当前选中节点(弹出确认框) |
|
||
| `delete_node_conformed(event)` | 确认删除后的处理逻辑 |
|
||
| `delete_subnode(node, subnode)` | 从父节点中移除子节点(更新数据与 DOM) |
|
||
| `update_node()` | 编辑当前选中节点(打开表单) |
|
||
| `update_node_inputed(event)` | 处理编辑表单提交 |
|
||
| `update_node_data(node, data)` | 应用新数据到节点,并刷新 UI |
|
||
| `get_children_data(node)` | 通过 AJAX 获取某节点的子节点数据 |
|
||
| `create_node_children(node, data)` | 使用给定数据批量创建子节点 |
|
||
| `node_click_handle(node, event)` | 节点点击处理(控制选中状态切换) |
|
||
| `node_selected(node, flag)` | 分发节点选中事件 |
|
||
| `node_checked(node, event)` | 处理节点勾选状态变化,维护 `checked_data` 列表 |
|
||
|
||
---
|
||
|
||
## 事件机制
|
||
|
||
### 内部触发事件(通过 `dispatch` 分发)
|
||
|
||
| 事件名 | 触发时机 | 参数说明 |
|
||
|--------|---------|----------|
|
||
| `node_selected` | 节点被选中或取消选中 | 包含节点原始数据 + `selected: true/false` |
|
||
| `check_changed` | 节点勾选状态改变 | 节点数据对象 |
|
||
| `[command]` | 工具栏执行命令(如 `add`, `delete`) | 命令参数 + 元数据(referer、title 等) |
|
||
|
||
> 示例监听方式:
|
||
```js
|
||
tree.bind('node_selected', function(e){
|
||
console.log('Selected node:', e.params);
|
||
});
|
||
```
|
||
|
||
---
|
||
|
||
## 图标与资源管理
|
||
|
||
### 图标策略
|
||
|
||
- 支持三种状态图标:
|
||
- `open`: 展开状态图标
|
||
- `close`: 收起状态图标
|
||
- `leaf`: 叶子节点图标
|
||
|
||
- 图标来源优先级:
|
||
1. `node_typeicons[type]` —— 按节点类型指定
|
||
2. 回退到 `default_type`
|
||
3. 使用内置默认图标集
|
||
|
||
```js
|
||
this.node_typeicons = {
|
||
folder: {
|
||
open: '/static/imgs/open-folder.svg',
|
||
close: '/static/imgs/close-folder.svg',
|
||
leaf: '/static/imgs/file.svg'
|
||
},
|
||
default_type: 'folder'
|
||
}
|
||
```
|
||
|
||
- 使用 `bricks_resource(path)` 解析静态资源路径。
|
||
|
||
---
|
||
|
||
## 编辑功能(EditableTree)
|
||
|
||
当配置 `editable` 选项时,启用增删改功能:
|
||
|
||
```js
|
||
editable: {
|
||
fields: [ /* 表单字段定义 */ ],
|
||
add_url: '/api/node/add',
|
||
delete_url: '/api/node/delete',
|
||
update_url: '/api/node/update'
|
||
}
|
||
```
|
||
|
||
### 功能流程
|
||
|
||
| 操作 | 流程 |
|
||
|------|------|
|
||
| 添加节点 | 弹出 `ModalForm` → 提交 → 调用 `add_url` → 成功后插入新节点 |
|
||
| 删除节点 | 确认对话框 → 调用 `delete_url` → 移除 DOM 与数据 |
|
||
| 修改节点 | 加载当前数据至表单 → 提交 → 调用 `update_url` → 局部刷新 UI |
|
||
|
||
> 注意:表单数据自动附加 `idField` 和 `parentField`(如有父节点)
|
||
|
||
---
|
||
|
||
## 数据结构要求
|
||
|
||
### 节点数据格式示例
|
||
|
||
```json
|
||
{
|
||
"id": "n1",
|
||
"name": "项目A",
|
||
"type": "project",
|
||
"is_leaf": false,
|
||
"children": [
|
||
{
|
||
"id": "n11",
|
||
"name": "子任务1",
|
||
"is_leaf": true
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
### 关键字段映射(可配置)
|
||
|
||
| 配置项 | 默认字段 |
|
||
|--------|----------|
|
||
| `idField` | `id` |
|
||
| `textField` | `name`(需在 `opts.textField` 中指定) |
|
||
| `is_leafField` | `is_leaf` |
|
||
| `typeField` | (可选)`type` |
|
||
|
||
---
|
||
|
||
## 使用示例
|
||
|
||
### 静态树(本地数据)
|
||
|
||
```js
|
||
var tree = new bricks.Tree({
|
||
idField: 'id',
|
||
textField: 'name',
|
||
is_leafField: 'is_leaf',
|
||
data: [
|
||
{ id: '1', name: '父节点', is_leaf: false, children: [
|
||
{ id: '2', name: '子节点', is_leaf: true }
|
||
]}
|
||
]
|
||
});
|
||
tree.open();
|
||
```
|
||
|
||
### 动态树(远程加载)
|
||
|
||
```js
|
||
var tree = new bricks.Tree({
|
||
idField: 'id',
|
||
textField: 'label',
|
||
dataurl: '/api/tree/nodes',
|
||
params: { app: 'doc' },
|
||
row_height: '40px'
|
||
});
|
||
document.body.appendChild(tree.dom_element);
|
||
```
|
||
|
||
### 可编辑树
|
||
|
||
```js
|
||
var editableTree = new bricks.Tree({
|
||
idField: 'id',
|
||
textField: 'title',
|
||
dataurl: '/api/tree',
|
||
editable: {
|
||
fields: [
|
||
{ name: 'title', label: '标题', type: 'text' },
|
||
{ name: 'type', label: '类型', type: 'select', options: [...] }
|
||
],
|
||
add_url: '/api/node/add',
|
||
delete_url: '/api/node/delete',
|
||
update_url: '/api/node/update'
|
||
}
|
||
});
|
||
```
|
||
|
||
---
|
||
|
||
## 异步加载机制
|
||
|
||
- 使用 `schedule_once(fn, delay)` 延迟执行以避免阻塞主线程
|
||
- 子节点数据仅在首次展开时请求一次(`children_loaded` 标志位控制)
|
||
- 支持无限层级嵌套
|
||
|
||
---
|
||
|
||
## 工具栏(Toolbar)
|
||
|
||
自动构建于树上方(如果启用了编辑或自定义工具):
|
||
|
||
- 支持图标+文字按钮
|
||
- 可扩展自定义命令
|
||
- 命令可通过以下方式注入数据:
|
||
- `selected_data: true` → 传入选中节点数据
|
||
- `checked_data: true` → 传入所有勾选节点
|
||
- 否则传入上下文数据(选中 > 勾选 > params)
|
||
|
||
---
|
||
|
||
## 注册组件
|
||
|
||
```js
|
||
bricks.Factory.register('Tree', bricks.Tree);
|
||
bricks.Factory.register('EditableTree', bricks.EditableTree); // 假设存在别名
|
||
```
|
||
|
||
允许通过描述符动态创建:
|
||
```js
|
||
bricks.widgetBuild({ widgettype: 'Tree', ...options }, parent);
|
||
```
|
||
|
||
---
|
||
|
||
## 已知限制与注意事项
|
||
|
||
1. **性能优化建议**:对于超大数据集(>1000 节点),建议启用虚拟滚动或懒加载优化。
|
||
2. **复选框过滤问题**:`checked_data` 过滤逻辑目前仅比较 `id`,应确保唯一性。
|
||
3. **FormData 处理**:对 `FormData` 类型做了特殊处理(`.append()`),兼容性强。
|
||
4. **图标路径错误排查**:注意 `bricks_resource()` 是否正确解析路径。
|
||
5. **循环引用风险**:`TreeNode` 持有 `tree` 和 `parent_node` 引用,注意内存释放。
|
||
|
||
---
|
||
|
||
## 附录:依赖函数说明
|
||
|
||
| 函数 | 来源 | 用途 |
|
||
|------|------|------|
|
||
| `bricks.extend(a, b)` | utils | 对象浅合并 |
|
||
| `bricks.uuid()` | utils | 生成唯一 ID |
|
||
| `bricks_resource(path)` | global | 解析资源路径 |
|
||
| `schedule_once(fn, delay)` | scheduler | 延迟执行函数 |
|
||
| `formdata2object(fd)` | utils | 将 FormData 转为普通对象 |
|
||
| `new bricks.HttpJson()` | net | 封装 JSON 请求 |
|
||
|
||
---
|
||
|
||
## 版本历史
|
||
|
||
- v1.0:初版发布,支持基本树形结构、异步加载、编辑操作、事件系统
|
||
|
||
---
|
||
|
||
> 文档维护者:Bricks Framework Team
|
||
> 最后更新:2025年4月5日 |