java+vue3+el-tree实现树形结构操作

news2025/1/13 13:48:24

基于springboot + vue3 elementPlus实现树形结构数据的添加、删除和页面展示

效果如下

在这里插入图片描述
在这里插入图片描述

代码如下,业务部分可以自行修改

  • java后台代码
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.daztk.mes.common.annotation.LogOperation;
import com.daztk.mes.common.utils.Result;
import com.daztk.mes.module.cad.dto.CadPartDto;
import com.daztk.mes.module.cad.entity.CadPart;
import com.daztk.mes.module.cad.entity.CadRelation;
import com.daztk.mes.module.cad.service.ICadPartService;
import com.daztk.mes.module.cad.service.ICadRelationService;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 零部件 
 * </p>
 *
 * @since 2024-05-30
 */
@RestController
@RequestMapping("cad/part")
public class CadPartController {

    @Autowired
    ICadPartService service;

    @Autowired
    ICadRelationService relationService;


    /**
     * 获取所有零部件
     * @param dto
     * @return
     */
    @GetMapping("listAll")
    public Result listAll(CadPartDto dto){
        List<CadPart> list = new ArrayList<>();
        try {
            list = service.getList(dto);

            if(CollectionUtils.isNotEmpty(list)){
                list = getTree(list);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return new Result().err();
        }
        return new Result().ok().setData(list);
    }

    @PostMapping("add")
    @LogOperation(tags = "CAD管理-零部件",serviceClass = ICadPartService.class,entityClass = CadPart.class)
    public Result add(@RequestBody CadPart cadPart){
        try {
            service.saveData(cadPart);
        } catch (Exception e) {
            e.printStackTrace();
            return new Result().err();
        }
        return new Result().ok().setData(cadPart);
    }


    @DeleteMapping("delete")
    @LogOperation(tags = "CAD管理-零部件",serviceClass = ICadPartService.class,entityClass = CadPart.class)
    public Result delete(@RequestBody Long[] ids){
        try {
            List<Long> idList = Arrays.asList(ids);
            List<Long> list = recurveIdList(idList);
            service.removeByIds(list);
        } catch (Exception e) {
            e.printStackTrace();
            return new Result().err();
        }
        return new Result().ok();
    }

    /**
     * 递归获取id树
     *
     * @param ids
     * @return
     */
    public List<Long> recurveIdList(List<Long> ids){
        List<Long> list = new ArrayList<>();
        for(Long id: ids){
            list.add(id);
            //关系表查子集
            List<CadRelation> cadRelations = relationService.list(new QueryWrapper<CadRelation>().eq("parent_id", id));
            if(CollectionUtils.isNotEmpty(cadRelations)){
                List<Long> partIds = cadRelations.stream().map(CadRelation::getPartId).collect(Collectors.toList());
                List<Long> list1 = recurveIdList(partIds);
                list.addAll(list1);
            }
        }
        return list;
    }


    public List<CadPart> getTree(List<CadPart> treeList){
        List<CadPart> collect = treeList.stream()
                .filter(item -> item.getParentId() == 0)//构造最外层节点,即id=0的节点
                .map(item -> {
                    item.setChildren(getChildren(item, treeList));//id=0的节点就为他设置子节点
                    return item;
                }).
                collect(Collectors.toList());
        return collect;

    }

    private static List<CadPart> getChildren(CadPart treeEntity, List<CadPart> treeEntityList) {
        List<CadPart> collect = treeEntityList.stream()
                .filter(item -> item.getParentId().equals(treeEntity.getId()))//判断当前节点的父id是不是要设置节点的id
                .map(item -> {
                    item.setChildren(getChildren(item, treeEntityList));//如果是 为其设置子节点 通过递归 为每个除了最外层节点的节点设置子节点
                    return item;
                })
                .collect(Collectors.toList());
        return collect;
    }

  • 前端代码
<template>
    <div class="custom-tree-container">
        <!-- <p>Using render-content</p>
      <el-tree style="max-width: 600px" :data="dataSource" show-checkbox node-key="id" default-expand-all
        :expand-on-click-node="false" :render-content="renderContent" /> -->
        <el-button type="primary" class="add-btn" icon="el-icon-circle-plus-outline" @click="addEdit">新增零部件</el-button>
        <div style="padding-top: 20px;">
            <el-tree style="max-width: 600px; " :data="dataSource" show-checkbox node-key="id" default-expand-all
            :expand-on-click-node="false" ref="treeRef" :props="mapProps">
            <template #default="{ node, data }">
                <span class="custom-tree-node">
                    <span>{{ node.label }}</span>
                    <span>
                        <a @click="append(data)"> 增加子节点 </a>
                        <a style="margin-left: 8px" @click="removeNode(node, data)"> 删除 </a>
                    </span>
                </span>
            </template>
        </el-tree>
        </div>
    

        <el-dialog :title="title" v-model="drawer" width="600px">
            <el-form :model="partForm" :rules="rules" ref="ruleFormsss" label-width="120px" class="demo-partForm">
                <el-form-item label="数模号:" prop="partNo">
                    <el-input v-model="partForm.partNo" clearable></el-input>
                </el-form-item>
                <el-form-item label="版本号:" prop="partRev">
                    <el-input v-model="partForm.partRev" clearable></el-input>
                </el-form-item>
                <el-form-item label="名称:" prop="name">
                    <el-input v-model="partForm.name" clearable></el-input>
                </el-form-item>
                <el-form-item label="中文名称:" prop="cName">
                    <el-input v-model="partForm.cName" clearable></el-input>
                </el-form-item>
                <el-form-item label="材料:" prop="material">
                    <el-input v-model="partForm.material" clearable></el-input>
                </el-form-item>
                <el-form-item label="规格:" prop="gauge">
                    <el-input v-model="partForm.gauge" clearable></el-input>
                </el-form-item>
                <el-form-item label="重量:" prop="massqty">
                    <el-input v-model="partForm.massqty" clearable></el-input>
                </el-form-item>
                <el-form-item label="数据类型:" prop="partType">
                    <el-input v-model="partForm.partType" clearable></el-input>
                </el-form-item>
            </el-form>
            <template #footer>
                <span class="dialog-footer">
                    <el-button @click="cancelEdit">取 消</el-button>
                    <el-button type="primary" @click="saveEdit">确 定</el-button>
                </span>
            </template>
        </el-dialog>
    </div>
</template>

<script>
import { reactive, ref, toRefs, unref, onMounted } from 'vue'
import { add, remove, listAll } from '@api/cad/part'
import { ElMessage } from 'element-plus'


export default {
    name: 'part',
    setup() {
        const state = reactive({
            drawer: false,
            title: '',
            query: {
                partName: ''
            },
            dataSource: [
                // {
                //   id: 1,
                //   label: 'Level one 1',
                //   children: []
                //   // children: [
                //   //   {
                //   //     id: 4,
                //   //     label: 'Level two 1-1',
                //   //     children: [
                //   //       {
                //   //         id: 9,
                //   //         label: 'Level three 1-1-1'
                //   //       }
                //   //     ]
                //   //   }
                //   // ]
                // }
            ],
            mapProps: {
                id: 'id',
                label: 'name',
                children: 'children'
            }
        })

        const treeRef = ref(null)

        const append = (data) => {
            state.drawer = true
            state.title = '添加子节点'
            partForm.parentId = data.id
            partForm.partNo = ''
            partForm.partRev = ''
            partForm.name = ''
            partForm.cName = ''
            partForm.material = ''
            partForm.gauge = ''
            partForm.massqty = ''
            partForm.partType = ''

            // debugger
            // 获取当前节点
            // const currentNode = treeRef.value.getNode(data.id)
            // // 获取当前节点在树中的索引
            // // const currentIndex = getNod(data.id)
            // // // 如果索引大于 0,获取前一个节点
            // // if (currentIndex > 0) {
            // //   const prevNode = getNodeAt(currentIndex - 1);
            // //   const prevId = prevNode.id; 
            // //   // 处理前一个节点的 id
            // // }

            // let treeNodeId = data.$treeNodeId //节点id
            // console.info(data)
            // // alert('当前id'+data.id)
            // // alert(data.$treeNodeId)
            // let id = null
            // if (!data.children || data.children.length == 0) {
            //   //没有子节点
            //   id = data.id * 100 + 1
            // } else {
            //   id = data.id * 100 + 1 + data.children.length
            // }
            // const newChild = { id: id, label: data.label + '-' + id, children: [] }
            // const child = [{ id: id, label: data.label + '-' + id, children: [] }]
            // data.children  = child
            // data.push()
            // state.dataSource = [data];
        }

        const removeNode = (node, data) => {
            const ids = []
            ids.push(data.id)
            remove(ids).then(res => {
                if (res.code == 200) {
                    ElMessage({
                        type: 'success',
                        message: '删除成功'
                    })
                    getPartTableData(state.query)
                } else {
                    ElMessage({
                        type: 'error',
                        message: res.msg
                    })
                }
            })
            // dataSource.value = [...dataSource.value];
        }

        const partForm = reactive({
            partNo: '',
            partRev: '',
            name: '',
            cName: '',
            material: '',
            gauge: '',
            massqty: '',
            partType: '',
            parentId: ''
        })

        const ruleFormsss = ref(null)


        const addEdit = () => {
            state.drawer = true
            state.title = '添加零部件'
            partForm.parentId = ''
            partForm.partNo = ''
            partForm.partRev = ''
            partForm.name = ''
            partForm.cName = ''
            partForm.material = ''
            partForm.gauge = ''
            partForm.massqty = ''
            partForm.partType = ''
        }

        // 取消
        const cancelEdit = () => {
            state.drawer = false
            setTimeout(() => {
                const form = unref(partFormsss)
                form.resetFields()
            }, 100)
        }
        // 新建/编辑零部件
        const saveEdit = async () => {
            const form = unref(ruleFormsss)
            if (!form) return
            try {
                await form.validate()
                if (state.title == '添加零部件' || state.title == '添加子节点') {
                    addSavePart()
                }
                if (state.title == '编辑零部件') {
                    updateSavePart()
                }
                state.drawer = false
            } catch (error) {
                console.error(error)
                ElMessage.error('抱歉,您有必填项未填!')
            }
        }


        const addSavePart = () => {
            // let quantity = partForm.quantity + partForm.iQuantity
            const data = {
                partNo: partForm.partNo,
                partRev: partForm.partRev,
                name: partForm.name,
                cName: partForm.cName,
                material: partForm.material,
                gauge: partForm.gauge,
                massqty: partForm.massqty,
                partType: partForm.partType,
                parentId: partForm.parentId
            }
            add(data).then(res => {
                state.drawer = false
                // const treeData = {
                //   id: res.data.id,
                //   label: res.data.name,
                //   children: []
                // }
                // state.dataSource = res.data
                getPartTableData(state.query)
                ElMessage.success('恭喜您,添加成功!')
            })
        }

        const updateSavePart = () => {
            const data = {
                id: state.handleId,
                name: partForm.name,
                manager: partForm.manager,
                startDate: partForm.startDate,
                endDate: partForm.endDate
            }
            update(data).then(res => {
                state.drawer = false
                getPartTableData(state.query)
                ElMessage.success('恭喜您,保存成功!')
            })
        }

        const rules = {
            name: [{ required: true, message: '此处为必填项', trigger: 'blur' }]
        }

        function getPartTableData() {
            listAll(state.query).then(res => {
                state.dataSource = res.data
            })
        }


        onMounted(() => {
            getPartTableData()
        })

        return {
            ...toRefs(state),
            append,
            removeNode,
            addEdit,
            cancelEdit,
            treeRef,
            partForm,
            ruleFormsss,
            saveEdit,
            rules
        }
    }
}




</script>

<style lang="scss" >
.el-tree-node {
  margin-top: 8px;
}
.custom-tree-node {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 18px;
  padding-right: 8px;
}

</style>

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

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

相关文章

高通Android 12 右边导航栏改成底部显示

最近同事说需要修改右边导航栏到底部&#xff0c;问怎么搞&#xff1f;然后看下源码尝试下。 1、Android 12修改代码路径 frameworks/base/services/core/java/com/android/server/wm/DisplayPolicy.java a/frameworks/base/services/core/java/com/android/server/wm/Display…

树莓派4B_OpenCv学习笔记6:OpenCv识别已知颜色_运用掩膜

今日继续学习树莓派4B 4G&#xff1a;&#xff08;Raspberry Pi&#xff0c;简称RPi或RasPi&#xff09; 本人所用树莓派4B 装载的系统与版本如下: 版本可用命令 (lsb_release -a) 查询: Opencv 版本是4.5.1&#xff1a; 学了这些OpenCv的理论性知识&#xff0c;不进行实践实在…

Roboflow 图片分类打标

今天准备找个图片标注工具&#xff0c;在网上搜了一下&#xff0c;看 Yolo 的视频中都是用 Roboflow 工具去尝试了一下&#xff0c;标注确实挺好用的&#xff0c;可以先用一些图片训练一个模型&#xff0c;随后用模型进行智能标注。我主要是做标注然后到处到本地进行模型的训练…

springboot的WebFlux 和Servlet

Spring Boot 中的 Servlet 定义&#xff1a; 在 Spring Boot 中&#xff0c;Servlet 应用程序通常基于 Spring MVC&#xff0c;它是一个基于 Servlet API 的 Web 框架。Spring MVC 提供了模型-视图-控制器&#xff08;MVC&#xff09;架构&#xff0c;用于构建 Web 应用程序。…

【Mac】增加 safari 体验的插件笔记

Safari 本身的功能不全面&#xff0c;探索积累了一点插件笔记&#xff0c;提升使用体验&#xff1b;但后面因为插件或会影响运行速度&#xff0c;就全部都禁止了。做个笔记记录一下。 Cascadea 相当于 stylus&#xff0c;可以自定义页面。测试过几个&#xff0c;只有几个可行。…

Java:爬虫htmlunit抓取a标签

如果对htmlunit还不了解的话可以参考Java&#xff1a;爬虫htmlunit-CSDN博客 了解了htmlunit之后&#xff0c;我们再来学习如何在页面中抓取我们想要的数据&#xff0c;我们在学习初期可以找一些结构比较清晰的网站来做测试爬取&#xff0c;首先我们随意找个网站如下&#xff…

【StableDiffusion】Embedding 底层原理,Prompt Embedding,嵌入向量

Embedding 是什么&#xff1f; Embedding 是将自然语言词汇&#xff0c;映射为 固定长度 的词向量 的技术 说到这里&#xff0c;需要介绍一下 One-Hot 编码 是什么。 One-Hot 编码 使用了众多 5000 长度的1维矩阵&#xff0c;每个矩阵代表一个词语。 这有坏处&#xff0c…

美国空军发布类ChatGPT产品—NIPRGPT

6月11日&#xff0c;美国空军研究实验室&#xff08;AFRL&#xff09;官网消息&#xff0c;空军部已经发布了一款生成式AI产品NIPRGPT。 据悉&#xff0c;NIPRGPT是一款类ChatGPT产品&#xff0c;可生成文本、代码、摘要等内容&#xff0c;主要为为飞行员、文职人员和承包商提…

Python 中浅拷贝(copy)和深拷贝(deepcopy)

1. 浅拷贝&#xff1a; 它创建一个新的对象&#xff0c;但对于原始对象内的子对象&#xff08;如列表中的嵌套列表&#xff09;&#xff0c;只是复制了引用。例如&#xff1a; import copy original_list [1, 2, 3] shallow_copied_list copy.copy(original_list) original_…

【PIXEL】2024年 Pixel 解除 4G限制

首先在谷歌商店下载 Shizuku 和 pixel IMS 两个app 然后打开shizuku &#xff0c;按照它的方法启动 推荐用adb 启动&#xff08; 电脑连手机 &#xff0c;使用Qtscrcpy最简洁&#xff09; 一条指令解决 shell sh /storage/emulated/0/Android/data/moe.shizuku.privileged.ap…

Chrome/Edge浏览器视频画中画可拉动进度条插件

目录 前言 一、Separate Window 忽略插件安装&#xff0c;直接使用 注意事项 插件缺点 1 .无置顶功能 2.保留原网页&#xff0c;但会刷新原网页 3.窗口不够美观 二、弹幕画中画播放器 三、失败的尝试 三、Potplayer播放器 总结 前言 平时看一些视频的时候&#xff…

ListView的使用

&#x1f4d6;ListView的使用 ✅1. 创建ListView✅2. 创建适配器Adapter✅3. 开始渲染数据 主要3步骤&#xff1a; 创建ListView 创建适配器Adapter&#xff0c;和Adapter对应的视图 开始渲染数据 效果图&#xff1a; ✅1. 创建ListView 例如现有DemoActivity页面&#xf…

C# WinForm —— 33 ContextMenuStrip介绍

1. 简介 右键某个控件/窗体时&#xff0c;弹出来的菜单&#xff0c;比如VS中右键窗体&#xff0c;弹出来的这个菜单&#xff1a; 和MenuStrip类似&#xff0c;ContextMenuStrip主菜单下面可以有子菜单&#xff0c;子菜单下面可以有下一级子菜单 2. 属性 和MenuStrip一样 …

国内服务器安装 Docker 服务和拉取 dockerhub 镜像

前提: 有一台海外的VPS,目的是安装dockerhub镜像.适用debian系统 1: 安装 docker-ce (国内服务器) sudo apt-get update sudo apt-get install ca-certificates curl sudo install -m 0755 -d /etc/apt/keyrings sudo curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/…

如何免费用 Qwen2 辅助你翻译与数据分析?

对于学生用户来说&#xff0c;这可是个好消息。 开源 从前人们有一种刻板印象——大语言模型里好用的&#xff0c;基本上都是闭源模型。而前些日子&#xff0c;Meta推出了Llama3后&#xff0c;你可能已经从中感受到现在开源模型日益增长的威力。当时我也写了几篇文章来介绍这个…

【DevOps】Ubuntu基本使用教程

目录 引言 Ubuntu简介 安装Ubuntu 准备工作 创建启动盘 安装过程 桌面环境 基本操作 定制桌面 文件管理 文件操作 文件权限 软件管理 安装软件 更新软件 系统设置 用户账户 网络设置 电源管理 命令行操作 常用命令 管理权限 安全与维护 系统更新 备份…

pdf添加书签的软件,分享3个实用的软件!

在数字化阅读日益盛行的今天&#xff0c;PDF文件已成为我们工作、学习和生活中不可或缺的一部分。然而&#xff0c;面对海量的PDF文件&#xff0c;如何高效地进行管理和阅读&#xff0c;成为了许多人关注的焦点。其中&#xff0c;添加书签功能作为提高PDF文件阅读体验的重要工具…

JetLinks开源物联网平台社区版部署教程

1.上github搜素jetlinks 2.找到源代码&#xff0c;并且下载到本地。 3.项目下载完成之后&#xff0c;还需要另外下载三个核心依赖模块。在github找到jetlinks。 4.点击进去下载&#xff0c;下载完成之后&#xff0c;你会发现里面有三个文件夹是空白的&#xff0c;先不用理会&am…

【云计算】Docker部署Nextcloud网盘并实现随地公网远程访问

配置文件 切换root权限&#xff0c;新建一个nextcloud的文件夹&#xff0c;进入该目录&#xff0c;创建docker-compose.yml [cpslocalhost ~]$ su root Password: 666666 [rootlocalhost cps]# ls Desktop Documents Downloads Music Pictures Public Templates Vide…

Day 16:3040. 相同分数的最大操作数目II

Leetcode 相同分数的最大操作数目II 给你一个整数数组 nums &#xff0c;如果 nums 至少 包含 2 个元素&#xff0c;你可以执行以下操作中的 任意 一个&#xff1a; 选择 nums 中最前面两个元素并且删除它们。选择 nums 中最后两个元素并且删除它们。选择 nums 中第一个和最后一…