Java做網(wǎng)站的學(xué)習(xí)路線網(wǎng)站seo站群軟件
一直想做分類下拉,然后選擇后搜索的頁(yè)面,正好做項(xiàng)目有了明確的需求,查找后發(fā)現(xiàn)el-tree的構(gòu)件可滿足需求,數(shù)據(jù)要求為:{ id:1,? label:name,? childer:[……]? }形式的,于是乎,開(kāi)搞!
一、效果預(yù)覽
點(diǎn)擊后
二、實(shí)現(xiàn)步驟
1、數(shù)據(jù)庫(kù)設(shè)計(jì)層面:
在設(shè)計(jì)數(shù)據(jù)庫(kù)的分類表時(shí),需要設(shè)置為層級(jí)結(jié)構(gòu)的樣式,指明父分類id和祖分類id,如此才可滿足分類需求。
具體如下,比如我的書(shū)簽分類表:markClass(以下只寫(xiě)出主要三列)
屬性 | 數(shù)據(jù)類型 | 字段說(shuō)明 | 舉例 |
markClassId | int | 書(shū)簽分類的id | 1001 |
parentid | int | 該分類的上級(jí)分類id(父id) | 1000 |
ancestors | string | 該分類所有上屬分類id(祖id) | 0,10,100,1000 |
2、后端基礎(chǔ)的構(gòu)建
依據(jù)表格完成對(duì)后端基礎(chǔ)增刪改的設(shè)置,可以用Ruoyi一鍵生成。以下主要說(shuō)明插入時(shí),ancestors的設(shè)置:
插入時(shí)需要依據(jù)父分類id來(lái)完成對(duì)ancestors的設(shè)置。(注意設(shè)置ancestors的getter、setter方法)
private MarkclassMapper markclassMapper;
//這個(gè)是在你寫(xiě)的Impl層中的那個(gè)類里,你的selectById方法在哪里就引用哪個(gè)public int insertMarkclass(Markclass markclass){Markclass mar=markclassMapper.selectMarkclassByMarkClassId(markclass.getParentid());//依據(jù)傳入的markclass對(duì)象的父分類id查找具體的分類markclass.setAncestors(mar.getAncestors()+","+markclass.getParentid());//調(diào)用set方法。按照字符串拼接的方法,獲取父分類所包含的祖分類id,并加上父分類id。return markclassMapper.insertMarkclass(markclass);}
3、主代碼——構(gòu)建樹(shù)相關(guān)
(1)創(chuàng)建適配el-tree數(shù)據(jù)類型的類
el-tree的構(gòu)件數(shù)據(jù)要求為:{ id:1,? label:name,? childer:[……]? }形式,于是可定義一個(gè)“TreeSelect”類(類名稱隨意)
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;
//引入需要的你定義到的實(shí)體類/*** Treeselect樹(shù)結(jié)構(gòu)實(shí)體類* 數(shù)據(jù)類型依據(jù)el-tree數(shù)據(jù)需求進(jìn)行定義,即為:id、label、children,* @author blhq*/
public class TreeSelect implements Serializable
{private static final long serialVersionUID = 1L;/** 節(jié)點(diǎn)ID */private Long id;/** 節(jié)點(diǎn)名稱 */private String label;/** 子節(jié)點(diǎn) */// @JsonInclude(JsonInclude.Include.NON_EMPTY)private List<TreeSelect> children;public TreeSelect(Class aclass) {this.id = aclass.getClassid();this.label = aclass.getClassname();// 檢查此處是否正確設(shè)置了 class_children 屬性this.children = aclass.getClass_children().stream().map(TreeSelect::new).collect(Collectors.toList());}//寫(xiě)了多個(gè)TreeSelect構(gòu)造類,以滿足后續(xù)其它類型對(duì)象的調(diào)用,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;}
}
補(bǔ)充:Markclass類
private List<Markclass> markChildren=new ArrayList<Markclass>();
//必須實(shí)例化,不然會(huì)報(bào)錯(cuò)java.lang.NullPointerException: null
//同時(shí)設(shè)置getter、setter方法
public List<Markclass> getMarkChildren() {return markChildren;
}public void setMarkChildren(List<Markclass> markChildren) {this.markChildren = markChildren;}
(2)service層:
在你的service類中創(chuàng)建方法:(如果沒(méi)有service層,則無(wú)視,直接看實(shí)現(xiàn)層操作即可)
注意:部分返回值類型為剛剛定義的TreeSelect的列表。
public List<TreeSelect> selectMarkClassTreeList(Markclass markclass);//接收用戶傳入的分類,即為當(dāng)前操作的分類public List<Markclass> buildMarkClassTree(List<Markclass> markclassList);//構(gòu)建樹(shù)的核心方法public List<TreeSelect> buildMarkClassTreeSelect(List<Markclass> classes);//注意返回值類型為剛剛定義的TreeSelect的列表。
(3)Impl實(shí)現(xiàn)層:
核心代碼:
@Overridepublic List<TreeSelect> selectMarkClassTreeList(Markclass markclass) {//傳入用戶操作的分類,獲取篩選后的書(shū)簽分類列表List<Markclass> markClasses = this.selectMarkclassList(markclass);//selectMarkclassList()為查找分類列表用的方法,返回值為L(zhǎng)ist<Markclass>類型return buildMarkClassTreeSelect(markClasses);//構(gòu)建樹(shù)}@Overridepublic List<TreeSelect> buildMarkClassTreeSelect(List<Markclass> classes) {List<Markclass> markClasses = buildMarkClassTree(classes);return markClasses.stream().map(TreeSelect::new).collect(Collectors.toList());}/*** 構(gòu)建前端所需要樹(shù)結(jié)構(gòu)*/@Overridepublic List<Markclass> buildMarkClassTree(List<Markclass> markclassList) {List<Markclass> returnList=new ArrayList<Markclass>();存儲(chǔ)當(dāng)前分類出現(xiàn)的節(jié)點(diǎn)List<Long> tempList = markclassList.stream().map(Markclass::getMarkClassId).collect(Collectors.toList());for (Markclass markclass : markclassList) {if(!tempList.contains(markclass.getParentid())){// 如果是頂級(jí)節(jié)點(diǎn), 遍歷該父節(jié)點(diǎn)的所有子節(jié)點(diǎn)recursionFn(markclassList, markclass);returnList.add(markclass);//不存在的加入進(jìn)去,從而完成一整個(gè)樹(shù)的遍歷}}if (returnList.isEmpty()) {returnList = markclassList;}return returnList;}private void recursionFn(List<Markclass> list, Markclass t) {// 遞歸。得到子節(jié)點(diǎn)列表List<Markclass> childList = getChildList(list, t);t.setMarkChildren(childList);for (Markclass tChild : childList) {if (hasChild(list, tChild)) {recursionFn(list, tChild);}}}/*** 得到子節(jié)點(diǎn)列表*/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層代碼:
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;/*** 書(shū)簽分類Service業(yè)務(wù)層處理* * @author blhq* @date 2024-06-19*/
@Service
public class MarkclassServiceImpl implements IMarkclassService
{@Autowiredprivate MarkclassMapper markclassMapper;/*** 查詢書(shū)簽分類* * @param markClassId 書(shū)簽分類主鍵* @return 書(shū)簽分類*/@Overridepublic Markclass selectMarkclassByMarkClassId(Long markClassId){return markclassMapper.selectMarkclassByMarkClassId(markClassId);}/*** 查詢書(shū)簽分類列表* * @param markclass 書(shū)簽分類* @return 書(shū)簽分類*/@Overridepublic List<Markclass> selectMarkclassList(Markclass markclass){return markclassMapper.selectMarkclassList(markclass);}/*** 新增書(shū)簽分類* * @param markclass 書(shū)簽分類* @return 結(jié)果*/@Overridepublic 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);}}/*** 修改書(shū)簽分類* * @param markclass 書(shū)簽分類* @return 結(jié)果*/@Overridepublic int updateMarkclass(Markclass markclass){return markclassMapper.updateMarkclass(markclass);}/*** 批量刪除書(shū)簽分類* * @param markClassIds 需要?jiǎng)h除的書(shū)簽分類主鍵* @return 結(jié)果*/@Overridepublic int deleteMarkclassByMarkClassIds(Long[] markClassIds){return markclassMapper.deleteMarkclassByMarkClassIds(markClassIds);}/*** 刪除書(shū)簽分類信息* * @param markClassId 書(shū)簽分類主鍵* @return 結(jié)果*/@Overridepublic int deleteMarkclassByMarkClassId(Long markClassId){return markclassMapper.deleteMarkclassByMarkClassId(markClassId);}@Overridepublic List<TreeSelect> selectMarkClassTreeList(Markclass markclass) {//傳入用戶操作的分類,獲取篩選后的書(shū)簽分類列表List<Markclass> markClasses = this.selectMarkclassList(markclass);//selectMarkclassList()為查找分類列表用的方法,返回值為L(zhǎng)ist<Markclass>類型return buildMarkClassTreeSelect(markClasses);//構(gòu)建樹(shù)}@Overridepublic List<TreeSelect> buildMarkClassTreeSelect(List<Markclass> classes) {List<Markclass> markClasses = buildMarkClassTree(classes);return markClasses.stream().map(TreeSelect::new).collect(Collectors.toList());}/*** 構(gòu)建前端所需要樹(shù)結(jié)構(gòu)*/@Overridepublic List<Markclass> buildMarkClassTree(List<Markclass> markclassList) {List<Markclass> returnList=new ArrayList<Markclass>();存儲(chǔ)當(dāng)前分類出現(xiàn)的節(jié)點(diǎn)List<Long> tempList = markclassList.stream().map(Markclass::getMarkClassId).collect(Collectors.toList());for (Markclass markclass : markclassList) {if(!tempList.contains(markclass.getParentid())){// 如果是頂級(jí)節(jié)點(diǎn), 遍歷該父節(jié)點(diǎn)的所有子節(jié)點(diǎn)recursionFn(markclassList, markclass);returnList.add(markclass);//不存在的加入進(jìn)去,從而完成一整個(gè)樹(shù)的遍歷}}if (returnList.isEmpty()) {returnList = markclassList;}return returnList;}private void recursionFn(List<Markclass> list, Markclass t) {// 遞歸。得到子節(jié)點(diǎn)列表List<Markclass> childList = getChildList(list, t);t.setMarkChildren(childList);for (Markclass tChild : childList) {if (hasChild(list, tChild)) {recursionFn(list, tChild);}}}/*** 得到子節(jié)點(diǎn)列表*/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層:
這時(shí)候,我們直接添加即可:推薦把它放到對(duì)應(yīng)需要分類顯示的文件中,比如我的是在書(shū)簽的controller中。
@GetMapping("/deptTree")public AjaxResult deptTree(Markclass markclass){List<TreeSelect> list = markclassService.selectMarkClassTreeList(markclass);return success(list);}
(5)xml文件:
修改查找邏輯的語(yǔ)句:將分類的查詢改為,從markclas表的ancestors查:
<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 bleft join markclass c on b.markClassId = c.markClassIdwhere 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 >= #{createTime}</if><if test="editTime != null "> and b.editTime <= #{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、前端應(yīng)用:
整體思路就是引用它,把它調(diào)過(guò)來(lái)使用就行。下面按我的思路來(lái)演示:
(1)引入api:
// 查詢書(shū)簽分類列表--tree
export function markClassTreeSelect(query) {return request({url: '/markclass/markclass/deptTree',method: 'get',params: query})
}
(2)頁(yè)面導(dǎo)入
注意將v-model中的值,改為你自己的。
<template><el-row :gutter="20"><el-col :span="4" :xs="24"><div class="head-container"><el-inputv-model="markName"placeholder="請(qǐng)輸入書(shū)簽分類名稱"clearablesize="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-allhighlight-current@node-click="handleNodeClick"/><!-- </div>--></el-col><el-col :span="20" :xs="24">…………</el-col></el-row>…………//你的其它頁(yè)面布局代碼,上文gutter表示左側(cè)分類欄占比大小。</template>
<script></script>內(nèi)需要添加的:
data() {return {//data內(nèi)需要添加的數(shù)據(jù):// 書(shū)簽分類名稱markName:undefined,// 書(shū)簽分類選項(xiàng)markOptions: undefined,//設(shè)置分類數(shù)據(jù)樣式:defaultProps: {children: "children",//children:就是告訴el-tree,//它需要的children,在你這里的數(shù)據(jù),叫啥名,比如我的為children//就是TreeSelect類中定義的childrenlabel: "label"},
},
},watch: {//在watch內(nèi),添加如下內(nèi)容,沒(méi)watch就自己加,在data(){},后面。// 根據(jù)名稱篩選樹(shù)markName(val) {this.$refs.tree.filter(val);}},created() {//調(diào)用方法this.getMarkTree();},methods: {// 獲取書(shū)簽分類樹(shù)getMarkTree() {markClassTreeSelect().then(response => {this.markOptions = response.data;console.log(response.data);// console.log(response);});},// 篩選節(jié)點(diǎn)filterNode(value, data) {if (!value) return true;return data.label.indexOf(value) !== -1;},// 節(jié)點(diǎn)單擊事件handleNodeClick(data) {this.queryParams.markClassId = data.id;this.handleQuery();},},
5、成果展示:
點(diǎn)擊游戲類后:
三、我遇到的問(wèn)題
(1):無(wú)法自動(dòng)裝配。找不到'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'.
方案:如果提示這個(gè),請(qǐng)把我們剛才定義的TreeSelect類,重構(gòu)一下,改個(gè)名就行。
更詳細(xì)說(shuō)明請(qǐng)看下文:
分類樹(shù)實(shí)現(xiàn)時(shí)遇見(jiàn)的bug:如XXXController $$EnhancerBySpringCGLIB$$c8ca0a15 cannot be cast to XXX……-CSDN博客文章瀏覽閱讀2次。實(shí)現(xiàn)分類樹(shù)實(shí)現(xiàn)時(shí)遇見(jiàn)的bughttps://blog.csdn.net/qq_64595427/article/details/139843770?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22139843770%22%2C%22source%22%3A%22qq_64595427%22%7D
如有問(wèn)題,歡迎留言討論哦~