树形结构-CRUD接口

news2025/1/22 21:57:06

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

新增效果 --默认值是 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;


 

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

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

相关文章

算法简单笔记4

5月31号&#xff0c;明天决赛&#xff0c;今天脑子也是一滩浆糊&#xff0c;踏马的一道题也做不出来&#xff0c;超级难受&#xff0c;只好简单复盘一下两道之前的题目&#xff0c;看完就差不多了&#xff0c;再学也没啥用了&#xff0c;写完这两题题解我就回去打把steam绝地求…

AI大模型的生命周期:从开发到退役的全面解析

前言 人工智能大模型&#xff08;AI大模型&#xff09;是当前AI领域的一大热点&#xff0c;它们具有强大的计算能力和广泛的应用前景。本文将全面介绍AI大模型的基础知识、训练过程、使用方法和应用场景。 一、初步了解AI大模型 AI大模型&#xff0c;通常指的是参数量达到亿…

k8s——Pod进阶(资源限制和探针)

一、资源限制 1.1 资源限制的定义 当定义Pod时可以选择性地为每个容器设定所需要的资源数量。 最常见的可设定资源是CPU和内存大小&#xff0c;以及其他类型的资源。 当为Pod中的容器指定了request资源时&#xff0c;调度器就使用该信息来决定将Pod调度到哪个节点上。当还为容器…

5.29工效学-人因工程人机交互

对于工效学这门课&#xff0c;一直都感觉很有意思&#xff0c;是一个值得再认真一点的课。可惜上课的时候效率不高&#xff0c;有感兴趣的东西课后也没有自行去拓展开来&#xff0c;前面的课我感觉还讲了比较重要的东西&#xff0c;但是&#xff0c;全忘了呢&#xff08;真的对…

【C++】入门(二):引用、内联、auto

书接上回&#xff1a;【C】入门&#xff08;一&#xff09;&#xff1a;命名空间、缺省参数、函数重载 文章目录 六、引用引用的概念引用的使用场景1. 引用做参数作用1&#xff1a;输出型参数作用2&#xff1a;对象比较大&#xff0c;减少拷贝&#xff0c;提高效率 2. 引用作为…

无忧易售ERP:解锁TikTok平台订单处理新效能,赋能跨境电商新未来

在这个全球化电商飞速发展的时代&#xff0c;TikTok作为新兴的电商蓝海&#xff0c;正吸引着无数商家的目光。然而&#xff0c;如何在竞争激烈的市场中脱颖而出&#xff0c;高效管理订单&#xff0c;提升顾客体验&#xff0c;成为每个商家必须面对的课题。无忧易售ERP&#xff…

uni-app+php 生成微信二维码 分销海报

主要代码如下&#xff0c;可直接复制调试参数&#xff1a; //查询当前用户是否有分销海报public function user_poster(){$this->checkAuth();//查询会员信息$user $this->getUserInfoById($this->user_id);if(!empty($user[distribution_img])){$result[data] $use…

一文搞懂分布式事务-Saga

Saga定义 Saga模式是一种分布式事务处理模式&#xff0c;用于保证分布式系统中的一系列操作要么全部成功执行&#xff0c;要么全部回滚&#xff0c;以实现一致性的目标。它采用了长事务的概念&#xff0c;将原子操作拆分为多个子事务&#xff0c;并通过补偿机制保证整个事务的…

Linux基本命令的使用(mkdir)

一、Linux必备命令之mkdir • mkdir命令主要用于创建目录 • 语法: mkdir [选项] 目录名称 若指定目录不存在则创建目录&#xff1b; 选项&#xff1a; -p&#xff0c;--parents 需要时创建目录的上层目录&#xff0c;若这些 目录已存在也不当作错误处理 二、Linux必备命令之…

成绩发布小程序哪个好用?

大家好&#xff0c;今天我要来跟大家分享一个超级实用的小秘密——易查分小程序&#xff01;作为老师&#xff0c;你是不是还在为发放成绩而头疼&#xff1f;是不是还在为通知家长而烦恼&#xff1f;别急&#xff0c;易查分小程序来帮你啦&#xff01; 易查分简直是老师们的贴心…

实战经验分享之移动云快速部署Stable Diffusion SDXL 1.0

本文目录 前言产品优势部署环境准备模型安装测试运行 前言 移动云是中国移动面向政府、企业和公众的新型资源服务。 客户以购买服务的方式&#xff0c;通过网络快速获取虚 拟计算机、存储、网络等基础设施服务&#xff1b;软件开发工具、运行环境、数据库等平台服务&#xff1…

活动选择问题(贪心法)

目录 问题概述 实例分析 代码实现 问题概述 实例分析 求解蓄栏保留问题。农场有n头牛,每头牛会有一个特定的时间区间[b,e]在蓄栏里挤牛奶,并且一个蓄栏里任何时刻只能有一头牛挤奶。现在农场主希望知道最少蓄栏能够满足上述要求,并给出每头牛被安排的方案。对于多种可行方案…

【算法】贪心算法简介

贪心算法概述 目录 1.贪心算法概念2.贪心算法特点3.贪心算法学习 1.贪心算法概念 贪心算法是一种 “思想” &#xff0c;即解决问题时从 “局部最优” 从而达到 “全局最优” 的效果。 ①把解决问题的过程分为若干步②解决每一步时候&#xff0c;都选择当前最优解(不关注全局…

如何编写一份高质量的渗透测试报告?

随着网络安全威胁的不断扩展与升级&#xff0c; 渗透测试目前已经成为众多组织主动识别安全漏洞与潜在风险的关键过程。然而&#xff0c;渗透测试的真正价值在于为用户提交一份全面和可操作的渗透测试报告&#xff0c;这份报告不仅仅是一个技术性文档&#xff0c;同时也是促进安…

c语言基础:数组的运用以及在内存中的地址的理解

目录 目录&#xff1a; 1.数组作为函数参数 2.数组在内存中的存储 2.1数组名是什么&#xff1f; 2.2下面我们来探讨二维数组的各个名字表示什么 二维数组的首元素地址是什么呢&#xff1f; *arr表示的是什么呢 &#xff1f;&#xff08;arr是二维数组&#xff09; 1.数组作…

纯分享#126个电商平台集合(包含60个不同国家)值得一看

01 亚洲 中国 淘宝&#xff1a;拥有大量的卖家和商品种类&#xff0c;主要面向中国市场。天猫:淘宝旗下的B2C电商平台&#xff0c;提供品质保证、正品保障的商品。京东:中国第二大B2C电商平台&#xff0c;提供品质保证、正品保障的商品。苏宁易购: 中国家电连锁巨头苏宁旗下的…

玩机进阶教程------修改gpt.bin分区表地址段 完全屏蔽系统更新 fast刷写分区表 操作步骤解析【二】

上期博文简单说明了分区表的基本常识。我们在有些环境中需要屏蔽手机的系统更新选项。除了以前博文中说明的修改系统更新下载文件夹的方法。还可以通过修改分区表类达到目的。在一些辅助维修工具上面带修改分区表功能。修改后效果为屏蔽系统更新和可以恢复出厂。原则上不深刷都…

STP----生成树协议

目的&#xff1a;解决二层环路问题 跨层封装 广播风暴---广播帧在二层环路中形成逆时针和顺时针转动环路&#xff0c;并且无限循环&#xff0c;最终造成设备宕机&#xff0c;网络瘫痪。 MAC地址表的翻摆&#xff08;漂移&#xff09;---同一个数据帧&#xff0c;顺时针接收后记…

pytorch学习笔记2

首先如果遇到conda找不到包&#xff0c;pip老是超时的情况建议添加一下镜像源 conda的 conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/ cond…

将 cuda kernel 编译成 ptx 和 rocm的hip asm

1&#xff0c;cuda 源码编译 cuda_a_one.cu __global__ void NNNNNVVVVV_one(int *A) {A[333] 777; }编译命令&#xff1a; %.ptx: %.cu nvcc -archsm_70 -ptx $< -o $ 生成的结果&#xff1a; 2, hip 源码编译 hip_a_one.hip__global__ void AAAAAMMMMM_one(int *A…