React antd 封装的可编辑的Tree

279 阅读2分钟

上代码:仅供参考哦~

import React, { useState, useEffect } from 'react'
import { Tree, Input, } from 'antd'
import {
  PlusOutlined,
  EditOutlined,
  MinusOutlined,
  CheckOutlined,
  DeleteOutlined,
} from '@ant-design/icons'
import _ from 'lodash'

const { TreeNode } = Tree

export default props => {

  const [expandedKeys, setExpandedKeys] = useState([])
  const [treeData, setTreeData] = useState(props.data) // 业务需求传过来的数组
  const [dataList, setDataList] = useState([]) // 存放扁平数据的数组
  const [autoExpandParent, setAutoExpandParent] = useState(true)

  // 调用扁平数组的方法
  useEffect(() => {
    generateList(treeData)
  }, [expandedKeys])

  // 展开时触发
  const onExpand = (expandedKeys) => {
    setExpandedKeys(expandedKeys)
    setAutoExpandParent(false)
    setDataList([])
  }

  // 添加子元素
  const addNode = (key, data) =>
    data.map((item) => {
      if (item.key === key) {
        if (item.children) {
          item.children.push({
            value: 'default',
            defaultValue: 'default',
            key: `${key}-${Math.random(100)}`,
            parentKey: key,
            isEditable: false,
          })
        } else {
          item.children = []
          item.children.push({
            value: 'default',
            defaultValue: 'default',
            key: `${key}-${Math.random(100)}`,
            parentKey: key,
            isEditable: false,
          })
        }
        return
      }
      if (item.children) {
        addNode(key, item.children)
      }
    })

  // 点击添加
  const onAdd = (e) => {
    const copyData = _.cloneDeep(treeData)
    const copyExpandedKeys = _.cloneDeep(expandedKeys)
    if (copyExpandedKeys.indexOf(e) === -1) {
      copyExpandedKeys.push(e)
    }
    addNode(e, copyData)
    setExpandedKeys(copyExpandedKeys)
    setTreeData(copyData)
  }

  // 删除结点,递归
  const deleteNode = (key, data) =>
    data.map((item, index) => {
      if (item.key === key) {
        data.splice(index, 1)
        return
      } else {
        if (item.children) {
          deleteNode(key, item.children)
        }
      }
    })

  // 点击删除  
  const onDelete = (key) => {
    const copyData = _.cloneDeep(treeData)
    deleteNode(key, copyData)
    setTreeData(copyData)
  }

  // 编辑结点,递归
  const editNode = (key, data) =>
    data.map((item) => {
      if (item.key === key) {
        item.isEditable = true
      } else {
        item.isEditable = false
      }
      // 当某节点处于编辑状态,并改变数据,点击编辑其他节点时,此节点变成不可编辑状态,value 需要回退到 defaultvalue
      item.value = item.defaultValue
      if (item.children) {
        editNode(key, item.children)
      }
    })

  // 点击编辑
  const onEdit = (key) => {
    const copyData = _.cloneDeep(treeData)
    editNode(key, copyData)
    setTreeData(copyData)
    setDataList([])
  }

  // 取消编辑递归
  const closeNode = (key, defaultValue, data) =>
    data.map((item) => {
      item.isEditable = false
      if (item.key === key) {
        item.value = defaultValue
      }
      if (item.children) {
        closeNode(key, defaultValue, item.children)
      }
    })

  // 编辑状态下点击取消编辑
  const onClose = (key, defaultValue) => {
    const copyData = _.cloneDeep(treeData)
    closeNode(key, defaultValue, copyData)
    setTreeData(copyData)
  }

  // 保存编辑递归
  const saveNode = (key, data) =>
    data.map((item) => {
      if (item.key === key) {
        item.defaultValue = item.value
      }
      if (item.children) {
        saveNode(key, item.children)
      }
      item.isEditable = false
    })

  // 编辑状态下保存值
  const onSave = (key) => {
    const copyData = _.cloneDeep(treeData)
    saveNode(key, copyData)
    setTreeData(copyData)
    generateList(copyData)
  }

  // 编辑状态下实时写入输入的值递归
  const changeNode = (key, value, data) =>
    data.map((item) => {
      if (item.key === key) {
        item.value = value
      }
      if (item.children) {
        changeNode(key, value, item.children)
      }
    })

  const onChange = (e, key) => {
    const copyData = _.cloneDeep(treeData)
    changeNode(key, e.target.value, copyData)
    setTreeData(copyData)
  }

  const renderTreeNodes = (data) =>
    data.map((item) => {
      if (item.isEditable) {
        item.title = (
          <div>
            <input
              value={item.value}
              onChange={(e) => onChange(e, item.key)}
            />
            <MinusOutlined
              type="close"
              style={{ marginLeft: 20 }}
              onClick={() => onClose(item.key, item.defaultValue)}
            />
            <CheckOutlined
              type="check"
              style={{ marginLeft: 10 }}
              onClick={() => onSave(item.key)}
            />
          </div>
        )
      } else {
        item.title = (
          <div>
            <span>{item.value}</span>
            <span>
              <EditOutlined
                style={{ marginLeft: 20 }}
                type="edit"
                onClick={() => onEdit(item.key)}
              />
              <PlusOutlined style={{ marginLeft: 10 }} type="add" onClick={() => onAdd(item.key)} />
              <DeleteOutlined
                style={{ marginLeft: 10 }}
                type="edit"
                onClick={() => onDelete(item.key)}
              />
            </span>
          </div>
        )
      }
      if (item.children) {
        return (
          <TreeNode title={item.title} key={item.key} dataRef={item}>
            {renderTreeNodes(item.children)}
          </TreeNode>
        )
      }
      return <TreeNode {...item} />
    })

  const checkEdit = (data) => {
    let bool
    data.map(item => {
      if (item.isEditable) {
        bool = true
        return
      }
      if (item.children) {
        checkEdit(item.children)
      }
    })
    return bool
  }

  const generateList = (data) => {
    for (let i = 0; i < data.length; i++) {
      const { key } = data[i]
      dataList.push({ key, value: data[i].value })
      if (data[i].children) {
        generateList(data[i].children)
      }
    }
  }

  const getParentKey = (key, tree) => {
    let parentKey
    for (let i = 0; i < tree.length; i++) {
      const node = tree[i]
      if (node.children) {
        if (node.children.some(item => item.key === key)) {
          parentKey = node.key
        } else if (getParentKey(key, node.children)) {
          parentKey = getParentKey(key, node.children)
        }
      }
    }
    return parentKey
  }


  return (
    <div>
      <Tree
        showLine={true}
        expandedKeys={expandedKeys}
        autoExpandParent={autoExpandParent}
        onExpand={onExpand}
        draggable
        selectable={false}
      >
        {renderTreeNodes(treeData)}
      </Tree>
    </div>
  )
}