跳转到内容

树容器自定义数据源

开发文档

树容器自定义数据源可以为 Tree、CascadeList 容器或者 CascadeModelSelect 控件等树形组件自定义获取数据的逻辑.

树形组件通常具有四种行为:

  1. 按父节点 id 和一组条件查询下级记录。树组件获取数据的行为是按层级的,第一次进入只会获取第一级的数据,然后按用户操作才会获取指定节点的下一级数据。
  2. 按给定条件搜索一组记录,记录只包含数据 id 和一个展示名。展示名会在下拉框中平铺展示,在 Trantor 内置的树容器数据源中,返回的显示名为记录的 mainField 链,以 / 分割,比如 父名称/自己名称。因此在自定义实现中也建议这样做。
  3. 用指定节点的父节点 id 反向构建树。需要包含指定节点和它的兄弟节点,以及祖先链上所有节点和祖先链上节点的兄弟节点。通常用在搜索时,用户点击搜索结果后渲染指定节点使用。
  4. 根据一组数据 id 查询对应的节点。用于组件编辑态的回显。

注意,大部分情况用 Trantor 内置的数据源即可实现功能,比如只展示按条件筛选下的某些数据。 只有想使用一个非持久模型作为树组件模型,或者树本身由多个模型组成,类似这种情况才需要自定义。

使用树容器自定义数据源之前需要经过评估,是不是一定要自定义。当我们遇到要使用一个非持久模型作为树组件模型,或者树本身由多个模型组成等场景,就可以使用树容器自定义数据源。

在线配置数据容器

进入 制品中心->配置->业务域->树形数据源,新增树形数据源:

image-20211216175627867

其中:

字段类型规则
资源原标识string必填,代码实现类名:如下面的CategoryTreeDataProvider
资源名称string必填,50个字以内的无限制字符
调用逻辑流2one模型必填,选择模型
资源描述string

@TreeDataSource

public @interface TreeDataSource {
/**
* @return 名称
*/
String name() default "";
/**
* @return 描述
*/
String desc() default "";
}

TreeDataProvider

public abstract class TreeDataProvider<M extends RootModel<ID>, Q extends QModel<M>, ID extends Serializable> {
private final Class<?> _modelClass;
private final Class<?> _queryModelClass;
public TreeDataProvider() {
Type superClass = this.getClass().getGenericSuperclass();
if (superClass instanceof Class) {
throw new IllegalArgumentException("TreeDataProvider without actual type <M, Q, ID>.");
} else {
Type[] types = ((ParameterizedType) superClass).getActualTypeArguments();
this._modelClass = (Class<?>) types[0];
this._queryModelClass = (Class<?>) types[1];
}
}
/**
* 按父级 id 和其它参数获取子级数据返回,对应前端数据容器点击节点前[+]展开数据时使用。
*
* @param parentId 父级节点 ID ,可能为 null ,为 null 时即代表查询的是顶级元素
* @param params 查询参数,有效的为:
* params.search 查询条件
*/
public abstract List<M> loadChildren(@Nullable ID parentId, TreeSearchParams<M, Q> params);
/**
* 按给定条件模糊查询整颗树,返回查询结果,查询结果只需包含记录 ID 和显示名。
* 由于检索时是在下拉框中平铺显示,因此建议显示名附带更多能识别元素的内容(例如将显示名置为 父级名称/自己名称)。
*
* @param params 搜索入参,有效的为:
* params.searchLeafOnly 是否只搜索叶子节点;
* params.search 查询条件
*/
public abstract List<TreeItem<ID>> search(TreeSearchParams<M, Q> params);
/**
* 用指定节点的父节点 id 重新构建树,需要包含指定节点和它的兄弟节点,以及祖先链上所有节点和祖先链上节点的兄弟节点。
* <br>
* 举例来说,当我想从 A/B/C 的 C 节点反向构建可用于完整展示的树时,最终展示效果应该类似这样:
* <pre>
* ─ A
* ├── B
* │ ├── C
* │ ├── C1
* │ └── C2
* ├── B1
* └── B2
* ─ A1
* ─ A2
* </pre>
* 因此需要自身的兄弟节点和祖先链的兄弟节点。
* <br>
* <b>需要特别注意的是,入参给出的是指定节点的父节点 ID ,而不是指定节点本身的 ID 。</b>
* 这是因为既然要获得指定节点本身的兄弟节点,那直接用父节点 ID 获取全部子节点更为快捷。
* <br>
* <b>此外还需注意的是返回结果只需将所有结果在同一 List 内返回即可,无需构建树。</b>
* 前端会根据给定的 parentId 字段名进行构建,因此需要确保对应的字段内有值。
*
* @param parentId 指定节点的父节点 ID ,可能为 null ,为 null 时即代表查询的是顶级元素
* @param params 查询入参,反向构建时可根据入参过滤掉部分数据,有效的只有:
* params.search 查询条件
*/
public abstract List<M> reverseBuild(@Nullable ID parentId, TreeSearchParams<M, Q> params);
/**
* 根据一组 id 查询节点,用于树形控件编辑态下回显数据。
*
* @param ids 节点 ID 列表
* @param params 查询入参,有效的只有:
* params.search 查询条件
*/
public abstract List<M> loadByIds(List<ID> ids, TreeSearchParams<M, Q> params);
public Class<?> _getModelClass() {
return _modelClass;
}
public Class<?> _getQueryModelClass() {
return _queryModelClass;
}
}

简单例子:

@TreeDataSource
public class CategoryTreeDataProvider extends TreeDataProvider<Category, QCategory, Long> {
@Override
public List<Category> loadChildren(@Nullable Long parentId, TreeSearchParams<Category, QCategory> params) {
}
@Override
public List<TreeItem<Long>> search(TreeSearchParams<Category, QCategory> params) {
}
@Override
public List<Category> reverseBuild(@Nullable Long parentId, TreeSearchParams<Category, QCategory> params) {
}
@Override
public List<Category> loadByIds(List<Long> longs, TreeSearchParams<Category, QCategory> params) {
}
}