antd React 实现可编辑表格(v1)

在项目中遇到了很多可编辑表格,这里做个总结:

先说第一种实现方式:

antd自带的可编辑表格进行改进

代码如下:

import React, { useContext, useState, useEffect } from 'react';
import {
  Table,
  Input,
  Popconfirm,
  Form,
  Tooltip,
  Select,
  DatePicker,
  TimePicker,
} from 'antd';
import moment from 'moment';
import { PlusOutlined } from '@ant-design/icons';

const budget = [
  {
    key: '0',
    name: 'flower',
    budget: '32',
    desc: moment('2021-08-27'),
  },
  {
    key: '1',
    name: 'ball',
    budget: '32',
    desc: moment('2021-08-17'),
  },
];
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,
  rules,
  handleSave,
  valueType,
  ...restProps
}) => {
  const form = useContext(EditableContext);
  useEffect(() => {
    form.setFieldsValue({
      [dataIndex]: record?.[dataIndex],
    });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

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

  // 这里可以自行扩展
  const renderNode = (valueType) => {
    switch (valueType) {
      case 'input':
        return <Input onPressEnter={save} onBlur={save} />;
      case 'select':
        return (
          <Select allowClear onBlur={save}>
            <Select.Option key="1" value={1}>
              1
            </Select.Option>
            <Select.Option key="2" value={2}>
              2
            </Select.Option>
          </Select>
        );
      case 'date':
        return <DatePicker onBlur={save} />;
      case 'dateRange':
        return <DatePicker.RangePicker onBlur={save} />;
      case 'time':
        return <TimePicker onBlur={save} />;
      default:
        break;
    }
  };
  let childNode = valueType ? (
    <Form.Item
      style={{
        margin: 0,
      }}
      name={dataIndex}
      rules={
        rules || [
          {
            required: true,
            message: `${title} 不能为空.`,
          },
        ]
      }
    >
      {renderNode(valueType)}
    </Form.Item>
  ) : (
    children
  );
  return <td {...restProps}>{childNode}</td>;
};

export default function EditTable({ value = budget, onChange }) {
  const [dataSource, setDataSource] = useState(value);
  const columns = [
    {
      title: 'name',
      dataIndex: 'name',
      width: '30%',
      editable: true,
      valueType: 'select',
      rules: [{ required: true, message: '测试' }],
    },
    {
      title: 'budget',
      dataIndex: 'budget',
      editable: true,
      valueType: 'input',
    },
    {
      title: 'describe',
      dataIndex: 'desc',
      editable: true,
      valueType: 'date',
    },
    {
      title: 'operation',
      dataIndex: 'operation',
      render: (_, record) =>
        dataSource.length >= 1 ? (
          <Tooltip placement="bottom" title="删除当前行">
            <Popconfirm
              title="确认删除?"
              onConfirm={() => handleDelete(record.key)}
            >
              <a>link</a>
            </Popconfirm>
          </Tooltip>
        ) : null,
    },
  ];

  const handleDelete = (key) => {
    setDataSource(dataSource.filter((item) => item.key !== key));
    onChange?.(dataSource.filter((item) => item.key !== key));
  };

  const handleAdd = () => {
    const newData = {
      key: new Date().getTime(),
      name: '-',
      budget: '-',
      desc: '-',
    };
    setDataSource([...dataSource, newData]);
    onChange?.([...dataSource, newData]);
  };

  const handleSave = (row) => {
    const newData = [...dataSource];
    const index = newData.findIndex((item) => row.key === item.key);
    const item = newData[index];
    newData.splice(index, 1, { ...item, ...row });
    setDataSource(newData);
    onChange?.(newData);
  };

  const components = {
    body: {
      row: EditableRow,
      cell: EditableCell,
    },
  };
  const newColumns = columns.map((col) => {
    if (!col.editable) {
      return col;
    }
    return {
      ...col,
      onCell: (record) => ({
        record,
        editable: col.editable,
        dataIndex: col.dataIndex,
        title: col.title,
        valueType: col.valueType,
        rules: col.rules,
        handleSave: handleSave,
      }),
    };
  });

  useEffect(() => {
    onChange?.([...dataSource]);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);
  return (
    <div>
      <Table
        components={components}
        rowClassName="rules_edit_row"
        rowKey={(record) => record.key}
        bordered
        pagination={false}
        showHeader={false}
        dataSource={dataSource}
        columns={newColumns}
      />
      <span
        style={{
          color: '#3880ff',
          cursor: 'pointer',
          position: 'relative',
          top: 6,
        }}
        onClick={handleAdd}
      >
        <PlusOutlined />
        添加
      </span>
    </div>
  );
}

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

superTiger_y

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值