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 相关文章推荐
JavaScript delete操作符应用实例
Jan 13 Javascript
js操作Xml(向服务器发送Xml,处理服务器返回的Xml)(IE下有效)
Jan 30 Javascript
jQuery开发者都需要知道的5个小技巧
Jan 08 Javascript
浏览器的JavaScript引擎的识别方法
Oct 20 Javascript
js生成随机数(指定范围)的实例代码
Jul 10 Javascript
three.js实现3D影院的原理的代码分析
Dec 18 Javascript
详解vue中移动端自适应方案
May 05 Javascript
详解使用uni-app开发微信小程序之登录模块
May 09 Javascript
小程序实现图片移动缩放效果
May 26 Javascript
JS制作简易计算器的实例代码
Jul 04 Javascript
vue动态加载SVG文件并修改节点数据的操作代码
Aug 17 Javascript
OpenLayers3实现地图显示功能
Sep 25 Javascript
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
YII模块实现绑定二级域名的方法
2014/07/09 PHP
php curl中gzip的压缩性能测试实例分析
2016/11/08 PHP
laravel框架模型和数据库基础操作实例详解
2020/01/25 PHP
基于jquery的loading 加载提示效果实现代码
2011/09/01 Javascript
Jquery中val()表单取值赋值的实例代码
2013/08/15 Javascript
js动态修改input输入框的type属性(实现方法解析)
2013/11/13 Javascript
JavaScript中for-in遍历方式示例介绍
2014/02/11 Javascript
JavaScript Sort 的一个错误用法示例
2015/03/20 Javascript
cookie的secure属性详解
2015/04/08 Javascript
JavaScript汉诺塔问题解决方法
2015/04/21 Javascript
基于jQuery仿淘宝产品图片放大镜代码分享
2020/06/23 Javascript
jQuery实现的个性化返回底部与返回顶部特效代码
2015/10/30 Javascript
深入理解JS DOM事件机制
2016/08/06 Javascript
JS实现放大、缩小及拖拽图片的方法【可兼容IE、火狐】
2016/08/23 Javascript
AngularJS下对数组的对比分析
2016/08/24 Javascript
Vue中添加手机验证码组件功能操作方法
2017/12/07 Javascript
详解Vue开发微信H5微信分享签名失败问题解决方案
2018/08/09 Javascript
小程序实现人脸识别功能(百度ai)
2018/12/23 Javascript
详解基于Wepy开发小程序插件(推荐)
2019/08/01 Javascript
vue+elementUi图片上传组件使用详解
2019/08/20 Javascript
vue.js+element 默认提示中英文操作
2020/11/11 Javascript
[37:35]DOTA2上海特级锦标赛A组资格赛#1 Secret VS MVP.Phx第二局
2016/02/25 DOTA
[01:03]悬念揭晓 11月26日DOTA2完美盛典不见不散
2017/11/23 DOTA
Python实现把json格式转换成文本或sql文件
2015/07/10 Python
python xpath获取页面注释的方法
2019/01/14 Python
Python基于pygame实现单机版五子棋对战
2019/12/26 Python
python 密码学示例——凯撒密码的实现
2020/09/21 Python
python 实现简单的计算器(gui界面)
2020/11/11 Python
Web前端页面跳转并取到值
2017/04/24 HTML / CSS
美国CVS药店官网:CVS Pharmacy
2018/07/26 全球购物
MYSQL基础面试题
2012/05/13 面试题
毕业生个人的自我评价优秀范文
2013/10/03 职场文书
小学捐书活动总结
2014/07/05 职场文书
幼儿园大班教师个人总结
2015/02/05 职场文书
生日宴会家属答谢词
2015/09/29 职场文书
选调生挂职锻炼工作总结
2015/10/23 职场文书