Element的el-tree控件后台数据结构的生成以及方法的抽取


Posted in Javascript onMarch 05, 2020

最近用到了el-tree控件,主要是数据的格式,按照官网的数据格式来就可以显示节点的树形结构了。
代码参考很多

这里给出一个比较好的链接:https://3water.com/article/181990.htm

代码说明在注释里写的很详细了已经,这里不再叙述说明。至于为什么抽取成这种格式的数据,那是因为ElementUI-tree规定的数据格式,你想要用这个控件,就必须按照他们规定的这个格式 来。
数据格式如下:

Element的el-tree控件后台数据结构的生成以及方法的抽取

Controller代码

@RequestMapping("/cateList")
  @ResponseBody
  public List<TbCategory> cateList() {

    // 整体思路:
    // 1、取得所有数据、放入集合List1 (tbCategories)
    // 2、将List1所有数据都放入到map(treeMap)中:元素id为键,元素本身对象为值
    // 3、取得顶层节点放入集合List2中(resultList)
    // 4、遍历List1中的所有数据,通过数据的parentId为键在map中取值
    //   1)如果能取到,则说明该元素有父节点
    //      1、判断该父节点下的childList中是否有已经子节点
    //       1、若无:则创建一个集合,将子节点放入
    //       2、若有:则直接将子节点放入即可
    // 5、把放好的数据放回到map中
    // 6、返回List2(resultList)

    // 注意:整个过程将所有数据取出放入list2(resultList),返回的也是  //list2


    List<TbCategory> tbCategories = categoryService.cateList();

    List<TbCategory> resultList = new ArrayList<TbCategory>(); // 存贮顶层的数据

    Map<Object ,Object> treeMap = new HashMap();
    Object itemTree;

    for(int i = 0;i<tbCategories.size() && !tbCategories.isEmpty();i++){
      itemTree = tbCategories.get(i);
      treeMap.put(tbCategories.get(i).getNodeId(),tbCategories.get(i));// 把所有的数据都放到map中


    }


// 这里也可以用另一种方法,就是拿到集合里的每个元素的父id去数据库中查询,但是,这样与数据库的交互次数就太多了
 // 遍历map得到顶层节点(游离节点也算作顶层节点)
    for(int i =0;i<tbCategories.size();i++){
      // 优点1:整个方法,只查询了一次数据库
      // 优点2:不用知道顶层节点的id
     if(!treeMap.containsKey(tbCategories.get(i).getParentId())){
        // 我们在存储的时候就是将元素的id为键,元素本身为值存入的
        // 以元素的父id为键,在map里取值,若取不到则,对应的元素不存在,即没有父节点,为顶层节点或游离节点
        // 将顶层节点放入list集合
        resultList.add(tbCategories.get(i));
      }
    }

    // 循环数据,将数据放到该节点的父节点的children属性中
    for(int i =0 ;i<tbCategories.size()&& !tbCategories.isEmpty();i++){
      // 数据库中,若一个元素有子节点,那么,该元素的id为子节点的父id
      //treeMap.get(tbCategories.get(i).getParentId()); // 从map集合中找到父节点
      TbCategory category = (TbCategory)treeMap.get(tbCategories.get(i).getParentId());
      if(category!=null ){ // 不等于null,也就意味着有父节点
        // 有了父节点,要判断父节点下存贮字节点的集合是否存在,然后将子节点放入
        if(category.getChildList() == null){
          // 判断一个集合是否被创建用null:表示结合还没有被分配内存空间(即还没有被创建),内存大小自然为null
          // 用集合的size判断集合中是否有元素,为0,没有元素(集合已经被创建),
          category.setChildList(new ArrayList<TbCategory>());
        }
        category.getChildList().add(tbCategories.get(i)); // 添加到父节点的ChildList集合下

        // 这一步其实可以不要,因为我们修改了数据(添加了子节点,然后在将元素放入到map中,
        // 若键相同,map会自动覆盖掉相同的键值对,达到更新map集合中的数据的目的),但是我们
        // 这里只是从map中取值,而并不关心值的子节点(子节点是对象本身自己封装的。这里我们知道
        // 元素从查询后放入map,父节点放入list,然后通过键来在map中取得对象,之后再将修改过的对象重新放入map当中
        // ,我们并没有直接操作list,但是在list中对象的值却是已经修改过了,这就是对象的引用传递,同一个引用对象是通过
        // 地址值来操作对象的,即有不同的引用,但是对象中的属性是已经通过引用的操作而改变的,所以这里一旦修改过后,无论是map中还是list中,再次取值时都已经是更改过后的值了)
        treeMap.put(tbCategories.get(i).getParentId(),category); // 把放好的数据放回到map中
      }

    }


   return resultList;
  }

实体类:

private Long nodeId;
  private String categoryName;
  private Long parentId;
  private Long childId;
  private List<T> childList;

以上数据都在后台封装好了,前台直接获取数据显示即可

<el-tree :data="treeList"
       :props="defaultProps"
       @node-click="handleNodeClick"
       node-key="nodeId"
       show-checkbox=true>
  </el-tree>

js:

defaultProps:{
        children: 'childList',
        label: 'categoryName' // 这里的名字要和你封装的数据中的节点的名字一样
      }
// 点击事件
 handleNodeClick: function (data) {
        console.log("没做处理");
      }

方法抽取

上面的方法虽然也能用,但是想把这个方法抽取成一个通用的方法,以后再写的时候就可以直接调取该方法了。抽取的过程中还是遇到了很多的问题的。

例如实体类A是对应数据库中存储节点的表的实体类,但是,实体类中是不存在setChildList、getChildList集合这些方法的。我就把这些存贮信息的字段写在了一个工具类里面,然后方法也在该工具类里。这样返回的时候就要返回一个装有该工具类的一个集合了。还有一个问题就是该工具类中都需要那些字段?因为是一棵树,所以我们需要节点id,几点的父id,节点的名称,以及存储子节点的集合,如果想要更多的数据,可以添加一个 T data泛型,该泛型直接将原本存储节点的实体类对象存储进来。这样所有的数据就都整齐了,数据结构也就完整了。

contrell代码:

因为我们想抽取一个公用的方法,那么参数的类型就是不确定的,即传入的list中元素的类型是不固定的,所以要用到泛型。上面说过了,我们要拿到节点id,父id,以及节点的名称赋值给工具类中对应的字段。但是既然是用泛型,所以就不知道对象类型,所以我们在工具类中是点不出相应的方法来取到值的,我们就要用到反射,反射的知道类中具体的字段或者方法的名字来取值,所以我们在controller来调用工具类的时候就要,将节点id、父id和节点名称传入。当然了还要传入从后台查询到了装有数据的list集合。

contreller代码如下所示:

@RequestMapping("/cateList")
@ResponseBody
public List<TreeUtils> cateList() throws Exception{
    List<TbCategory> tbCategories = categoryService.cateList();
    List<TreeUtils> treeList = TreeUtils.getTreeList(tbCategories,"nodeId", "parentId", "categoryName");

    return treeList;
  }

工具类:

// 抽取方法的时候要考虑一个问题,即,返回一个集合,集合中有父节点和字节点,父节点和字节点的类型一定要统一,
// 即这里返回的是一个装有TreeUtils类型的集合,那么集合里的父节点和子节点一定都得是TreeUtils类型的

public class TreeUtils<T> {

  private Integer id;      // 节点id
  private Integer parentId;  // 父节点
  private String name;     // 节点名称 ,返回给前台的是一个装有TreeUtils的集合的数据,所以在前台显示数据的时候,el-tree的lable的名字的和这个一样
  private List<TreeUtils> childList; // 父节点中存放子节点的集合
  private T data;       // 节点数据

方法

/**
   * @param listData  // 从数据库中查询的数据
   * @return
   */
  public static List<TreeUtils> getTreeList(List<?> listData ,String id,String parentId,String categoryName) throws Exception{

    List<TreeUtils> resultList = new ArrayList<TreeUtils>(); // 最终返回的结果
    Map<Integer ,Object> map = new HashMap<Integer,Object>();

    for(int i =0;i<listData.size() && !listData.isEmpty();i++){

      // 写一个与该方法差不多的方法,将得到TreeUtils的代码抽取出来
      // 也可以将listData集合整个转换成装有TreeUtils的集合x,然后再循环x
      TreeUtils treeUtils = new TreeUtils();
      treeUtils.setId(Integer.parseInt(TreeUtils.getFileValue(listData.get(i),id).toString())); // id       // 返回值为Object无法直接转换成Integer,先toString,再转换成Integer。这里的返回值写成Object是因为多种类型字段的值都可以用该方法
      treeUtils.setParentId(Integer.parseInt(TreeUtils.getFileValue(listData.get(i),parentId).toString())); // 父id
      treeUtils.setName(TreeUtils.getFileValue(listData.get(i),categoryName).toString()); // 节点名
      //System.out.println("节点名为+"+TreeUtils.getFileValue(listData.get(i),categoryName).toString());
      treeUtils.setData(listData.get(i));  // data:原对象中的所有属性,无children

      // 通过反射得到每条数据的id将数据封装的map集合中,id为键,元素本身为值
      map.put(treeUtils.getId(),treeUtils);


      // 将所有顶层元素添加到resultList集合中
      //if( 0 == treeUtils.getParentId()){
       //  resultList.add(treeUtils);
      // }
    }
// 得到所有的顶层节点,游离节点也算作顶层节点
// 优点一,不用知道等级节点的id
// 优点而,只查询了一次数据库
    for(int i =0;i<listData.size();i++){
      if(!map.containsKey(Integer.parseInt(TreeUtils.getFileValue(listData.get(i),parentId).toString()))){
        resultList.add((TreeUtils) map.get(Integer.parseInt(TreeUtils.getFileValue(listData.get(i),id).toString())));
      }
    }



    for(int i =0;i<listData.size() && !listData.isEmpty();i++){
      TreeUtils obj = (TreeUtils)map.get(Integer.parseInt(TreeUtils.getFileValue(listData.get(i), parentId).toString()));
      if(obj != null){
        if(obj.getChildList() == null){
          obj.setChildList(new ArrayList());
        }
        obj.getChildList().add(map.get(Integer.parseInt(TreeUtils.getFileValue(listData.get(i),id).toString())));
      }
    }
    return resultList;
  }

反射的方法

/**
   * 通过反射得到的数据类型的也是不一定的,所以这里我们返回值为Object
   * Object是无法直接转为Integer,现将Object转为String,然后再将String转为Integer
   * @param item
   * @param fileName
   * @return
   */
  public static Object getFileValue(Object item,String fileName) throws Exception {
    Class<?> aClass = item.getClass();
    Field file = aClass.getDeclaredField(fileName); // 得到所有字段包括私有字段
    file.setAccessible(true); // 取消访问限制
    return file.get(item);  // 这里就体现出反射的意思了,我们通常都是通过对象拿到字段,这里是通过字段,将类的字节码对象为参数传入,来得到值
  }

ps:抽取方法遇到了很多的问题,其中的T,?等泛型还只是会简单的用,并不熟练,以后要多加学习。

 到此这篇关于Element的el-tree控件后台数据结构的生成以及方法的抽取的文章就介绍到这了,更多相关Element el-tree生成及方法抽取内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
响应鼠标变换表格背景或者颜色的代码
Mar 30 Javascript
Javascript事件热键兼容ie|firefox
Dec 30 Javascript
重写javascript中window.confirm的行为
Oct 21 Javascript
你必须知道的Javascript知识点之&quot;字面量和对应类型&quot;说明介绍
Apr 23 Javascript
Javascript弹出窗口的各种方法总结
Nov 11 Javascript
jquery实现pager控件示例
Apr 09 Javascript
使用jquery操作session方法分享
Jan 22 Javascript
分享9点个人认为比较重要的javascript 编程技巧
Apr 27 Javascript
基于jquery实现导航菜单高亮显示(两种方法)
Aug 23 Javascript
JavaScript必知必会(十) call apply bind的用法说明
Jun 08 Javascript
jQuery模拟完美实现经典FLASH导航动画效果【附demo源码下载】
Nov 09 Javascript
vscode自定义vue模板的实现
Jan 27 Vue.js
element-ui树形控件后台返回的数据+生成组织树的工具类
Mar 05 #Javascript
vue中使用vue-print.js实现多页打印
Mar 05 #Javascript
koa2的中间件功能及应用示例
Mar 05 #Javascript
微信小程序利用for循环解决内容变更问题
Mar 05 #Javascript
javascript将16进制的字符串转换为10进制整数hex
Mar 05 #Javascript
js将URL网址转为16进制加密与解密函数
Mar 04 #Javascript
基于node+websocket+html实现腾讯课堂聊天室聊天功能
Mar 04 #Javascript
You might like
php array_flip() 删除数组重复元素
2009/01/14 PHP
PHP 文件上传源码分析(RFC1867)
2009/10/30 PHP
浅析PHP程序防止ddos,dns,集群服务器攻击的解决办法
2013/06/18 PHP
destoon实现底部添加你是第几位访问者的方法
2014/07/15 PHP
PHP实现通过中文字符比率来判断垃圾评论的方法
2014/10/20 PHP
PHP SPL标准库之接口(Interface)详解
2015/05/11 PHP
Yii2框架实现数据库常用操作总结
2017/02/08 PHP
PHP实现读取文件夹及批量重命名文件操作示例
2019/04/15 PHP
让回调函数 showResponse 也带上参数的代码
2007/08/13 Javascript
Yii-自定义删除确认弹框(zyd)jquery实现代码
2013/03/04 Javascript
利用jQuery及AJAX技术定时更新GridView的某一列数据
2015/12/04 Javascript
js表单提交和submit提交的区别实例分析
2015/12/10 Javascript
js实现的页面矩阵图形变换特效
2016/01/26 Javascript
JavaScript知识点总结(六)之JavaScript判断变量数据类型
2016/05/31 Javascript
jQuery插件FusionCharts绘制的3D饼状图效果实例【附demo源码下载】
2017/03/03 Javascript
Iscrool下拉刷新功能实现方法(推荐)
2017/06/26 Javascript
JS实现移动端按首字母检索城市列表附源码下载
2017/07/05 Javascript
AngularJS使用ng-repeat遍历二维数组元素的方法详解
2017/11/11 Javascript
详解vue.js数据传递以及数据分发slot
2018/01/20 Javascript
JavaScript中call和apply方法的区别实例分析
2018/08/03 Javascript
ios设备中angularjs无法改变页面title的解决方法
2018/09/13 Javascript
JS动画实现回调地狱promise的实例代码详解
2018/11/08 Javascript
使用 Vue 实现一个虚拟列表的方法
2019/08/20 Javascript
js 将多个对象合并成一个对象 assign方法的实现
2020/09/24 Javascript
Python通过正则表达式选取callback的方法
2015/07/18 Python
Python分析微信好友性别比例和省份城市分布比例的方法示例【基于itchat模块】
2020/05/29 Python
Python3.9.0 a1安装pygame出错解决全过程(小结)
2021/02/02 Python
Html5元素及基本语法详解
2016/08/02 HTML / CSS
联想西班牙官网:Lenovo西班牙
2018/08/28 全球购物
环保专业大学生职业规划设计
2014/01/10 职场文书
公司业务员岗位职责
2014/03/18 职场文书
《金孔雀轻轻跳》教学反思
2014/04/20 职场文书
2014年幼儿园小班工作总结
2014/12/04 职场文书
2015年青年志愿者工作总结
2015/05/20 职场文书
拉贝日记观后感
2015/06/05 职场文书
Python3.8官网文档之类的基础语法阅读
2021/09/04 Python