一个树状结构的参数需求

基于react antd设计的树状参数需求,之前遇到过这个层级比较深的参数需求,类型与请求后端设计参数,类型可以选择数组对象数字字符串等

ini 复制代码
import React, { useEffect, useState, forwardRef, useImperativeHandle } from 'react';
import { Form, Select, Input, Row, Col, Button, Tree, Space } from 'antd';
import { PlusCircleOutlined, MinusCircleOutlined, PlusOutlined, DeleteOutlined } from '@ant-design/icons';
import styles from '../index.module.less';
import { cloneDeep } from 'lodash';

const ParamSet = forwardRef((props, ref) => {
  const { onChange, value } = props;
  const [treeData, setTreeData] = useState([
    {
      title: (key) => renderTreeNodesTitle(key),
      key: '0-0',
      isLeaf: false,
      name: 'root',
      type: 'object',
      desc: 'root',
      children: [],
    },
  ]);
  const addNode = (currNode) => {
    if (currNode.isLeaf) {
      currNode.isLeaf = false;
    }
    console.log('currNode', currNode);
    if (currNode.children && currNode.children.length > 0) {
      if (currNode.type === 'array') {
        const firstChild = currNode.children[0];
        currNode.children.push({
          title: (key) => renderTreeNodesTitle(key),
          key: `${currNode.key}-${Date.now().toString().slice(-5)}`,
          isLeaf: true,
          parentType: currNode.type,
          parentNode: currNode,
          name: '',
          type: firstChild.type,
          value: '',
          desc: '',
        });
      } else if (currNode.type === 'object') {
        currNode.children.push({
          title: (key) => renderTreeNodesTitle(key),
          key: `${currNode.key}-${Date.now().toString().slice(-5)}`,
          isLeaf: true,
          parentType: currNode.type,
          name: '',
          type: 'string',
          value: '',
          desc: '',
        });
      }
      console.log('treeData', treeData);
      setTreeData([...treeData]);
      return;
    }
    console.log('zhepaobulai');
    currNode.children = [
      {
        title: (key) => renderTreeNodesTitle(key),
        key: `${currNode.key}-${Date.now().toString().slice(-5)}`,
        isLeaf: true,
        parentType: currNode.type,
        parentNode: currNode,
        name: '',
        type: 'string',
        value: '',
        desc: '',
      },
    ];
    setTreeData([...treeData]);
  };
  const findNodes = (treeData, currNode) => {
    const res = treeData.filter((item) => {
      if (item.key !== currNode.key) {
        if (item.children?.length) {
          item.children = findNodes(item.children, currNode);
        }
        return true;
      } else {
        return false;
      }
    });
    return res;
  };
  const delNode = (currNode) => {
    const res = findNodes(treeData, currNode);
    setTreeData(res);
  };
  const changeParamName = (e, currNode) => {
    console.log(e.target.value);
    currNode.name = e.target.value;
    setTreeData([...treeData]);
  };
  const changeParamType = (e, currNode) => {
    currNode.type = e;
    if (e === 'object' || e === 'array') {
      currNode.children = null;
      currNode.isLeaf = false;
      currNode.value = '';
    } else {
      currNode.children = null;
      currNode.isLeaf = true;
    }
    if (currNode.parentType === 'array') {
      currNode.parentNode.children.forEach((item) => {
        item.type = currNode.type;
      });
    }
    setTreeData([...treeData]);
  };
  const changeParamValue = (e, currNode) => {
    currNode.value = e.target.value;
    setTreeData([...treeData]);
  };
  const changeParamDesc = (e, currNode) => {
    currNode.desc = e.target.value;
    setTreeData([...treeData]);
  };
  const renderTreeNodesTitle = (currNode) => {
    console.log('整棵树', treeData);
    return (
      <div key={currNode.key}>
        <Space size={3}>
          <Input placeholder="参数名" value={currNode.name} disabled={currNode.parentType === 'array'} onChange={(e) => changeParamName(e, currNode)} />
          <Select
            placeholder="参数类型"
            style={{ width: 120 }}
            value={currNode.type}
            onChange={(e) => changeParamType(e, currNode)}
            options={[
              { value: 'string', label: 'string' },
              { value: 'integer', label: 'integer' },
              { value: 'number', label: 'number' },
              { value: 'boolean', label: 'boolean' },
              { value: 'array', label: 'array' },
              { value: 'object', label: 'object' },
            ]}
          />
          <div style={{ width: '120px' }}>
            <Input
              placeholder="参数值"
              disabled={['object', 'array'].includes(currNode.type)}
              value={currNode.value}
              onChange={(e) => changeParamValue(e, currNode)}
            />
          </div>
          <div style={{ width: '120px' }}>
            <Input placeholder="备注" value={currNode.desc} onChange={(e) => changeParamDesc(e, currNode)} />
          </div>
          <div style={{ width: '10px' }}>
            {['object', 'array'].includes(currNode.type) && (
              <PlusCircleOutlined className={styles.headerIcon} onClick={() => addNode(currNode)} />
            )}
          </div>
          <div style={{ width: '10px' }}>
            {!currNode.children?.length && (
              <MinusCircleOutlined className={styles.headerIcon} onClick={() => delNode(currNode)} />
            )}
          </div>
        </Space>
      </div>
    );
  };

  const add = () => {};
  const formatTreeData = (treeData = []) => {
    const res = treeData.map((item) => {
      if (item.type === 'object' || item.type === 'array') {
        if (item.children?.length) {
          item.children = formatTreeData(item.children);
        }
        return {
          name: item.name,
          type: item.type,
          children: item.children,
          desc: item.desc,
        };
      }
      return {
        name: item.name,
        type: item.type,
        value: item.value,
        desc: item.desc,
      };
    });
    return res;
  };
  useEffect(() => {
    const copyData = cloneDeep(treeData);
    const resTreeData = formatTreeData(copyData);
    onChange(resTreeData);
  }, [treeData]);
  useImperativeHandle(ref, () => ({
    getParams: (newParams) => {
      return treeData;
    },
  }));
  return (
    <div>
      <Tree autoExpandParent={true} defaultExpandAll={true} treeData={treeData} />
      {/* <Row>
        <Col span={14}>
          <Button type="dashed" onClick={() => add()} icon={<PlusOutlined />}>
            添加参数信息
          </Button>
        </Col>
      </Row> */}
    </div>
  );
});
export default ParamSet;
相关推荐
前端大卫3 小时前
Vue3 + Element-Plus 自定义虚拟表格滚动实现方案【附源码】
前端
却尘3 小时前
Next.js 请求最佳实践 - vercel 2026一月发布指南
前端·react.js·next.js
ccnocare3 小时前
浅浅看一下设计模式
前端
Lee川3 小时前
🎬 从标签到屏幕:揭秘现代网页构建与适配之道
前端·面试
Ticnix4 小时前
ECharts初始化、销毁、resize 适配组件封装(含完整封装代码)
前端·echarts
纯爱掌门人4 小时前
终焉轮回里,藏着 AI 与人类的答案
前端·人工智能·aigc
twl4 小时前
OpenClaw 深度技术解析
前端
崔庆才丨静觅4 小时前
比官方便宜一半以上!Grok API 申请及使用
前端
星光不问赶路人4 小时前
vue3使用jsx语法详解
前端·vue.js
天蓝色的鱼鱼4 小时前
shadcn/ui,给你一个真正可控的UI组件库
前端