Describa los nodos del árbol personalizado de diseño de react + ant, cambie el nombre de los nodos, agregue, elimine, etc.

1. Nodo personalizado

        Usando el componente de árbol en el diseño de hormigas , el componente de árbol básico solo necesita vincular la propiedad treeData a un valor de estructura de árbol (treeData={treeData}):

        Sin embargo, dado que el nombre para mostrar simple ya no puede satisfacer las necesidades aquí, use el TreeNode procesado por treeData para agregarlo al interior del Tree.El código es el siguiente, donde onSelect se activa cuando se hace clic en el nodo del árbol:

  // 点击节点,第一次点击节点是选中,第二次点击同一个节点是取消选中,用keys来判断是否有选中
  const onSelect = (keys, info) => {
    if (keys.length > 0) {
      setSelectNode(info.node);
    } else {
      setSelectNode({});
    }
  };

//...

      <Tree
        style={
   
   { marginTop: "20px" }}
        showLine={false}
        showIcon={true}
        onSelect={onSelect}
      >
        {handleTreeData(treeData)}
      </Tree>

        Obtenga treeData, el formato de treeData aquí es:

      [{
        id:'1',
        name:'所有',
        count:'21',
        suffix:'江苏',
        childNodes:[
          {
            id:'1-1',
            name:'南京',
            count:'21',
            suffix:'',
            childNodes:[]
          }
        ]
      }]

        Obtenido de la interfaz:

  const _getTreeData= async () => {
    setTreeData([]);
    try {
      let result = await getTreeData();
      setTreeData(result);
    } catch (error) {
    }
  };

        A continuación, el autor necesita procesar los datos.Como se puede ver en el código anterior, los datos treeData se procesan a través de la función handleTreeData. Analice la estructura de árbol básica. El nodo de Tree es en realidad TreeNode. Para cada nodo de TreeNode, de acuerdo con la introducción en el sitio web oficial , solo necesita establecer su título y atributos clave. Generalmente, el atributo clave es la identificación del nodo del datos del árbol y, por lo tanto, recursivamente fuera de todos los TreeNodes:

  //  重写树
  const handleTreeData = (treeData) => {
    return treeData?.map((treeNode) => handleNodeData(treeNode));
  };
  const handleNodeData = (treeNode) => {
    if (treeNode.toString() === "[object Object]") {
        treeNode.title = (
          <div>
            <span className="text-overflow" title={treeNode.name}>{treeNode.name}</span>
            <span>({treeNode.other.count}) _{treeNode.suffix}</span>
          </div>
        );

        return (
            <TreeNode title={treeNode.title} key={treeNode.id}>
                {treeNode?.childNodes?.map((n) => handleNodeData(n))}
            </TreeNode>
        );
    }
    return <TreeNode {...treeNode} />;
  };

        Hasta ahora, el árbol se ha mostrado de acuerdo con las necesidades del autor:


 2. Cambiar el nombre del nodo

        Agregue un botón detrás de cada nodo. Haga clic en el botón para cambiar el nodo al estado de edición. El valor predeterminado es el nombre del nodo original. De acuerdo con lo anterior, es fácil pensar en agregar un botón de edición a treeNode.title en handleNodeData() y renombrar vinculante():

<Button onClick={() => rename()} size={"small"}>重命名</Button>

Además, los atributos de isEdit y defaultValue (utilizados para usar el nombre de nodo original después de cancelar el cambio de nombre)         deben agregarse a cada nodo . Si isEdit es verdadero, indica el estado de edición, de lo contrario, el nodo se muestra normalmente . Inicialice los datos y establezca isEdit de todos los nodos en falso. defaultValue es el valor de nombre .

  // 设置不可编辑
  const setAllNotEdit = (arr) => {
    let data = [].concat(arr);
    data.forEach((val) => {
      val.isEdit = false;
      if (val.childNodes && val.childNodes.length) {
        setAllNotEdit(val.childNodes);
      }
    });
    return data;
  };
  // 查询组织树
  const _getTreeData= async () => {
    //...
      let data = setAllNotEdit(result);
      setTreeData(data);
    //...
  };

        Haga clic en cambiar el nombre para activar el cambio de nombre (): busque los datos del nodo en los datos del árbol (defina la función deepTree para encontrar los datos del nodo de destino y cambie el valor de la clave denominada tercer parámetro de entrada al cuarto parámetro de entrada ), cambie isEdit a verdadero. Haga definiciones de nodo separadas para el estado de edición y el estado normal en handleNodeData:

  const deepTree = (arr, key, keyName, value, otherValue) => {
    let data = [].concat(arr);
    for (let i = 0; i < data.length; i++) {

      if (data[i].id === key) {
        data[i][keyName] = value;
      } else if (typeof otherValue === "boolean") {
        data[i][keyName] = otherValue;
      }
      if (data[i].childNodes && data[i].childNodes.length) {
        deepTree(data[i].childNodes, key, keyName, value, otherValue);
      }
    }
    return data;
  };
 
 // 重命名
  const rename = () => {
    if (selectNode && selectNode.key) {
      let data = deepTree(treeData, selectNode.key, "isEdit", true, false);
      setTreeData(data);
    } else {
      message.warning("请选择节点");
    }
  };

//...

const handleNodeData = (treeNode) => {
    if (treeNode.toString() === "[object Object]") {
      if (treeNode.isEdit) {
        treeNode.title = (
          <div>
            <input value={treeNode.name} onChange={(e) => { changeNodeName(e,treeNode.id); }}/>
            ({treeNode.count})
            _{treeNode.suffix}
            <Button onClick={() => { saveTreeNode(treeNode); }} size={"small"} type="link" >确定</Button>
            <Button onClick={() => { cancelRename(treeNode); }} size={"small"} type="link" >取消</Button>
          </div>
        );
      } else {
            //...
        }
        //...
    }
    return <TreeNode {...treeNode} />;
};

        En este punto, cuando se hace clic en el botón de cambio de nombre, el nodo ha cambiado al estado de edición y hay dos botones de Aceptar y Cancelar después de la entrada. Cuando se ingresa un nuevo nombre en la entrada, se activa changeNodeName (). Sin este paso, el valor de la entrada no se puede modificar (porque el nombre del nodo siempre está vinculado y el nombre del nodo no ha cambiado):

  // 修改节点名称
  const changeNodeName = (e, key) => {
    let data = deepTree(treeData, key, "name", e.target.value);
    setTreeData(data);
  };

        Cuando hace clic en Cancelar, significa cancelar el cambio de nombre y usar el nombre original. Encuentre el nodo actual en los datos del nodo y modifique el valor al valor anterior (hemos guardado este valor en defaultValue):

  // 取消修改节点名称
  const cancelRename = (treeNode) => {
    let dataHasReset = deepTree(
      treeData,
      treeNode.id,
      "name",
      treeNode.defaultValue
    );
    let data = setAllNotEdit(dataHasReset);
    setTreeData(data);
  };

        Haga clic en Aceptar para modificar el nombre del nodo.

  • Si solo necesita actualizar la página en este momento , solo necesita encontrar el nodo en los datos del nodo y actualizar el valor predeterminado:
  const saveTreeNode = (treeNode) => {
    let dataHasChangeDefaultVal = deepTree(
      treeData,
      treeNode.id,
      "defaultValue",
      treeNode.name
    );
    let data = setAllNotEdit(dataHasChangeDefaultVal);
    setTreeData(data);
  };
  • Si llama a la interfaz para actualizar el nodo, solo necesita llamar a la interfaz y volver a cargar el árbol después de que la interfaz tenga éxito:
  // 保存修改的节点名称
  const saveTreeNode = async (treeNode) => {
    try {
      await updateNode({
        //...
      });
      _getTreeData();
    } catch (e) {
    }
  };

        En el control de árbol, hacer clic en un nodo una vez significa seleccionar el nodo actual, y hacer clic nuevamente significa cancelar la selección, pero al cambiar al estado de edición, podemos hacer clic varias veces. Para evitar la pérdida de datos, modifique onSelect de la siguiente manera ( donde dataref son los accesorios de TreeNode):

  // 点击节点
  const onSelect = (keys, info) => {
    if (keys.length > 0 || info.node?.dataRef?.isEdit) {
      setSelectNode(info.node);
    } else {
      setSelectNode({});
    }
  };

const formatNodeData = (treeNode) => {
    if (treeNode.toString() === "[object Object]") {
      //...
      return (
        <TreeNode title={treeNode.title} key={treeNode.id} dataRef={treeNode}>
          {treeNode?.childNodes?.map((d) => formatNodeData(d))}
        </TreeNode>
      );
    }
    return <TreeNode {...treeData} />;
})

        Después de cambiar el nombre del nodo seleccionado, aunque el árbol es un árbol nuevo, el estado seleccionado del nodo y los datos del nodo seleccionado se guardan Para evitar malentendidos causados ​​por datos fuera de sincronización, aquí el autor obtiene un nuevo Cuando el se muestra el árbol, el estado seleccionado se eliminará y los datos seleccionados también estarán vacíos . Solo se necesita setSelectNode({}) para vaciar los datos seleccionados . Pero eliminar el estado seleccionado requiere el uso de otra propiedad del control Tree: selectedKeys , lo que significa el nodo seleccionado. Cuando se agrega esta propiedad, cuando se hace clic en el nodo, el valor vinculado también debe actualizarse:

  // 点击节点
  const onSelect = (keys, info) => {
    setSelectedKeys(keys);
    //...
  };

//...
      <Tree
        showLine={false}
        showIcon={true}
        defaultExpandAll={true}
        onSelect={onSelect}
        selectedKeys={selectedKeys}
      >
        {formatTreeData(treeData)}
      </Tree>

        Hasta ahora, se ha implementado el cambio de nombre de los nodos.


3. Agregar nuevos nodos

        El autor agrega nodos secundarios aquí y los nodos hermanos son similares, por lo que no los repetiré aquí.

        Los llamados nuevos nodos en realidad están procesando datos en una estructura de árbol.

  • Si solo necesita actualizar la página en este momento, agregue un objeto a los childNodes del nodo seleccionado actualmente, así que busque el nodo seleccionado recursivamente y empuje un nuevo nodo:
  const onAdd = (arr) => {
    let data = [].concat(arr);
    data.forEach((item) => {
      if (item.id === selectNode.key) {
        if (!item.childNodes) {
          item.childNodes = [];
        }
        item.childNodes.push({
          name: "新节点",
          defaultValue: "新节点",
          id: selectNode.key + Math.random(100),
          suffix:'',
          count:'',
          isEditable: false,
          childNodes: [],
        });
        return;
      }
      if (item.childNodes) {
        onAdd(item.childNodes);
      }
    });
    return data;
  };
  const addNode = () => {
    if (selectNode && selectNode.key) {
        let data = onAdd(treeData);
        setTreeData(data);
    } else {
      message.warning("请选择节点");
    }
  };
  • Si llama a la interfaz para actualizar el nodo, solo necesita llamar a la interfaz y volver a cargar el árbol después de que la interfaz tenga éxito:
  // 添加下级
  const addNode = async () => {
    if (selectNode && selectNode.key) {
        try {
          let result = await addNode({
            //...
          });
          _getTreeData();
        } catch (e) {
        }
    }
  };

        Hasta el momento, el nuevo nodo ha sido implementado.


4. Eliminar nodo

        A diferencia de agregar un nodo, eliminar un nodo es buscar el nodo seleccionado en los datos y eliminar el elemento de childNodes. También parte de dos escenarios:

  • Si solo necesita actualizar la página en este momento, elimine un objeto en los childNodes del nodo seleccionado actualmente, por lo tanto, busque el nodo seleccionado recursivamente, simplemente empalme:
  const onDelete = (arr) => {
    arr.forEach((item, index) => {
      if (item.id === selectNode.key) {
        arr.splice(index, 1);
        return;
      }
      if (item.childNodes) {
        onDelete(item.childNodes);
      }
    });
    return arr;
  };
  const delNode = () => {
    if (selectNode && selectNode.key) {
        let data = onDelete(treeData);
        setTreeData([].concat(data));
        setSelectNode({});
    } else {
      message.warning("请选择节点");
    }
  };
  • Si llama a la interfaz para actualizar el nodo, solo necesita llamar a la interfaz y volver a cargar el árbol después de que la interfaz tenga éxito:
  // 删除节点
  const delNode = () => {
    if (selectNode && selectNode.key) {
      try {
        let result = await deleteNode({
           //...
        });
        _getTreeData();
      } catch (e) {}
    } else {}
  };

        Hasta ahora, se ha implementado la eliminación de nodos.


Resumir

        Este artículo detalla los nodos personalizados para el control del árbol del diseño de react + ant, así como la adición, eliminación y modificación de nodos. Si tiene alguna sugerencia, avise~

Supongo que te gusta

Origin blog.csdn.net/sxww_zyt/article/details/129927356
Recomendado
Clasificación