最近用到了el-tree控件,主要是数据的格式,按照官网的数据格式来就可以显示节点的树形结构了。
代码参考很多
这里给出一个比较好的链接:https:///article/181990.htm
https:///article/181990.htm代码说明在注释里写的很详细了已经,这里不再叙述说明。至于为什么抽取成这种格式的数据,那是因为ElementUI-tree规定的数据格式,你想要用这个控件,就必须按照他们规定的这个格式 来。
数据格式如下:
Controller代码
@RequestMapping("/cateList")

@ResponseBody

public List 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 tbCategories = categoryService.cateList();


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


Map treeMap = new HashMap();

Object itemTree;


for(int i = 0;i
itemTree = tbCategories.get(i);

treeMap.put(tbCategories.get(i).getNodeId(),tbCategories.get(i));// 把所有的数据都放到map中



}


// 这里也可以用另一种方法,就是拿到集合里的每个元素的父id去数据库中查询,但是,这样与数据库的交互次数就太多了
// 遍历map得到顶层节点(游离节点也算作顶层节点)

for(int i =0;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
// 数据库中,若一个元素有子节点,那么,该元素的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());

}

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;

}
@RequestMapping("/cateList")

@ResponseBody

public List 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 tbCategories = categoryService.cateList();


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


Map treeMap = new HashMap();

Object itemTree;


for(int i = 0;i
itemTree = tbCategories.get(i);

treeMap.put(tbCategories.get(i).getNodeId(),tbCategories.get(i));// 把所有的数据都放到map中



}


// 这里也可以用另一种方法,就是拿到集合里的每个元素的父id去数据库中查询,但是,这样与数据库的交互次数就太多了
// 遍历map得到顶层节点(游离节点也算作顶层节点)

for(int i =0;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
// 数据库中,若一个元素有子节点,那么,该元素的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());

}

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 childList;
private Long nodeId;

private String categoryName;

private Long parentId;

private Long childId;

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

:props="defaultProps"

@node-click="handleNodeClick"

node-key="nodeId"

show-checkbox=true>



:props="defaultProps"

@node-click="handleNodeClick"

node-key="nodeId"

show-checkbox=true>

js:
defaultProps:{

children: 'childList',

label: 'categoryName' // 这里的名字要和你封装的数据中的节点的名字一样

}
// 点击事件
handleNodeClick: function (data) {

console.log("没做处理");

}
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 cateList() throws Exception{

List tbCategories = categoryService.cateList();

List treeList = TreeUtils.getTreeList(tbCategories,"nodeId", "parentId", "categoryName");


return treeList;

}
@RequestMapping("/cateList")
@ResponseBody
public List cateList() throws Exception{

List tbCategories = categoryService.cateList();

List treeList = TreeUtils.getTreeList(tbCategories,"nodeId", "parentId", "categoryName");


return treeList;

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

public class TreeUtils {


private Integer id;
// 节点id

private Integer parentId;
// 父节点

private String name;
// 节点名称 ,返回给前台的是一个装有TreeUtils的集合的数据,所以在前台显示数据的时候,el-tree的lable的名字的和这个一样

private List childList; // 父节点中存放子节点的集合

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

public class TreeUtils {


private Integer id;
// 节点id

private Integer parentId;
// 父节点

private String name;
// 节点名称 ,返回给前台的是一个装有TreeUtils的集合的数据,所以在前台显示数据的时候,el-tree的lable的名字的和这个一样

private List childList; // 父节点中存放子节点的集合

private T data;
// 节点数据方法
/**

* @param listData
// 从数据库中查询的数据

* @return

*/

public static List getTreeList(List listData ,String id,String parentId,String categoryName) throws Exception{


List resultList = new ArrayList(); // 最终返回的结果

Map map = new HashMap();


for(int i =0;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
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
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;

}

/**

* @param listData
// 从数据库中查询的数据

* @return

*/

public static List getTreeList(List listData ,String id,String parentId,String categoryName) throws Exception{


List resultList = new ArrayList(); // 最终返回的结果

Map map = new HashMap();


for(int i =0;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
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
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);
// 这里就体现出反射的意思了,我们通常都是通过对象拿到字段,这里是通过字段,将类的字节码对象为参数传入,来得到值

}
/**

* 通过反射得到的数据类型的也是不一定的,所以这里我们返回值为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,?等泛型还只是会简单的用,并不熟练,以后要多加学习。 
标签:

你有充足的理由选择我们

深圳上海杭州北京前端外包开发:工作10年以上的小伙伴团队,前端开发工作是我们最大的事业。所有您担心的问题,都可以写到合同里。我们会100%努力完成,直到您满意!