java实现分类下拉树,点击时对应搜索---后端逻辑

一直想做分类下拉,然后选择后搜索的页面,正好做项目有了明确的需求,查找后发现el-tree的构件可满足需求,数据要求为:{ id:1,  label:name,  childer:[……]  }形式的,于是乎,开搞!

一、效果预览

点击后

二、实现步骤

1、数据库设计层面:

在设计数据库的分类表时,需要设置为层级结构的样式,指明父分类id和祖分类id,如此才可满足分类需求。

具体如下,比如我的书签分类表:markClass(以下只写出主要三列)

属性数据类型字段说明举例
markClassIdint书签分类的id1001
parentidint该分类的上级分类id(父id)1000
ancestorsstring该分类所有上属分类id(祖id)0,10,100,1000

2、后端基础的构建

依据表格完成对后端基础增删改的设置,可以用Ruoyi一键生成。以下主要说明插入时,ancestors的设置:

插入时需要依据父分类id来完成对ancestors的设置。(注意设置ancestors的getter、setter方法)

private MarkclassMapper markclassMapper;
//这个是在你写的Impl层中的那个类里,你的selectById方法在哪里就引用哪个public int insertMarkclass(Markclass markclass){Markclass mar=markclassMapper.selectMarkclassByMarkClassId(markclass.getParentid());//依据传入的markclass对象的父分类id查找具体的分类markclass.setAncestors(mar.getAncestors()+","+markclass.getParentid());//调用set方法。按照字符串拼接的方法,获取父分类所包含的祖分类id,并加上父分类id。return markclassMapper.insertMarkclass(markclass);}

3、主代码——构建树相关

(1)创建适配el-tree数据类型的类

el-tree的构件数据要求为:{ id:1,  label:name,  childer:[……]  }形式,于是可定义一个“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;
//引入需要的你定义到的实体类/*** Treeselect树结构实体类* 数据类型依据el-tree数据需求进行定义,即为:id、label、children,* @author blhq*/
public class TreeSelect implements Serializable
{private static final long serialVersionUID = 1L;/** 节点ID */private Long id;/** 节点名称 */private String label;/** 子节点 *///    @JsonInclude(JsonInclude.Include.NON_EMPTY)private List<TreeSelect> children;public TreeSelect(Class aclass) {this.id = aclass.getClassid();this.label = aclass.getClassname();// 检查此处是否正确设置了 class_children 属性this.children = aclass.getClass_children().stream().map(TreeSelect::new).collect(Collectors.toList());}//写了多个TreeSelect构造类,以满足后续其它类型对象的调用,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类

private List<Markclass> markChildren=new ArrayList<Markclass>();
//必须实例化,不然会报错java.lang.NullPointerException: null
//同时设置getter、setter方法
public List<Markclass> getMarkChildren() {return markChildren;
}public void setMarkChildren(List<Markclass> markChildren) {this.markChildren = markChildren;}

(2)service层:

在你的service类中创建方法:(如果没有service层,则无视,直接看实现层操作即可)

注意:部分返回值类型为刚刚定义的TreeSelect的列表。

    public List<TreeSelect> selectMarkClassTreeList(Markclass markclass);//接收用户传入的分类,即为当前操作的分类public List<Markclass> buildMarkClassTree(List<Markclass> markclassList);//构建树的核心方法public List<TreeSelect> buildMarkClassTreeSelect(List<Markclass> classes);//注意返回值类型为刚刚定义的TreeSelect的列表。

(3)Impl实现层:

核心代码:
    @Overridepublic List<TreeSelect> selectMarkClassTreeList(Markclass markclass) {//传入用户操作的分类,获取筛选后的书签分类列表List<Markclass> markClasses = this.selectMarkclassList(markclass);//selectMarkclassList()为查找分类列表用的方法,返回值为List<Markclass>类型return buildMarkClassTreeSelect(markClasses);//构建树}@Overridepublic List<TreeSelect> buildMarkClassTreeSelect(List<Markclass> classes) {List<Markclass> markClasses = buildMarkClassTree(classes);return markClasses.stream().map(TreeSelect::new).collect(Collectors.toList());}/*** 构建前端所需要树结构*/@Overridepublic 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层代码:
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业务层处理* * @author blhq* &#064;date  2024-06-19*/
@Service
public class MarkclassServiceImpl implements IMarkclassService 
{@Autowiredprivate MarkclassMapper markclassMapper;/*** 查询书签分类* * @param markClassId 书签分类主键* @return 书签分类*/@Overridepublic Markclass selectMarkclassByMarkClassId(Long markClassId){return markclassMapper.selectMarkclassByMarkClassId(markClassId);}/*** 查询书签分类列表* * @param markclass 书签分类* @return 书签分类*/@Overridepublic List<Markclass> selectMarkclassList(Markclass markclass){return markclassMapper.selectMarkclassList(markclass);}/*** 新增书签分类* * @param markclass 书签分类* @return 结果*/@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);}}/*** 修改书签分类* * @param markclass 书签分类* @return 结果*/@Overridepublic int updateMarkclass(Markclass markclass){return markclassMapper.updateMarkclass(markclass);}/*** 批量删除书签分类* * @param markClassIds 需要删除的书签分类主键* @return 结果*/@Overridepublic int deleteMarkclassByMarkClassIds(Long[] markClassIds){return markclassMapper.deleteMarkclassByMarkClassIds(markClassIds);}/*** 删除书签分类信息* * @param markClassId 书签分类主键* @return 结果*/@Overridepublic int deleteMarkclassByMarkClassId(Long markClassId){return markclassMapper.deleteMarkclassByMarkClassId(markClassId);}@Overridepublic List<TreeSelect> selectMarkClassTreeList(Markclass markclass) {//传入用户操作的分类,获取筛选后的书签分类列表List<Markclass> markClasses = this.selectMarkclassList(markclass);//selectMarkclassList()为查找分类列表用的方法,返回值为List<Markclass>类型return buildMarkClassTreeSelect(markClasses);//构建树}@Overridepublic List<TreeSelect> buildMarkClassTreeSelect(List<Markclass> classes) {List<Markclass> markClasses = buildMarkClassTree(classes);return markClasses.stream().map(TreeSelect::new).collect(Collectors.toList());}/*** 构建前端所需要树结构*/@Overridepublic 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中。

@GetMapping("/deptTree")public AjaxResult deptTree(Markclass markclass){List<TreeSelect> list = markclassService.selectMarkClassTreeList(markclass);return success(list);}

(5)xml文件:

修改查找逻辑的语句:将分类的查询改为,从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 &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:

// 查询书签分类列表--tree
export function markClassTreeSelect(query) {return request({url: '/markclass/markclass/deptTree',method: 'get',params: query})
}

(2)页面导入

注意将v-model中的值,改为你自己的。

<template><el-row :gutter="20"><el-col :span="4" :xs="24"><div class="head-container"><el-inputv-model="markName"placeholder="请输入书签分类名称"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>…………//你的其它页面布局代码,上文gutter表示左侧分类栏占比大小。</template>

<script></script>内需要添加的:

   data() {return {//data内需要添加的数据:// 书签分类名称markName:undefined,// 书签分类选项markOptions: undefined,//设置分类数据样式:defaultProps: {children: "children",//children:就是告诉el-tree,//它需要的children,在你这里的数据,叫啥名,比如我的为children//就是TreeSelect类中定义的childrenlabel: "label"},
},
},watch: {//在watch内,添加如下内容,没watch就自己加,在data(){},后面。// 根据名称筛选树markName(val) {this.$refs.tree.filter(val);}},created() {//调用方法this.getMarkTree();},methods: {// 获取书签分类树getMarkTree() {markClassTreeSelect().then(response => {this.markOptions = response.data;console.log(response.data);// console.log(response);});},// 筛选节点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类,重构一下,改个名就行。

更详细说明请看下文:

分类树实现时遇见的bug:如XXXController $$EnhancerBySpringCGLIB$$c8ca0a15 cannot be cast to XXX……-CSDN博客文章浏览阅读2次。实现分类树实现时遇见的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

如有问题,欢迎留言讨论哦~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/31119.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

算法训练营day15--110.平衡二叉树+ 257. 二叉树的所有路径+ 404.左叶子之和+222.完全二叉树的节点个数

一、110.平衡二叉树 题目链接&#xff1a;https://leetcode.cn/problems/balanced-binary-tree/ 文章讲解&#xff1a;https://programmercarl.com/0110.%E5%B9%B3%E8%A1%A1%E4%BA%8C%E5%8F%89%E6%A0%91.html 视频讲解&#xff1a;https://www.bilibili.com/video/BV1Ug411S7m…

体育时间:“中国第一”的出海代表们,一致瞄准了这一赛道?

2024年无疑又是一个体育赛事超级大年。 从1月的亚洲杯&#xff0c;2月的世乒团体锦标赛、第14届冬运会、到6月欧洲杯与美洲杯隔空对决&#xff0c;巴黎奥运会也将在7月盛大开赛&#xff0c;随后则还有8月的巴黎残奥会&#xff0c;对于期待万分的体育粉丝们&#xff0c;这将是极…

计算机编码以及URL转码

目录 一、计算机编码 1.ASCII编码 2. GB2312编码 3.GBK编码 4.UTF-8编码 二、URL转码 1.encodeURI和decodeURI 2.encodeURIComponent 和 decodeURIComponent 三、Base64 一、计算机编码 在计算机中&#xff0c;所有的数据在存储和运算时都要使用二进制数表示&#xf…

Spring Boot程序打包docker镜像

1.将springboot程序使用maven package打包出jar。 2.创建dockerfile。 FROM openjdk:8 VOLUME /tmp EXPOSE 8601 #ADD 后面的参数是项目名字 / 后面的参数是自定义的别名 ADD webflux-hello-0.0.1-SNAPSHOT.jar /webflux-hello.jar #这里的最后一个变量需要和前面起的别名相同…

Python 爬取淘宝指定搜索商品评论 标题 销量 计算sign

只需要替换原来的Cookie和token即可使用&#xff0c;自动计算对应链接地址的sign直接使用即可。需要注意是一个账号爬取过多会有验证码 import json import hashlib import random import timeimport pandas as pd import requestsresults []def fetch_review_list(datas, md…

vuejs3+elementPlus后台管理系统,左侧菜单栏制作,跳转、默认激活菜单

默认激活菜单,效果&#xff1a; 默认激活菜单&#xff0c;效果1&#xff1a; 默认激活菜单&#xff0c;效果2&#xff1a; 跳转链接效果&#xff1a; 制作&#xff1a; <script setup> import {useUserStore} from "/stores/userStore.js"; import {ref} fr…

适配器模式(大话设计模式)C/C++版本

适配器模式 C #include <iostream> using namespace std; // 球员 class Player { protected:string name;public:Player(string name) : name(name) {}virtual void Attack() 0;virtual void Defense() 0;virtual ~Player() {} }; // 前锋 class Forwards : public P…

20240613解决飞凌的OK3588-C的核心板的USB3.0接口不读U盘的问题

20240613解决飞凌的OK3588-C的核心板的USB3.0接口不读U盘的问题 2024/6/13 15:21 缘起&#xff0c;由于USB3.0的CC芯片在飞凌的OK3588-C的开发板的底板上&#xff0c;一切正常。 如果你单独使用核心板&#xff0c;很容易出现这个问题【省成本没有使用CC芯片】&#xff1a;不读U…

300PLC连接Modbus转Profibus网关与阀岛modbusRTU通讯

一、概况 300PLC作为常见的控制器设备&#xff0c;在与阀岛Modbus RTU通讯时&#xff0c;通常需要借助Modbus转Profibus网关&#xff08;XD-MDPB100&#xff09;来实现连接和数据交换。PLC通过Modbus转Profibus网关&#xff08;XD-MDPB100&#xff09;与阀岛Modbus RTU通讯是比…

labelme使用笔记:目标检测数据集标注和语义分割数据集批量生成

AI应用开发相关目录 本专栏包括AI应用开发相关内容分享&#xff0c;包括不限于AI算法部署实施细节、AI应用后端分析服务相关概念及开发技巧、AI应用后端应用服务相关概念及开发技巧、AI应用前端实现路径及开发技巧 适用于具备一定算法及Python使用基础的人群 AI应用开发流程概…

[AIGC] 使用Google的Guava库中的Lists工具类:常见用法详解

在Java程序设计中&#xff0c;集合是我们最常用的数据结构之一。为了方便我们操作集合&#xff0c;Google的Guava库提供了一个名为Lists的工具类&#xff0c;它封装了许多用于操作List对象的实用方法。在本文中&#xff0c;我们将详细介绍其常见的用法&#xff0c;以帮助您更好…

PyQt5 生成py文件不能运行;pushButton点击事件;QTextEdit 获取输入框内容

目录 cant open file c.pyuic: c.pyuic $FileName$ -o $FileNameWithoutExtension$.p PyQt5 生成py文件不能运行 pushButton点击事件 QTextEdit 获取输入框内容 整体运行代码: Creating a Qt Widget Based Application | Qt Creator Manual cant open file c.pyuic: c.…

NAT Easyip实验

我们这篇博客将重点讲述easy ip的配置&#xff1a; 以下面的一个简单的实验拓扑图为例&#xff1a; 本实验使用的网络地址&#xff1a; 1. 我们先来完成基础配置&#xff1a; 1.1AR1的基础配置&#xff1a; 1.2AR2上的基础配置 1.3完成AR1和AR2的基础配置后&#xff0c;我们…

rust数据类型

目录 一&#xff0c;基本类型 1&#xff0c;基本类型 &#xff08;1&#xff09;整数类型 &#xff08;2&#xff09;浮点数 &#xff08;3&#xff09;bool类型 &#xff08;4&#xff09;char类型 2&#xff0c;基本类型的代数结构 二&#xff0c;复合类型 1&#x…

这些帮助你成长的IOS应用,建议收藏

TrackIt TrackIt是一款功能丰富的任务清单、日程管理和习惯打卡应用&#xff0c;旨在帮助用户提高效率和专注力。通过这些功能&#xff0c;用户可以更好地规划时间和任务&#xff0c;从而实现个人目标和养成良好习惯。 在目标设定方面&#xff0c;SMART原则是一个常用的方法&a…

【决战欧洲之巅】丹麦 vs 英格兰战术分析和球员状态以及走地数据分析

丹麦 vs 英格兰战术分析和球员状态 阵型与战术 丹麦: 阵型:丹麦通常采用4-3-3阵型&#xff0c;以因防守为基础&#xff0c;同时通过快速反击等找机会。 ”战术: 防守:丹麦在防守时以紧密的四后卫体彩为核心&#xff0c;辅以中场球员的积极回防&#xff0c;形成稳因的防 线。…

解决HassOS无法获取ip地址问题

目录 问题描述解法 问题描述 在ESXi中安装完HassOS后&#xff0c;开机遇到一个无法获取ip地址的问题&#xff0c;如下图所示&#xff1a; 输入network info命令&#xff0c;显示ipv4已被禁用&#xff0c;如下图所示&#xff1a; 解法 在控制台ha >后输入下面命令 netw…

基于Java技术“漫画之家”系统

开头语&#xff1a;你好呀&#xff0c;我是计算机学姐码农小野&#xff01;如果有相关需求&#xff0c;可以私信联系我。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;Java技术&#xff0c;B/S结构&#xff0c;SpringBoot框架 工具&#xff1a;MyEc…

板凳--------第60章 SOCKET:服务器设计

60.1 迭代型和并发型服务器 1016 1.迭代型&#xff1a; 服务器每次只处理一个客户端&#xff0c;只有当完全处理完一个客户端的请求后才会去处理下一个客户端。只适用于快速处理客户端请求的场景&#xff0c;因为每个客户端都必须等待&#xff0c;直到前面所有的客户端都处理完…

一键批量复制至指定文件夹,轻松实现同名文件覆盖,数据管理更高效!

在数字化时代&#xff0c;文件的管理与复制是每位电脑用户都不可避免的日常工作。你是否曾因为大量文件的复制与更新而焦头烂额&#xff1f;是否曾因为同名文件的冲突而不知所措&#xff1f;别担心&#xff0c;现在&#xff0c;我们为您带来高效文件复制管理的秘诀&#xff0c;…