一、思路
实现左树右表其实只需要这几步:
绘制左树右表样式的动态表单来展示数据,这一步可以直接用平台的bos_treelist
查询左树数据并按格式构建节点
如果左数要支持搜索框和工具栏,需要自行实现逻辑
实现点击左数节点时刷新过滤右表
左树右表页面可以理解为有两个普通的列表页面,各有各的视图与模型,如下图所示,右边是这个单据本身的列表页面,左边是bos_treelist多加的一块区域供自行绘制,我们平时那些继承AbstractListPlugin的插件里getView拿到的都是右边的页面,只有继承了AbstractTreeListPlugin才能通过getTreeListView()拿到左边的页面和监听左树相应事件
二、代码实现
以单据里一个普通文本字段(下图中的分组字段)作为左树实现左树右表为例:
首先修改【列表表单模板】,【F7列表表单模板】(此时保存预览就能看到上图效果,左树已经有了,只是为空)
单据注册插件继承自AbstractTreeListPlugin,实现initializeTree方法初始化左树:
这里首先要去查询左树的数据,由于示例用的是单据的文本字段作为左树,所以是去查询所有单据数据后再去重,如果你们的左树是单据上的某个基础资料,改下查询为查这个基础资料数据即可(查出id和name就行)
拿到根节点的TreeNode,添加下级节点(TreeNode构造方法的第二个参数是节点id,这里我用的文本做左树,没有现成的id,就用文本代替了,用基础资料做左树的建议用id)
this.getTreeModel().addNode方法可以给指定节点添加下级节点,这里我左树只做一级,所以都是给根节点添加下级,大家想做多级的,调整第一个入参即可(TreeNode为树形结构,有parentid和children)
public class TestTreeListPlugin extends AbstractTreeListPlugin { @Override public void initializeTree(EventObject e) { super.initializeTree(e); //左树数据来源 DynamicObjectCollection col = QueryServiceHelper.query("a2h6_treelisttest","a2h6_textfield",new QFilter[]{}); if (CollectionUtils.isEmpty(col)){ return; } Set<String> set = col.stream().map(dy -> dy.getString("a2h6_textfield")).collect(Collectors.toSet()); //开始插入左树节点 TreeNode root = this.getTreeModel().getRoot(); for (String nodeName : set) { this.getTreeModel().addNode(root.getId(), new TreeNode(root.getId(),nodeName,nodeName)); } //控制根节点是否可见 // this.getTreeModel().setRootVisable(false); } }
效果如图所示,根节点默认叫【全部】,想修改文本的在上面加上root.setText("文本");即可,也可以调整根节点可见性,更多方法见左树右表单据列表插件-数据模型 (kingdee.com)
再就是处理左树搜索框和工具栏:
initTreeToolbar方法可以让我们决定是否展示搜索框,新增,编辑,删除按钮
@Override public void initTreeToolbar(EventObject e) { //父类方法会调用this.getView().setVisible(true, new String[]{"btnnew", "btnedit", "btndel", "searchap"}); // super.initTreeToolbar(e); }
search方法可以监听左树的搜索,这里我们要根据搜索内容过滤左树,得先把以前的子节点清掉,再重新查出左树数据再添加新的子节点,最后刷新根节点
@Override public void search(SearchEnterEvent evt) { super.search(evt); //清理以前的子节点 TreeNode root = this.getTreeModel().getRoot(); if (CollectionUtils.isNotEmpty(root.getChildren())){ root.getChildren().clear(); } //根据查询内容查询查询左树数据 QFilter[] filters = StringUtils.isEmpty(evt.getText())?new QFilter[]{}: new QFilter("a2h6_textfield", QCP.like,"%"+evt.getText()+"%").toArray(); DynamicObjectCollection col = QueryServiceHelper.query("a2h6_treelisttest","a2h6_textfield", filters); if (CollectionUtils.isNotEmpty(col)) { Set<String> set = col.stream().map(dy -> dy.getString("a2h6_textfield")).collect(Collectors.toSet()); //开始插入左树节点 for (String nodeName : set) { this.getTreeModel().addNode(root.getId(), new TreeNode(root.getId(), nodeName, nodeName)); } } //刷新节点 this.getTreeListView().refreshTreeNode(root.getId()); //补充功能:同时刷新右表,会和右表过滤控件已选择条件互斥,目前我的处理不太完善,慎用 if (StringUtils.isNotEmpty(evt.getText())){ QFilter filter = new QFilter("a2h6_textfield", QCP.like,"%"+evt.getText()+"%"); ListShowParameter listShowParameter = (ListShowParameter)this.getView().getFormShowParameter(); List<QFilter> filterList = listShowParameter.getListFilterParameter().getQFilters(); filterList.add(filter); ((IListView)this.getView()).refresh(); }else { ((IListView)this.getView()).refresh(); } }
效果如图所示
treeToolbarClick方法可以监听工具栏的点击事件,如果左树用的基础资料,点击新增可以自行showForm新增页面,然后在closedCallBack方法里参考search方法清掉旧节点,重新插入新节点,再刷新根节点。点击编辑、删除同理,这里就不贴代码了,打个断点看下入参格式
buildTreeListFilter方法在点击左树节点时会触发,这时需要过滤右表,添加filter即可(这里nodeId我在构造节点时传的就是文本本身,所以直接拿来用了)
@Override public void buildTreeListFilter(BuildTreeListFilterEvent e) { super.buildTreeListFilter(e); //点击的是根节点,即全部,不用给右表设置过滤条件 if (e.getNodeId().equals(this.getTreeModel().getRoot().getId())){ return; } //这里的过滤和右表搜索框也会互斥 e.addQFilter(new QFilter("a2h6_textfield", QCP.equals,e.getNodeId())); }
效果如图所示
这样一个左树右表页面就完成了,更多事件可参考:左树右表单据列表插件事件总览 (kingdee.com)