Tree结构目录场景

概述

技术栈:

  • springBoot
  • Mybatis-Plus
  • Vue
  • stream

内容:

做一个目录场景

步骤

1. 新建项目

在这里插入图片描述

在这里插入图片描述

2. 整合SSM

  • 在pom文件中整合ssm相关的依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>

<!--mysql数据库驱动-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.26</version>
</dependency>

<!--mybatis-plus依赖-->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.4.2</version>
</dependency>

<!--web依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!--lombok依赖-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
    <version>1.18.20</version>
</dependency>

<!--测试test依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.12.0</version>
</dependency>

3. 在application.yaml中配置端口和数据库连接

logging.level:设置日志级别,后面跟生效的区域,比如root表示整个项目,也可以设置为某个包下,也可以具体到某个类名(日志级别的值不区分大小写)

server:
  port: 9100 # 端口号

spring:
  freemarker: # freemarker模板引入
    suffix: .html # 模板后缀
    cache: false # 关闭缓存
  jackson: # 日期格式化
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8 # 时区
    locale: zh_CN
    generator: # 数据处理(防止精度丢失)
      write-numbers-as-strings: true
      write-bigdecimal-as-plain: true
    serialization:
      write-dates-as-timestamps: false
  datasource:  #数据源
    type: com.zaxxer.hikari.HikariDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/yue-user-cours-db?serverTimezone=GMT%2b8&useUnicode=true&characterEncoding=utf-8&useSSL=false #数据库
    username: root
    password:
    hikari:
      connection-timeout: 60000
      validation-timeout: 3000
      idle-timeout: 60000
      login-timeout: 5
      max-lifetime: 60000
      maximum-pool-size: 30
      minimum-idle: 10
      read-only: false

# mybatis-plus配置
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  mapper-locations: classpath*:/mapper/*.xml


logging:
  level:
    root: info
    # logging.level:设置日志级别,后面跟生效的区域,
    #比如root表示整个项目,也可以设置为某个包下,也可以具体到某个类名(日志级别的值不区分大小写)

4. 编写Category相关beanmapperservicecontroller

  • @TableField: 注解用于标识非主键的字段。将数据库列与 JavaBean 中的属性进行映射

数据库表

在这里插入图片描述

pojo

  • @TableName(表名):对数据库中对应表进行映射,操作数据
  • @TableId: 将某个成员变量指定为数据表主键
  • @TableField:标识非主键的字段。将数据库列与 JavaBean 中的属性进行映射
    • @TableField(exist = false):默认为true,而修改为false的话 ,代表当前这个字段不在表中
package com.yue.pojo;

import com.baomidou.mybatisplus.annotation.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;
import java.util.List;

/**
 * @Author: 夜雨
 * @Date: 2021-12-21-9:55
 * @Description:课程分类
 * @Version 1.0
 */
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
@Builder
@TableName("kss_course_category")
public class CourseCategory implements java.io.Serializable{

    @TableId(type = IdType.AUTO)
    private Integer id;
    // 上课标题
    private String title;
    // 分类描述
    private String descrciption;
    // 是否更新
    private Integer mark;
    // 发布状态1发布 0未发布
    private Integer status;
    // 类型 1文件夹 2文件
    private Integer type;
    // 父id =0 根集 其它全部都是子集(节点pid对映哪个id就说明其是哪个节点的子节点)
    private Integer pid;
    // 是否展开和收起
    private Boolean isexpand;
    // 子节点集合:这里一定加exist=false ,代表当前这个字段不在表中,不加就会报错
    @TableField(exist = false)
    private List<CourseCategory> chilrenList;
    // 创建时间
    @TableField(fill = FieldFill.INSERT)//在新增的时候填充
    private Date createTime;
    // 更新时间
    @TableField(fill = FieldFill.INSERT_UPDATE)//在新增的时候填充
    private Date updateTime;
}


mapper

  • BaseMapper 接口:Mapper 继承该接口后,无需编写 mapper.xml 文件,即可获得CRUD功能,其 全限定名称为com.baomidou.mybatisplus.core.mapper.BaseMapper<T>,该接口提供了插入、修改、删除和查询功能。我们可以在测试类中直接使用了insert()selectById()updateById()deleteById()方法

BaseMapper部分源码截图:
在这里插入图片描述

package com.yue.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yue.pojo.CourseCategory;

/**
 * @Author: 夜雨
 * @Date: 2021-12-21-10:18
 * @Description:
 * @Version 1.0
 */
public interface CourseCategoryMapper extends BaseMapper<CourseCategory> {
}

service & serviceImpl

  • IService 接口:Mapper 继承该接口后,即可获得CRUD功能,其 全限定名称为com.baomidou.mybatisplus.extension.service<T>.使用该接口时,其实现类也必须继承ServiceImpl

IService接口部分源码截图:
在这里插入图片描述
ServiceImpl类部分源码截图
在这里插入图片描述

接口:

package com.yue.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.yue.pojo.CourseCategory;

/**
 * @Author: 夜雨
 * @Date: 2021-12-21-10:27
 * @Description:
 * @Version 1.0
 */
public interface CourseCategoryService extends IService<CourseCategory> {
}

实现类:

package com.yue.service;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yue.mapper.CourseCategoryMapper;
import com.yue.pojo.CourseCategory;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Service;

/**
 * @Author: 夜雨
 * @Date: 2021-12-21-10:34
 * @Description:
 * @Version 1.0
 */
@Service
@Log4j2
public class CourseCategoryServiceImpl extends ServiceImpl<CourseCategoryMapper, CourseCategory> implements CourseCategoryService {
}


controller

package com.yue.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Author: 夜雨
 * @Date: 2021-12-21-11:15
 * @Description:
 * @Version 1.0
 */
@RestController
public class CourseCategoryController {
    @GetMapping("/api/category/tree")
    public String tree(){
        return "tree";
    }
}

测试整合SSM:@MapperScan("com.yue.mapper")

package com.yue;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.yue.mapper")
public class YeyuStreamVueTreeApplication {

    public static void main(String[] args) {
        SpringApplication.run(YeyuStreamVueTreeApplication.class, args);
    }

}

进行查询测试:

package com.yue.controller;

import com.yue.pojo.CourseCategory;
import com.yue.service.CourseCategoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * @Author: 夜雨
 * @Date: 2021-12-21-11:15
 * @Description:
 * @Version 1.0
 */
@RestController
public class CourseCategoryController {

    @Autowired
    private  CourseCategoryService courseCategoryService;

    @GetMapping("/api/category/tree")
    public List<CourseCategory> tree(){
        return courseCategoryService.list();
    }
}

http://localhost:9100/api/category/tree
在这里插入图片描述

5. 使用stream流完成分类的递归的相关操作

(1)定义接口

public interface CourseCategoryService extends IService<CourseCategory> {

    /*
    * 返回分类tree
    * */
    List<CourseCategory> findCategoires();
}

(2)递归实现:

  • sorted((a,b) -> a.getSorted()-b.getSorted()):升序
  • sorted((a,b) -> b.getSorted()-a.getSorted()):降序
@Service
@Log4j2
public class CourseCategoryServiceImpl extends ServiceImpl<CourseCategoryMapper, CourseCategory> implements CourseCategoryService {
    @Override
    public List<CourseCategory> findCategoires() {
        //1.查询表中所有数据
        List<CourseCategory> allList = this.list();
        //2.查询根节点
        List<CourseCategory> rootList = allList.stream().filter(category -> category.getPid().equals(0)).
                sorted((a,b) -> a.getSorted()-b.getSorted()).
                collect(Collectors.toList());
        //3.查询子节点
        List<CourseCategory> subList = allList.stream().filter(category -> !category.getPid().equals(0)).collect(Collectors.toList());
        //4.循环根节点找到各自对应子节点
        rootList.forEach(root -> rootSub(root,subList));
        return rootList;
    }

    /*
     * 根节点寻找对应子节点
     * */
    private void rootSub(CourseCategory root,List<CourseCategory> subList){
        //根节点id和子节点pid相等
        List<CourseCategory> childList = subList.stream().filter(sub -> sub.getPid().equals(root.getId())).
                sorted((a,b) -> a.getSorted()-b.getSorted()).
                collect(Collectors.toList());
        //如果某个根节点下有子节点,就将子节点放入对应根节点下
        if (!CollectionUtils.isEmpty(childList)) {
            root.setChildrenList(childList);
            //如果子节点不为空,则继续递归,去找对应子节点
            childList.stream().forEach(child -> rootSub(child,subList));
        }else {
            root.setChildrenList(new ArrayList<>());
        }
    }
}

将控制层修改后进去看看效果
在这里插入图片描述

6. 使用axios完成异步接口的调用

概述

两种方式:

  • 要么使用现成的tree组件 v-tree/ztree/other tree

  • 自己开发

    步骤:

    • 查询接口返回数据
    • 使用Axios查询数据接口返回给页面
    • 使用vue来进行tree实现

此处注意一点:

temeleaf下的html文件只可通过路由访问,不能直接访问

测试引入axios.js是否成功:

  • <style> [v-cloak]{display: none} </style>作用:防止抖动渲染(就是刚进去会有一刹那的{{title}}显示)
<!doctype html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport"
              content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>树形菜单</title>
        <style>
            /*防止抖动渲染*/
            [v-cloak]{display: none}
        </style>
    </head>
    <body>
    <div id="app" v-cloak>
        <h1>{{title}}</h1>
    </div>


    <script src="/js/vue.min.js"></script>
    <script src="/js/axios.min.js"></script>
    <script>

        var vue = new Vue({
            el:"#app", //el 配置项指实例负责管理的区域; #app 指 id="app" 的dom标签里的所有内容(只对其有效)
            data:{
                title:"基于vue的Tree结构目录场景",
            },
            created:function(){
                // 1: 页面加载初始化执行获取数组
            },
            methods: {
                // 加载tree的数据信息
            }
            })


    </script>

    </body>
</html>

注意:此处可能静态资源没编译进去会报js404,所以这个时候可以手动复制进编译文件中:
在这里插入图片描述

测试结果:引入成功

在这里插入图片描述

调用代码

<!doctype html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport"
              content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>树形菜单</title>
        <style>
            [v-cloak]{display: none}
        </style>
    </head>
    <body>
    <div id="app" v-cloak>
        <h1>{{title}}</h1>
        <div>{{dataList}}</div>
    </div>


    <script src="/js/vue.min.js"></script>
    <script src="/js/axios.min.js"></script>
    <script>
        var vue = new Vue({
            el: "#app",
            data: {
                title: "基于vue的Tree结构目录场景",
                dataList:[]
            },
            created: function () {
                // 1: 页面加载初始化执行获取数组
                this.loadTree();
            },
            methods: {
                // 加载tree的数据信息
                loadTree: function () {
                    axios.get("/api/category/tree").then(res => {
                        this.dataList=res.data;
                    })
                }
            }
        })
    </script>

    </body>
</html>


测试调用结果:调用成功

在这里插入图片描述

7. 使用vue的组件和递归组件完成tree结构

基础框架实现

<!doctype html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport"
              content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>树形菜单</title>
        <style>
            [v-cloak]{display: none}
        </style>
    </head>
    <body>
    <div id="app" v-cloak>
        <h1>{{title}}</h1>
        <yue-tree v-bind:date="dateList"></yue-tree>
    </div>


    <script src="/js/vue.min.js"></script>
    <script src="/js/axios.min.js"></script>
    <script>
        /*自定义组件*/
        Vue.component("yue-tree",{
            /*自定义属性*/
            props:{
                date:Array
            },
            /*自定义模板*/
            template:"<ul>" +
                "<li v-for='(d,index) in date'>{{d.title}} <yue-tree :date='d.childrenList'></yue-tree></li>"+
                "</ul>",
        })

        var vue = new Vue({
            el: "#app",
            data: {
                title: "基于vue的Tree结构目录场景",
                dateList:[]
            },
            created: function () {
                // 1: 页面加载初始化执行获取数组
                this.loadTree();
            },
            methods: {
                // 加载tree的数据信息a
                loadTree: function () {
                    axios.get("/api/category/tree").then(res => {
                        this.dateList=res.data;
                    })
                }
            }
        })
    </script>

    </body>
</html>

在这里插入图片描述

添加图标

 /*自定义模板*/
            template:"<ul>" +
                    "<li v-for='(d,index) in date'>"+
                        "<span>" +
                            "<span v-if='d.type==1'>目录图标 </span> " +
                            "<span v-if='d.type==2'>文件图标 </span> " +
                                "{{d.title}}" +
                        "</span>"+
                            "<yue-tree v-show='d.isexpand' :date='d.childrenList'></yue-tree>"+
                    "</li>"+
                "</ul>",

在这里插入图片描述

添加单击事件

<!doctype html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport"
              content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>树形菜单</title>
        <style>
            [v-cloak] {
                display: none
            }
        </style>
    </head>
    <body>
        <div id="app" v-cloak>
            <h1>{{title}}</h1>
            <yue-tree v-bind:date="dateList"></yue-tree>
        </div>


        <script src="/js/vue.min.js"></script>
        <script src="/js/axios.min.js"></script>
        <script>
            /*自定义组件*/
            Vue.component("yue-tree", {
                /*自定义属性*/
                props: {
                    date: Array
                },
                /*自定义模板*/
                template: "<ul>" +
                    "<li v-for='(d,index) in date' @click.stop ='expend(d)'>" +
                    "<span>" +
                    "<span v-if='d.type==1'>目录图标 </span> " +
                    "<span v-if='d.type==2'>文件图标 </span> " +
                    "{{d.title}}" +
                    "</span>" +
                    "<yue-tree v-show='d.isexpand' :date='d.childrenList'></yue-tree>" +
                    "</li>" +
                    "</ul>",
                /*自定义事件*/
                methods: {
                    expend: function (obj) {
                        obj.isexpand = !obj.isexpand
                    }
                }
            })

            var vue = new Vue({
                el: "#app",
                data: {
                    title: "基于vue的Tree结构目录场景",
                    dateList: []
                },
                created: function () {
                    // 1: 页面加载初始化执行获取数组
                    this.loadTree();
                },
                methods: {
                    // 加载tree的数据信息a
                    loadTree: function () {
                        axios.get("/api/category/tree").then(res => {
                            this.dateList = res.data;
                        })
                    }
                }
            })
        </script>

    </body>
</html>

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

月色夜雨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值