一直想做分类下拉,然后选择后搜索的页面,正好做项目有了明确的需求,查找后发现el-tree的构件可满足需求,数据要求为:{ id:1, label:name, childer:[……] }形式的,于是乎,开搞!
一、效果预览 编辑
点击后 编辑
二、实现步骤 1、数据库设计层面: 在设计数据库的分类表时,需要设置为层级结构的样式,指明父分类id和祖分类id ,如此才可满足分类需求。
具体如下,比如我的书签分类表:markClass(以下只写出主要三列)
属性
数据类型
字段说明
举例
markClassId
int
书签分类的id
1001
parentid
int
该分类的上级分类id(父id)
1000
ancestors
string
该分类所有上属分类id(祖id)
0,10,100,1000
2、后端基础的构建 依据表格完成对后端基础增删改的设置,可以用Ruoyi一键生成。以下主要说明插入时,ancestors的设置:
插入时需要依据父分类id来完成对ancestors的设置。(注意设置ancestors的getter、setter方法)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 private MarkclassMapper markclassMapper; public int insertMarkclass (Markclass markclass ) { Markclass mar= markclassMapper.selectMarkclassByMarkClassId (markclass.getParentid ()); markclass.setAncestors (mar.getAncestors ()+"," +markclass.getParentid ()); return markclassMapper.insertMarkclass (markclass); }
3、主代码——构建树相关 (1)创建适配el-tree数据类型的类 el-tree的构件数据要求为:{ id:1, label:name, childer:[……] }形式,于是可定义一个“TreeSelect”类(类名称随意)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 package com.blhq.wjs.domain;import java.io.Serializable;import java.util.List;import java.util.stream.Collectors;import com.blhq.common.core.domain.entity.SysMenu;import com.fasterxml.jackson.annotation.JsonInclude;public class TreeSelect implements Serializable { private static final long serialVersionUID = 1L ; private Long id; private String label; private List<TreeSelect> children; public TreeSelect (Class aclass) { this .id = aclass.getClassid(); this .label = aclass.getClassname(); this .children = aclass.getClass_children(). stream().map(TreeSelect::new ).collect(Collectors.toList()); } public TreeSelect (Markclass markclass) { this .id=markclass.getMarkClassId(); this .label=markclass.getClassname(); this .children=markclass.getMarkChildren(). stream().map(TreeSelect::new ).collect(Collectors.toList()); } public TreeSelect (SysMenu menu) { this .id = menu.getMenuId(); this .label = menu.getMenuName(); this .children = menu.getChildren(). stream().map(TreeSelect::new ).collect(Collectors.toList()); } public Long getId () { return id; } public void setId (Long id) { this .id = id; } public String getLabel () { return label; } public void setLabel (String label) { this .label = label; } public List<TreeSelect> getChildren () { return children; } public void setChildren (List<TreeSelect> children) { this .children = children; } }
补充:Markclass类 1 2 3 4 5 6 7 8 9 10 private List<Markclass> markChildren=new ArrayList <Markclass>();public List<Markclass> getMarkChildren () { return markChildren; } public void setMarkChildren (List<Markclass> markChildren) { this .markChildren = markChildren; }
(2)service层: 在你的service类中创建方法:(如果没有service层,则无视,直接看实现层操作即可)
注意:部分返回值类型为刚刚定义的TreeSelect的列表。
1 2 3 4 5 6 7 8 9 public List<TreeSelect> selectMarkClassTreeList (Markclass markclass) ; public List<Markclass> buildMarkClassTree (List<Markclass> markclassList) ; public List<TreeSelect> buildMarkClassTreeSelect (List<Markclass> classes) ;
(3)Impl实现层: 核心代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 @Override public List<TreeSelect> selectMarkClassTreeList (Markclass markclass) { List<Markclass> markClasses = this .selectMarkclassList(markclass); return buildMarkClassTreeSelect(markClasses); } @Override public List<TreeSelect> buildMarkClassTreeSelect (List<Markclass> classes) { List<Markclass> markClasses = buildMarkClassTree(classes); return markClasses.stream().map(TreeSelect::new ).collect(Collectors.toList()); } @Override public List<Markclass> buildMarkClassTree (List<Markclass> markclassList) { List<Markclass> returnList=new ArrayList <Markclass>(); List<Long> tempList = markclassList.stream().map(Markclass::getMarkClassId).collect(Collectors.toList()); for (Markclass markclass : markclassList) { if (!tempList.contains(markclass.getParentid())){ recursionFn(markclassList, markclass); returnList.add(markclass); } } if (returnList.isEmpty()) { returnList = markclassList; } return returnList; } private void recursionFn (List<Markclass> list, Markclass t) { List<Markclass> childList = getChildList(list, t); t.setMarkChildren(childList); for (Markclass tChild : childList) { if (hasChild(list, tChild)) { recursionFn(list, tChild); } } } private List<Markclass> getChildList (List<Markclass> list, Markclass t) { List<Markclass> tlist = new ArrayList <Markclass>(); for (Markclass n : list) { if (StringUtils.isNotNull(n.getParentid()) && n.getParentid().longValue() == t.getMarkClassId().longValue()) { tlist.add(n); } } return tlist; } private boolean hasChild (List<Markclass> list, Markclass t) { return getChildList(list, t).size() > 0 ; }
完整Impl层代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 package com.blhq.wjs.service.impl;import java.util.ArrayList;import java.util.List;import java.util.stream.Collectors;import com.blhq.common.utils.StringUtils;import com.blhq.wjs.domain.TreeSelect;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import com.blhq.wjs.mapper.MarkclassMapper;import com.blhq.wjs.domain.Markclass;import com.blhq.wjs.service.IMarkclassService;@Service public class MarkclassServiceImpl implements IMarkclassService { @Autowired private MarkclassMapper markclassMapper; @Override public Markclass selectMarkclassByMarkClassId (Long markClassId) { return markclassMapper.selectMarkclassByMarkClassId(markClassId); } @Override public List<Markclass> selectMarkclassList (Markclass markclass) { return markclassMapper.selectMarkclassList(markclass); } @Override public int insertMarkclass (Markclass markclass) { if (markclass.getMarkClassId() == null ) { return markclassMapper.insertMarkclass(markclass); }else { Markclass mar=markclassMapper.selectMarkclassByMarkClassId(markclass.getParentid()); markclass.setAncestors(mar.getAncestors()+"," +markclass.getParentid()); return markclassMapper.insertMarkclass(markclass); } } @Override public int updateMarkclass (Markclass markclass) { return markclassMapper.updateMarkclass(markclass); } @Override public int deleteMarkclassByMarkClassIds (Long[] markClassIds) { return markclassMapper.deleteMarkclassByMarkClassIds(markClassIds); } @Override public int deleteMarkclassByMarkClassId (Long markClassId) { return markclassMapper.deleteMarkclassByMarkClassId(markClassId); } @Override public List<TreeSelect> selectMarkClassTreeList (Markclass markclass) { List<Markclass> markClasses = this .selectMarkclassList(markclass); return buildMarkClassTreeSelect(markClasses); } @Override public List<TreeSelect> buildMarkClassTreeSelect (List<Markclass> classes) { List<Markclass> markClasses = buildMarkClassTree(classes); return markClasses.stream().map(TreeSelect::new ).collect(Collectors.toList()); } @Override public List<Markclass> buildMarkClassTree (List<Markclass> markclassList) { List<Markclass> returnList=new ArrayList <Markclass>(); List<Long> tempList = markclassList.stream().map(Markclass::getMarkClassId).collect(Collectors.toList()); for (Markclass markclass : markclassList) { if (!tempList.contains(markclass.getParentid())){ recursionFn(markclassList, markclass); returnList.add(markclass); } } if (returnList.isEmpty()) { returnList = markclassList; } return returnList; } private void recursionFn (List<Markclass> list, Markclass t) { List<Markclass> childList = getChildList(list, t); t.setMarkChildren(childList); for (Markclass tChild : childList) { if (hasChild(list, tChild)) { recursionFn(list, tChild); } } } private List<Markclass> getChildList (List<Markclass> list, Markclass t) { List<Markclass> tlist = new ArrayList <Markclass>(); for (Markclass n : list) { if (StringUtils.isNotNull(n.getParentid()) && n.getParentid().longValue() == t.getMarkClassId().longValue()) { tlist.add(n); } } return tlist; } private boolean hasChild (List<Markclass> list, Markclass t) { return getChildList(list, t).size() > 0 ; } }
(4)controller层: 这时候,我们直接添加即可:推荐把它放到对应需要分类显示的文件中,比如我的是在书签的controller中。
1 2 3 4 5 6 @GetMapping("/deptTree") public AjaxResult deptTree (Markclass markclass) { List<TreeSelect> list = markclassService.selectMarkClassTreeList(markclass); return success(list); }
(5)xml文件: 修改查找逻辑的语句:将分类的查询改为,从markclas表的ancestors查:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 < select id= "selectBookmarkList" parameterType= "Bookmark" resultMap= "BookmarkResult"> select b.markId, b.markName, b.markClassId, b.website, b.`desc `, b.createTime, b.editTime, b.icon, b.statue, b.commonGrade, b.allGrade, b.markPlot, b.likes, b.markExtend, c.classname, c.ancestors from bookmark b left join markclass c on b.markClassId = c.markClassId where 1 = 1 < if test= "markId != null "> and markId = #{markId}< / if> < if test= "markName != null and markName != ''"> and markName like concat('%' , #{markName}, '%' )< / if> < if test= "markClassId != null "> AND (b.markClassId = #{markClassId} OR b.markClassId IN ( SELECT c.markClassId FROM class c WHERE find_in_set(#{markClassId}, ancestors) )) < / if> < if test= "website != null and website != ''"> and website like concat('%' , #{website}, '%' )< / if> < if test= "desc != null and desc != ''"> and b.`desc ` like concat('%' , #{desc }, '%' )< / if> < if test= "createTime != null "> and b.createTime & gt;= #{createTime}< / if> < if test= "editTime != null "> and b.editTime & lt;= #{editTime}< / if> < if test= "icon != null and icon != ''"> and icon like concat('%' , #{icon}, '%' )< / if> < if test= "statue != null "> and statue = #{statue}< / if> < if test= "commonGrade != null "> and commonGrade = #{commonGrade}< / if> < if test= "allGrade != null "> and allGrade like concat('%' , #{allGrade}, '%' )< / if> < if test= "markPlot != null and markPlot != ''"> and markPlot = #{markPlot}< / if> < if test= "likes != null "> and likes like concat('%' , #{likes}, '%' )< / if> < if test= "markExtend != null "> and markExtend like concat('%' , #{markExtend}, '%' )< / if> < / select >
4、前端应用: 整体思路就是引用它,把它调过来使用就行。下面按我的思路来演示:
(1)引入api: 1 2 3 4 5 6 7 8 export function markClassTreeSelect (query ) { return request ({ url : '/markclass/markclass/deptTree' , method : 'get' , params : query }) }
(2)页面导入 注意将v-model中的值,改为你自己的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 <template > <el-row :gutter ="20" > <el-col :span ="4" :xs ="24" > <div class ="head-container" > <el-input v-model ="markName" placeholder ="请输入书签分类名称" clearable size ="small" prefix-icon ="el-icon-search" style ="margin-bottom: 20px" /> </div > <el-tree :data ="markOptions" :props ="defaultProps" :expand-on-click-node ="false" :filter-node-method ="filterNode" ref ="tree" node-key ="id" default-expand-all highlight-current @node-click ="handleNodeClick" /> </el-col > <el-col :span ="20" :xs ="24" > ………… </el-col > </el-row > …………//你的其它页面布局代码,上文gutter表示左侧分类栏占比大小。 </template >
内需要添加的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 data ( ) { return { markName :undefined , markOptions : undefined , defaultProps : { children : "children" , label : "label" }, }, }, watch : { markName (val ) { this .$refs .tree .filter (val); } }, created ( ) { this .getMarkTree (); }, methods : { getMarkTree ( ) { markClassTreeSelect ().then (response => { this .markOptions = response.data ; console .log (response.data ); }); }, filterNode (value, data ) { if (!value) return true ; return data.label .indexOf (value) !== -1 ; }, handleNodeClick (data ) { this .queryParams .markClassId = data.id ; this .handleQuery (); }, },
5、成果展示: 编辑
点击游戏类后:
编辑
三、我遇到的问题
(1):无法自动装配。找不到’MarkclassMapper’类型的 Bean。
方案:在MarkclassMapper中,最前面加入@Mapper注解即可
(2)Failed to instantiate: Factory method ‘sqlSessionFactory’ threw exception; TypeException: The alias ‘TreeSelect’ is already mapped to the value ‘com.blhq.wjs.domain.TreeSelect’.
方案:如果提示这个,请把我们刚才定义的TreeSelect类,重构一下,改个名就行。
更详细说明请看下文: