JavaWeb与Vue3 Elment plus Tree树状结构以及MyBatis的树状结构分类


前言

MyBatis 是一个优秀的持久层框架,用于与数据库进行交互。它可以帮助你轻松地执行数据库操作,包括查询、插入、更新和删除。
Vue Element Plus Tree 树形控件 是一个功能强大且易于使用的前端组件,用于展示具有层次结构的数据。你可以在其中显示树状数据,支持节点选择、自定义节点内容、懒加载、拖拽等功能。
Java Servlet 是用于处理 Web 请求和响应的 Java 技术。你可以使用它来创建后端 API,与前端进行数据交互。


提示:以下是本篇文章正文内容,下面案例可供参考

一、准备工作?

首先创建一个Vue3项目和一个后端Maven的webapp项目。
然后在vue中添加elment plus的依赖,并且在一个vue组件中加入Tree组件的基础模板

<template>
  <div class="custom-tree-container">
    <el-tree
      style="max-width: 600px"
      :data="dataSource"
      show-checkbox
      node-key="id"
      default-expand-all
      :expand-on-click-node="false"
    >
      <template #default="{ node, data }">
        <span class="custom-tree-node">
          <span>{{ node.label }}</span>
          <span>
            <a @click="append(data)"> Append </a>
            <a style="margin-left: 8px" @click="remove(node, data)"> Delete </a>
          </span>
        </span>
      </template>
    </el-tree>
  </div>
</template>

<script lang="ts" setup>
import { ref } from 'vue'
import type Node from 'element-plus/es/components/tree/src/model/node'

interface Tree {
  id: number
  label: string
  children?: Tree[]
}
let id = 1000

const append = (data: Tree) => {
  const newChild = { id: id++, label: 'testtest', children: [] }
  if (!data.children) {
    data.children = []
  }
  data.children.push(newChild)
  dataSource.value = [...dataSource.value]
}

const remove = (node: Node, data: Tree) => {
  const parent = node.parent
  const children: Tree[] = parent.data.children || parent.data
  const index = children.findIndex((d) => d.id === data.id)
  children.splice(index, 1)
  dataSource.value = [...dataSource.value]
}

const renderContent = (
  h,
  {
    node,
    data,
    store,
  }: {
    node: Node
    data: Tree
    store: Node['store']
  }
) => {
  return h(
    'span',
    {
      class: 'custom-tree-node',
    },
    h('span', null, node.label),
    h(
      'span',
      null,
      h(
        'a',
        {
          onClick: () => append(data),
        },
        'Append '
      ),
      h(
        'a',
        {
          style: 'margin-left: 8px',
          onClick: () => remove(node, data),
        },
        'Delete'
      )
    )
  )
}

const dataSource = ref<Tree[]>([
])
</script>

<style>
.custom-tree-node {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 14px;
  padding-right: 8px;
}
</style>

还要创建一个数据库表,我已经创建好了,可以直接使用

/*
 Navicat Premium Data Transfer

 Source Server         : RpWn
 Source Server Type    : MySQL
 Source Server Version : 50735 (5.7.35)
 Source Host           : localhost:3306
 Source Schema         : demo

 Target Server Type    : MySQL
 Target Server Version : 50735 (5.7.35)
 File Encoding         : 65001

 Date: 16/03/2024 15:44:31
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for category
-- ----------------------------
DROP TABLE IF EXISTS `category`;
CREATE TABLE `category`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `code` varchar(7) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `name` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `sorts` tinyint(4) NULL DEFAULT 0,
  `parentId` int(11) NULL DEFAULT 0,
  `isdel` bit(1) NULL DEFAULT b'0',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 35 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of category
-- ----------------------------
INSERT INTO `category` VALUES (1, '101', '家用电器', 1, 0, b'0');
INSERT INTO `category` VALUES (2, '102', '手机', 2, 0, b'0');
INSERT INTO `category` VALUES (3, '103', '电脑', 3, 0, b'0');
INSERT INTO `category` VALUES (4, '104', '服装', 4, 0, b'0');
INSERT INTO `category` VALUES (5, '10101', '电视', 1, 1, b'0');
INSERT INTO `category` VALUES (6, '10102', '空调', 2, 1, b'0');
INSERT INTO `category` VALUES (7, '10103', '冰箱', 3, 1, b'0');
INSERT INTO `category` VALUES (8, '10104', '洗衣机', 4, 1, b'0');
INSERT INTO `category` VALUES (9, '10201', '手机', 1, 2, b'0');
INSERT INTO `category` VALUES (10, '10202', '配件', 2, 2, b'0');
INSERT INTO `category` VALUES (11, '10203', '摄像', 3, 2, b'0');
INSERT INTO `category` VALUES (12, '10204', '音响', 4, 2, b'0');
INSERT INTO `category` VALUES (13, '10301', '电脑整机', 1, 3, b'0');
INSERT INTO `category` VALUES (14, '10302', '外设配件', 2, 3, b'0');
INSERT INTO `category` VALUES (15, '10401', '男装', 1, 4, b'0');
INSERT INTO `category` VALUES (16, '10402', '女装', 2, 4, b'0');
INSERT INTO `category` VALUES (17, '1010101', '游戏电视', 1, 5, b'0');
INSERT INTO `category` VALUES (18, '1010102', '艺术电视', 2, 5, b'0');
INSERT INTO `category` VALUES (19, '1010103', 'K歌电视', 3, 5, b'0');
INSERT INTO `category` VALUES (20, '1010201', '新风空调', 1, 6, b'0');
INSERT INTO `category` VALUES (21, '1010202', '空调挂机', 2, 6, b'0');
INSERT INTO `category` VALUES (22, '1010203', '空调柜机', 3, 6, b'0');
INSERT INTO `category` VALUES (23, '1010301', '多门', 1, 7, b'0');
INSERT INTO `category` VALUES (24, '1010302', '三门', 2, 7, b'0');
INSERT INTO `category` VALUES (25, '1020101', '游戏手机', 1, 9, b'0');
INSERT INTO `category` VALUES (26, '1020102', '5G手机', 2, 9, b'0');
INSERT INTO `category` VALUES (27, '1030101', '笔记本', 1, 13, b'0');
INSERT INTO `category` VALUES (28, '1030102', '游戏本', 2, 13, b'0');
INSERT INTO `category` VALUES (29, '1040101', '短袖', 1, 15, b'0');
INSERT INTO `category` VALUES (30, '1040102', '裤子', 2, 15, b'0');
INSERT INTO `category` VALUES (31, '1040201', '袜子', 1, 16, b'0');
INSERT INTO `category` VALUES (34, '1040202', '靴子', 1, 16, b'0');

SET FOREIGN_KEY_CHECKS = 1;

二、使用步骤

1.引入库

代码如下(示例):

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings('ignore')
import  ssl
ssl._create_default_https_context = ssl._create_unverified_context

目前页面就是下图这个样子
在这里插入图片描述

2.读入数据

接着我们在后端创建mapper.xml 和javaBean类以及mybatis.cofig.xml和实体类对性的BeanMapper,并在pom.xml引入servlet ,lombok ,mybatis的依赖坐标

<dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.15</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.30</version>
        </dependency>

在这里插入图片描述
在CategoryMapper接口中写入 查询的方法List<Category> queryAllCategory();
最重要的是在映射文件中书写高级映射

<resultMap id="categoryMap" type="category">
        <id property="id" column="id"/>
        <result property="code" column="code"/>
        <result property="name" column="name"/>
        <collection property="children" javaType="list" ofType="category">
            <id property="id" column="twoId"/>
            <result property="code" column="code"/>
            <result property="name" column="twoName"/>
            <collection property="children" javaType="list" ofType="category">
                <id property="id" column="threeId"/>
                <result property="code" column="code"/>
                <result property="name" column="threeName"/>
            </collection>
        </collection>
    </resultMap>

    <select id="queryAllCategory" resultMap="categoryMap" flushCache="true">
        SELECT c1.id,
               c1.code,
               c1.`name`,
               c2.id     twoId,
               c2.`name` twoName,
               c2.code   twoCode,
               c3.id     threeId,
               c3.`name` threeName,
               c3.code   threeeCode
        FROM category c1
                 LEFT JOIN category c2 ON c1.id = c2.parentId
            AND c2.isdel = 0
                 LEFT JOIN category c3 ON c2.id = c3.parentId
            AND c3.isdel = 0
        WHERE c1.parentId = 0
        ORDER BY c1.sorts
    </select>

在CategoryServlet中继承HttpServlet并重写doPost和doGet方法,因为我们是查询所有的分类所以使用doGet

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8");
        resp.setCharacterEncoding("UTF-8");
        SqlSession session = FactoryUtil.getFactory().openSession(true);
        CategoryMapper categoryMapper = session.getMapper(CategoryMapper.class);
        List<Category> list = categoryMapper.queryAllCategory();
        resp.getWriter().write("11", list, 0);
        session.close();
    }

最后我们就可以在前端发送请求并且接收相应:

<template>
    <div class="custom-tree-container">
        <el-tree style="max-width: 600px" :data="treeData" show-checkbox node-key="id" default-expand-all
            :expand-on-click-node="false" :render-content="renderContent" @node-click="handleNodeClick" />
        <el-divider />
    </div>
</template>

<script setup>
import axios from 'axios';
import { ref, onMounted, watch } from 'vue'
import { ElMessage } from 'element-plus';
import { useRouter } from 'vue-router';
const router = useRouter();
const treeData = ref([])
const initData = () => {
    axios.get('http://localhost:8080/category')
        .then((res) => {
            console.log(res)
            if (res.status === 200 && res.data.code === 200) {
                const categories = res.data.data
                const formattedData = categories.map(category => formatCategory(category))
                treeData.value = formattedData
            } else {
                console.error('Failed to fetch category data')
            }
        })
        .catch(err => {
            console.error('Error fetching category data:', err)
        })
}

const formatCategory = (category) => {
    const formattedCategory = {
        id: category.id,
        label: category.name,
        children: category.children ? category.children.map(child => formatCategory(child)) : []
    }
    return formattedCategory
}

const remove = (node, data) => {
    let id = data.id;
    axios.delete('http://localhost:8080/category', {
        params: {
            id
        }
    }).then((res) => {
        if (res.status == 200) {
            let rs = res.data;
            if (rs.code == 200) {
                ElMessage({
                    message: '删除成功',
                    type: 'success',
                })
                initData()
            }
        }
    }).catch((err) => {
        console.log(err);
    })
}

const renderContent = (
    h,
    {
        node,
        data,
        store,
    }
) => {
    return h(
        'span',
        {
            class: 'custom-tree-node',
        },
        h('span', null, node.label),
        h(
            'span',
            null,
            h(
                'a',
                {
                    onClick: () => append(data),
                },
                '添加子分类 '
            ),
            h(
                'a',
                {
                    style: 'margin-left: 8px',
                    onClick: () => remove(node, data),
                },
                '删除'
            )
        )
    )
}
...
//初始化挂载运行并渲染数据
onMounted(() => {
    initData()
})

该处使用的url网络请求的数据。
在这里插入图片描述
树状分类结构图,并且可以


总结

在配置MyBatis高级映射时,一定要注意resultType和resultMap的用法,千万不要用混淆了。在使用resultMap时需要我们自己去配置高级映射。
提示:①当提供的返回类型属性是resultType时,MyBatis会将Map里面的键值对取出赋给resultType所指定的对象对应的属性。所以其实MyBatis的每一个查询映射的返回类型都是ResultMap,只是当提供的返回类型属性是resultType的时候,MyBatis对自动的给把对应的值赋给resultType所指定对象的属性。 ②当提供的返回类型是resultMap时,因为Map不能很好表示领域模型,就需要自己再进一步的把它转化为对应的对象,这常常在复杂查询中很有作用。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值