树形结构-CRUD接口

先看一下效果:整体的效果
   

新增效果 --默认值是 default

 修改效果 -

大致效果如上

---------------------------------------------------------------------------------------------------------------------------------
下面讲解代码如何实现的
 根据你使用的UI的框架中的树结构---形成相应的数据结构(递归形式)-如果后端给你分好了(感谢他)
这个是给我的是一个  数组包裹的对象形式 进行递归
这个我写在--一个通用的 js 导出形式  这边我需要是的是两种类型 所以分了一下(效果图上的是第一个)  
解释一下:data:后端给你的数据
                  parentId是 从0 或者 -1 开始 看你的需求 我这边是 -1

                   show:我这是区分我要的是那种类型         

递归形式走的

export function buildTree(data, parentId, show) {
  const result = [];
  data?.filter(item => item.parentId === parentId)
    .forEach(items => {
      const children = buildTree(data, items.id, show);
      let node = ''
      if (!show) {
        node = {
          value: `${items.categoryName}`,
          key: `${items.id}`,
          defaultValue: `${items.categoryName}`,
          isEditable: false,
          parentId: `${items.parentId}`,
          children: children.length ? children : undefined,
        };
      } else {
        node = {
          value: `${items.id}`,
          title: `${items.categoryName}`,
          children: children.length ? children : undefined,
        };
      }

      result.push(node);
    });
  return result;
}

 这边都是实现的代码了-------可以详细看下  ---谢谢  -如果不懂-请评论


import { DownOutlined, EditOutlined, PlusOutlined, MinusOutlined, CloseOutlined, CheckOutlined } from '@ant-design/icons';
import { Input, Tree, message } from 'antd';
import React, { useEffect, useState } from 'react';
import moment from 'moment';
import { GetclubGoodsCategoryList, PostclubGoodsCategoryRemove, PostclubGoodsCategorySave, PostclubGoodsCategoryUpdate } from '@/services/commodity';
import { buildTree } from '@/utils/utils';

const { TreeNode } = Tree;
// 三张图
import reduce from '@/assets/icon/reduce.png';
import addIng from '@/assets/icon/addIng.png';
import editIng from '@/assets/icon/editIng.png';
function TreeData({ onNodeClick }) {

    // 佛祖保佑,不会报错

    const [selectedNode, setSelectedNode] = useState(null);

    // const treeData = buildTree(data1, 0);
    const expandedKeyArr = ["0"];

    const [data, setData] = useState();
    const [listObj, setListObj] = useState({});
    let isMounted = true;
    const getDate = async () => {
        try {
//获取最初的数据  ------------接口
            let res = await GetclubGoodsCategoryList();
            if (isMounted && res && res.code == "200") {
                const treeData = buildTree(res.data, 0);
                setData(treeData);
            }
        } catch (error) {
            console.error("Error fetching data: ", error);
        }
    };
    useEffect(() => {

        getDate();
        return () => {
            isMounted = false;
        };
    }, []);


    const [expandedKeys, setExpandedKeys] = useState(expandedKeyArr);

    // 修改---树结构 有值---接口
    const onChangeUpdate = async () => {
        if (Object.keys(listObj).length > 0) {
            let res = await PostclubGoodsCategoryUpdate({
                categoryName: listObj.value,
                // parentId: listObj.key,
                id: listObj.key,

            });
            if (res && res.code == "200") {
                message.success("修改成功");
//重新获取树形结构
                getDate();
            }
        }

    }

    const onExpand = (expandedKeys) => {
        setExpandedKeys(expandedKeys);
    };
    const handleNodeClick = (node) => {
        setSelectedNode(node);

        // 进行暴露---作为一个组件调用你所点击的值对象
        onNodeClick(node);
    };

    const renderTreeNodes = (data, depth = 0) => {
        return data?.map((item) => {
            const title = item.isEditable ? (
                <div style={{
                    display: 'flex',
                    justifyContent: 'space-between',
                    alignItems: 'center',
                }}>

                        {/*  输入框显示 和   一个对号  一个 叉号

*/}

                    <Input placeholder="输入名称" defaultValue={item.defaultValue}
                        onChange={(e) => onChange(e, item)} />

                    <CheckOutlined style={{
                        margin: '0 4px',
                    }} onClick={() => onSave(item)} />
                    <CloseOutlined onClick={() => onClose(item.parentKey, item.defaultValue)} />
                </div>
            ) : (
                <div onClick={() => handleNodeClick(item)}
                    style={{
                        display: 'flex',
                        justifyContent: 'space-between',
                        alignItems: 'center',
                    }}
                >
                    <div
                        style={{
                            whiteSpace: 'nowrap'
                        }}
                    >{item.value}</div>
                    <div
                        style={{
                            display: 'flex',
                            flexWrap: 'nowrap',
                            alignItems: 'center',
                        }}
                    >
                        {/* <EditOutlined onClick={() => onEdit(item.key)} />
                        <PlusOutlined onClick={() => onAdd(item.key)} />
                        {item.parentKey !== "0" && (
                            <MinusOutlined onClick={() => onDelete(item.key)} />
                        )} */}

                        {/* 两层之上就没有图标l 
                        效果上的左边字 右边图片 

*/}
                        {
                            item.value &&
                            <>
                                <img style={{ margin: '0 4px' }} width='15px' onClick={() => onEdit(item.key)} height='15px' src={editIng} alt="" />
                                {
                                    depth < 2 && <img width='15px' onClick={() => onAdd(item.key)} height='15px' src={addIng} alt="" />
                                }

                                <img style={{ margin: '0 4px' }} onClick={() => onDelete(item.key)} width='15px' height='15px' src={reduce} alt="" />
                            </>

                        }
                    </div>
                </div>
            );

            if (item.children) {
                return (
                    <TreeNode title={title} key={item.key}>
                        {renderTreeNodes(item.children, depth + 1)}
                    </TreeNode>
                );
            }

            return <TreeNode title={title} key={item.key} />;
        });
    };

    const onAdd = (key) => {
        if (expandedKeys.indexOf(key) === -1) {
            expandedKeys.push(key);
        }
        setExpandedKeys(expandedKeys.slice());
        addNode(key, data);
        setData([...data]);
    };

    const onEdit = (key) => {
        editNode(key, data);
        setData([...data]);
    };

    const editNode = (key, data) =>
        data.forEach((item) => {
            if (item.key === key) {
                item.isEditable = true;
            } else {
                item.isEditable = false;
            }
            item.value = item.defaultValue;
            if (item.children) {
                editNode(key, item.children);
            }
        });

    const getRandomNumber = (min = 1, max = 10000) => {
        return Math.floor(Math.random() * (max - min + 1)) + min;
    };

    const addNode = (key, data) =>
        data.forEach((item) => {
            if (item.key === key) {
                if (item.children) {
                    item.children.push({
                        value: "default",
                        key: getRandomNumber(),
                        parentKey: key,
                        isEditable: false,
                        showAdd: true
                    });
                } else {
                    item.children = [
                        {
                            value: "default",
                            key: getRandomNumber(),
                            parentKey: key,
                            isEditable: false,
                            showAdd: true
                        },
                    ];
                }
                return;
            }
            if (item.children) {
                addNode(key, item.children);
            }
        });

    const onChange = (e, key) => {
        changeNode(key.parentKey ? key : key, e.target.value, data);
        setData([...data]);

    };

    const changeNode = (key, value, data) =>

        data.forEach((item) => {

            if (item.parentKey || item.key == key.key) {

                item.value = value;
            }
            if (item.children) {
                changeNode(key, value, item.children);
            }
        });

    // 保存---数据新增---接口
    const onSaveObj = async () => {

        let res = await PostclubGoodsCategorySave({
            categoryName: listObj.value,
            parentId: listObj.parentKey,
        })
        if (res && res.code === 200) {
            message.success('新增成功')
            getDate();
        } else {
            message.error('新增失败')
        }
    };

    useEffect(() => {

        if (listObj.showAdd) {

            onSaveObj();
        } else {
            onChangeUpdate()
        }


    }, [listObj])
    const onSave = (key) => {

        if (key.value == undefined) {
            message.error('请输入分类名称')
            return
        }
        setListObj(key)
        saveNode(key, data);
        setData([...data]);

    };


    const saveNode = (key, data) =>
        data.forEach((item) => {
            if (item.parentKey === key) {
                item.defaultValue = item.value;
            }
            if (item.children) {
                saveNode(key, item.children);
            }
            item.isEditable = false;
        });

    const onClose = (key, defaultValue) => {
        closeNode(key, defaultValue, data);
        setData([...data]);
    };

    const closeNode = (key, defaultValue, data) =>
        data.forEach((item, index) => {
            item.isEditable = false;
            if (item.parentKey === key) {
                data.splice(index, 1);
                item.value = defaultValue;
            }
            if (item.children) {
                closeNode(key, defaultValue, item.children);
            }
        });

    const onDelete = (key) => {
        deleteNode(key, data);
        setData([...data]);
    };
    //删除节点---接口
    const deleteIds = async (key) => {
        let data = await PostclubGoodsCategoryRemove({
            ids: key,
        })
        if (data && data.code === 200) {
            message.success('删除成功')
            getDate();
        }
    }
    const deleteNode = (key, data) =>
        data.forEach((item, index) => {
            if (item.key === key) {
                if (!item.showAdd) {
                    deleteIds(key);
                }


                data.splice(index, 1);

                return;
            } else {
                if (item.children) {
                    deleteNode(key, item.children);
                }
            }
        });

    return (
        <div style={{
            position: 'relative',
            left: '-14px'
        }}>
            <Tree onExpand={onExpand} expandedKeys={expandedKeys}>
                {renderTreeNodes(data)}
            </Tree>

        </div>
    );
}

export default TreeData;


 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Pytorch中的torch.save()文件保存格式探索以及mmdetection加载预训练模型参数对不齐和收到意外参数报错解决方案

使用mmdetection时遇到的问题比较多&#xff0c;首先要对自己要使用的预训练模型有一定的了解&#xff0c;并且懂得使用各种分类模型时不同的模型不同任务执行阶段需要参数上的对其。&#xff08;比如mask-rcnn和它的三个头之间的参数&#xff09;。 首先&#xff0c;谈谈torc…

一个案例告诉你,MySQL如何查询今天、昨天、近7天、近30天、本月、上个月、本季度、上季度、本年、上一年数据

参考博客 mysql查询当天/昨天/近7天/近30天/本月/上个月/本季度/上季度/本年/上一年 数据 正文内容 创建测试案例&#xff08;也可直接使用附录MySQL脚本生成数据&#xff09; 1、新建测试表 CREATE TABLE example (id INT AUTO_INCREMENT PRIMARY KEY,date_column DATE,d…

深入Kafka消息分区机制:从原理到实践

深入Kafka消息分区机制&#xff1a;从原理到实践 在现代分布式系统中&#xff0c;如何高效地处理海量数据是一个至关重要的问题。Apache Kafka作为一种高吞吐量的分布式消息系统&#xff0c;广泛应用于日志收集、实时分析等场景。为了保证数据的高效处理和系统的高可扩展性&am…

ChatGPT的逆袭历程:核心技术深度解析

在ChatGPT问世之前&#xff0c;已有许多大模型存在&#xff0c;但为何只有它成为了AI时代的“iPhone时刻”&#xff1f;这不仅得益于其技术优势&#xff0c;还在于其发展过程中所采用的一系列创新策略。本文将深度复盘ChatGPT的逆袭历程&#xff0c;分析其核心技术&#xff0c;…

数组中的第K个最大元素,力扣

目录 题目地址&#xff1a; 我们直接看题解吧&#xff1a; 快速理解解题思路小建议&#xff1a; 审题目事例提示&#xff1a; 解题方法&#xff1a; 解题分析&#xff1a; 解题思路&#xff1a; 题目地址&#xff1a; 215. 数组中的第K个最大元素 - 力扣&#xff08;LeetCode&a…

蚂蚁技术日首次开放,精彩看点分享

每年的 5 月 27 日&#xff0c;是蚂蚁的技术日&#xff0c;用来鼓励蚂蚁技术人保持敬畏和创新之心&#xff0c;到今天&#xff0c;第九届“527 蚂蚁技术日”已发展成为技术周&#xff0c;成为蚂蚁技术人的嘉年华。 2015 年 5 月 27 日&#xff0c;因为光纤被挖断&#xff0c;全…

visual studio code 全局搜索

VScode写代码的时候&#xff0c;会经常性的需要进行查找代码&#xff0c;那么怎么在Visual Studio Code中进行查找呢&#xff0c;下面就来大家vscode全局搜索的方法。 想要在vscode全局搜索进行全局搜索&#xff0c;使用快捷键CTRLSHIFTF即可进行搜索&#xff0c;也可以在左边…

排序算法(一) 基础排序算法

排序算法 基础排序算法 排序本质&#xff1a;减小逆序对的过程 在基础排序算法中&#xff0c;将待排序序列分为相对有序区与相对无序区。 每次遍历到数组末尾称为一轮。 冒泡排序(无序区-有序区, O ( n 2 ) O(n^2) O(n2),稳定,就地) 在每一轮中&#xff0c;逐次与下一邻项…

MMrotate报错AttributeError: ‘NoneType‘ object has no attribute ‘shape‘

使用MMrotate训练自定义数据集报错&#xff1a; AttributeError: ‘NoneType’ object has no attribute ‘shape’ 2024-05-31 17:48:06,121 - mmrotate - INFO - workflow: [(train, 1)], max: 12 epochs 2024-05-31 17:48:06,121 - mmrotate - INFO - Checkpoints will be …

太速科技-基于3U VPX 4核8线程I7 X86主板

基于3U VPX 4核8线程I7 X86主板 一、产品概述 该产品是一款基于第六代Intel i7四核八线程处理器的高性能3U VPX刀片式计算机。产品提供了4个x4 PCIe 3.0总线接口&#xff0c;其中2个x4 PCIe 3.0接口可配置为1个x8 PCIe3.0接口&#xff0c;另外2个x4 PCIe 3.0接口可灵活配置…

8086 汇编笔记(三):第一个程序

一、一个源程序从写出到执行的过程 第一步&#xff1a;编写汇编源程序 第二步&#xff1a;对源程序进行编译连接 第三步&#xff1a;执行可执行文件中的程序 二、源程序 codesg segment ; 定义一个段&#xff0c;段的名称为“codesg”&#xff0c;这个段从此开始…

向量数据库引领 AI 创新——Zilliz 亮相 2024 亚马逊云科技中国峰会

2024年5月29日&#xff0c;亚马逊云科技中国峰会在上海召开&#xff0c;此次峰会聚集了来自全球各地的科技领袖、行业专家和创新企业&#xff0c;探讨云计算、大数据、人工智能等前沿技术的发展趋势和应用场景。作为领先的向量数据库技术公司&#xff0c;Zilliz 在本次峰会上展…

[代码复现]Self-Attentive Sequential Recommendation

参考代码&#xff1a;SASRec.pytorch 可参考资料&#xff1a;SASRec代码解析 前言&#xff1a;文中有疑问的地方用?表示了。可以通过ctrlF搜索’?。 环境 conda create -n SASRec python3.9 pip install torch torchvision因为我是mac运行的&#xff0c;所以device是mps 下面…

【计算机网络】——概述(图文并茂)

概述 一.信息时代的计算机网络二.互联网概述1.网络&#xff0c;互连网&#xff0c;互联网&#xff08;因特网&#xff09;1.网络2.互连网3.互联网&#xff08;因特网&#xff09; 2.互联网简介1.互联网发展的三个阶段2.互联网服务提供者&#xff08;ISP&#xff09;3.互联网的组…

计算器状态的初始化之旅

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、引言 二、状态属性的创建与初始化 三、内部函数与显示逻辑 四、总结与展望 一、引言 在…

使用element的提示框并修改css

使用el-tooltip来做提示框&#xff1a; <el-tooltip popper-class"popper" content"敬请期待" placement"right"><div><i class"iconfont icon-lianjie-01"></i><span>输入链接</span></div&…

掀桌子、降价、免费...之后,国内大模型应用进入高速时代

5月15日&#xff0c;字节跳动打响大模型市场价格战第一枪&#xff1b;5月21日阿里云更狠&#xff0c;价格降了97%&#xff0c;比字节还便宜37.5%同日&#xff0c;百度更为激进&#xff0c;直接宣布其两款主力模型ENIRE Speed和ENIRE Lite全面免费&#xff1b;5月22号&#xff0…

怎么花草识别?方法有三种!

怎么花草识别&#xff1f;在这个五彩斑斓的世界里&#xff0c;花草是我们生活中不可或缺的一部分。它们点缀着我们的环境&#xff0c;为我们带来无尽的美丽与惊喜。然而&#xff0c;面对众多的花草种类&#xff0c;你是否曾感到困惑和迷茫&#xff0c;不知道如何识别它们&#…

沃通CA参与《证书透明规范》及《自动化证书管理规范》两项商密标准制定

沃通CA加入由零信技术牵头的两项商密标准《证书透明规范》及《自动化证书管理规范》编制工作。沃通CA作为国内依法设立的电子认证服务机构与领先的SSL证书服务商&#xff0c;很荣幸参与到两项商密标准的编制工作中&#xff0c;不仅提供多年SSL证书领域的应用经验&#xff0c;还…

Spring Boot详解:深入了解与实践

文章目录 1. Spring Boot简介1.1 什么是Spring Boot&#xff1f;1.2 Spring Boot的历史背景1.3 Spring Boot的核心特点 2. Spring Boot的核心概念2.1 自动配置2.1.1 自动配置原理2.1.2 自定义配置 2.2 Spring Boot Starter2.3 Spring Boot CLI 3. Spring Boot的主要功能模块3.1…