You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现Antd树形表格(Tree Table)的可编辑功能?

Antd树形表格(Tree Table)可编辑实现方案

你的问题核心在于Antd树形表格的行组件并非普通表格的body.row,而是treeRow,原代码中components配置错误,导致EditableRow没有被正确挂载,EditableCell无法获取到上下文里的Form实例,因此点击单元格无法触发编辑状态。此外,原代码缺少树形数据的更新逻辑,即使编辑成功也无法保存修改。

关键修改点

  1. 修正组件配置:将树形表格的行组件指定为treeRow,而非普通表格的body.row
  2. 实现树形数据递归更新:树形结构的数据需要递归查找目标节点并更新
  3. 传递保存回调:确保handleSave函数能传递到EditableCell中

修改后的完整代码

import React from 'react';
import './index.css';
import { Button, Form, Input, Popconfirm, Table, Space, Switch } from 'antd';
import { useContext, useEffect, useRef, useState } from 'react';

const columns = [
  {
    title: 'Name',
    dataIndex: 'name',
    key: 'name',
    editable: true
  },
  {
    title: 'Age',
    dataIndex: 'age',
    key: 'age',
    width: '12%',
    editable: true,
  },
  {
    title: 'Address',
    dataIndex: 'address',
    width: '30%',
    key: 'address',
  },
];

const data = [
  {
    key: 1,
    name: 'John Brown sr.',
    age: 60,
    address: 'New York No. 1 Lake Park',
    children: [
      {
        key: 11,
        name: 'John Brown',
        age: 42,
        address: 'New York No. 2 Lake Park',
      },
      {
        key: 12,
        name: 'John Brown jr.',
        age: 30,
        address: 'New York No. 3 Lake Park',
        children: [
          {
            key: 121,
            name: 'Jimmy Brown',
            age: 16,
            address: 'New York No. 3 Lake Park',
          },
        ],
      },
      {
        key: 13,
        name: 'Jim Green sr.',
        age: 72,
        address: 'London No. 1 Lake Park',
        children: [
          {
            key: 131,
            name: 'Jim Green',
            age: 42,
            address: 'London No. 2 Lake Park',
            children: [
              {
                key: 1311,
                name: 'Jim Green jr.',
                age: 25,
                address: 'London No. 3 Lake Park',
              },
              {
                key: 1312,
                name: 'Jimmy Green sr.',
                age: 18,
                address: 'London No. 4 Lake Park',
              },
            ],
          },
        ],
      },
    ],
  },
  {
    key: 2,
    name: 'Joe Black',
    age: 32,
    address: 'Sydney No. 1 Lake Park',
  },
];

const EditableContext = React.createContext(null);

const EditableRow = ({ index, ...props }) => {
  const [form] = Form.useForm();
  return (
    <Form form={form} component={false}>
      <EditableContext.Provider value={form}>
        <tr {...props} />
      </EditableContext.Provider>
    </Form>
  );
};

const EditableCell = ({
  title,
  editable,
  children,
  dataIndex,
  record,
  handleSave,
  ...restProps
}) => {
  const [editing, setEditing] = useState(false);
  const inputRef = useRef(null);
  const form = useContext(EditableContext);

  useEffect(() => {
    if (editing) {
      inputRef.current?.focus();
    }
  }, [editing]);

  const toggleEdit = () => {
    setEditing(!editing);
    form.setFieldsValue({
      [dataIndex]: record[dataIndex],
    });
  };

  const save = async () => {
    try {
      const values = await form.validateFields();
      toggleEdit();
      handleSave({
        ...record,
        ...values,
      });
    } catch (errInfo) {
      console.log('Save failed:', errInfo);
    }
  };

  let childNode = children;
  if (editable) {
    childNode = editing ? (
      <Form.Item
        style={{ margin: 0 }}
        name={dataIndex}
        rules={[{ required: true, message: `${title} is required.` }]}
      >
        <Input ref={inputRef} onPressEnter={save} onBlur={save} />
      </Form.Item>
    ) : (
      <div
        className="editable-cell-value-wrap"
        style={{ paddingRight: 24 }}
        onClick={toggleEdit}
      >
        {children}
      </div>
    );
  }
  return <td {...restProps}>{childNode}</td>;
};

const App = () => {
  const [treeData, setTreeData] = useState(data);

  // 递归更新树形数据中的指定节点
  const updateTreeData = (list, key, updatedObj) => {
    return list.map(item => {
      if (item.key === key) {
        return { ...item, ...updatedObj };
      }
      if (item.children) {
        return { ...item, children: updateTreeData(item.children, key, updatedObj) };
      }
      return item;
    });
  };

  const handleSave = (updatedRecord) => {
    setTreeData(prev => updateTreeData(prev, updatedRecord.key, updatedRecord));
  };

  // 为可编辑列配置onCell,传递必要参数
  const editableColumns = columns.map(col => {
    if (!col.editable) return col;
    return {
      ...col,
      onCell: (record) => ({
        record,
        editable: col.editable,
        dataIndex: col.dataIndex,
        title: col.title,
        handleSave,
      }),
    };
  });

  const components = {
    body: {
      cell: EditableCell,
    },
    treeRow: EditableRow, // 树形表格使用treeRow作为行组件
  };

  return (
    <>
      <Table
        columns={editableColumns}
        components={components}
        dataSource={treeData}
        pagination={false}
        treeDefaultExpandAll // 默认展开所有树形节点,方便测试
      />
    </>
  );
};

export default App;

说明

  • 组件配置修正:components中指定treeRow: EditableRow,这是树形表格行组件的正确配置项,普通表格用body.row,树形表格必须用treeRow
  • 树形数据更新:updateTreeData函数通过递归遍历树形结构,找到对应key的节点并更新,确保修改能正确反映到数据源中
  • 参数传递:通过onCell将handleSave等必要参数传递给EditableCell,保证编辑完成后能触发保存逻辑

内容的提问来源于stack exchange,提问作者Deep Kumar Singh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 01:53:00