结合antd3的表单写出一个自定义的Form表单控件-可编辑表格

579 阅读2分钟

antd3官网的表单介绍中,自定义的表单组件必须要传一个value跟onChange方法,才能和表单联动数据

官网的自定义表单控件:3x.ant.design/components/…

  • 要注意的是,官网的写法有两种,示例写法是class类组件的写法,函数组件有些不同,需要加ref。其实这个很简单,只需要看一眼别人怎么写的就能写出来,我这里是函数组件的写法,复用性不高,就是记录一下。
import { Table, Input, Button, Alert } from 'antd';
import React, { useState } from 'react';
import styles from './index.scss';

let editFlag = false;

function EditableTable({ value = {}, onChange }, ref) {
  const [state, setState] = useState(() => {
    let arr = [];
    if (value && Object.keys(value).length !== 0) {
      let count = 0;
      for (const k in value) {
        arr.push({
          key: k,
          value: value[k],
          valueEdit: false,
          keyEdit: false,
          id: count
        });
        count++;
      }
      return arr;
    }
    return arr;
  });

  const [warningVisible, setWarningVisible] = useState(false);

  const columns = [
    {
      title: 'Key',
      dataIndex: 'key',
      align: 'center',
      ellipsis: true,
      render: (text, record, index) => {
        // console.log(record, index);
        return (
          <div style={{ width: '100%', height: '100%' }}>
            {record.keyEdit ? (
              <Input
                autoFocus
                style={{ width: '100%' }}
                value={text}
                onChange={(e) => {
                  const v = e.target.value;
                  v.trim() === ''
                    ? setWarningVisible(true)
                    : setWarningVisible(false);
                  handleChange(v, index, 'key');
                }}
                onPressEnter={(e) =>
                  handleOver(e, index, record.keyEdit, 'key')
                }
                onBlur={(e) => handleOver(e, index, record.keyEdit, 'key')}
              ></Input>
            ) : (
              <div
                style={{ width: '100%', height: '100%' }}
                onClick={() => {
                  if (editFlag) {
                    return;
                  }
                  editFlag = true;
                  toggleEdit(index, record.keyEdit, 'key');
                }}
              >
                {text}
              </div>
            )}
          </div>
        );
      }
    },
    {
      title: 'Value',
      dataIndex: 'value',
      align: 'center',
      ellipsis: true,
      render: (text, record, index) => {
        // console.log(record, index);
        return (
          <div style={{ width: '100%', height: '100%' }}>
            {record.valueEdit ? (
              <Input
                autoFocus
                style={{ width: '100%' }}
                value={text}
                onChange={(e) => {
                  const v = e.target.value;
                  v.trim() === ''
                    ? setWarningVisible(true)
                    : setWarningVisible(false);
                  handleChange(v, index, 'value');
                }}
                onPressEnter={(e) =>
                  handleOver(e, index, record.valueEdit, 'value')
                }
                onBlur={(e) => handleOver(e, index, record.valueEdit, 'value')}
              ></Input>
            ) : (
              <div
                style={{ width: '100%', height: '100%' }}
                onClick={() => {
                  if (editFlag) {
                    return;
                  }
                  editFlag = true;
                  toggleEdit(index, record.valueEdit, 'value');
                }}
              >
                {text}
              </div>
            )}
          </div>
        );
      }
    },
    {
      title: '操作',
      align: 'center',
      render: (text, record, index) => (
        <Button type="link" onClick={() => handleDelete(index)}>
          删除
        </Button>
      )
    }
  ];

  // 增
  const handleAdd = () => {
    setState([
      ...state,
      {
        key: 'initialKey',
        value: 'initialValue',
        valueEdit: false,
        keyEdit: true
      }
    ]);
  };

  //删
  const handleDelete = (index) => {
    setState(state.filter((item, i) => i !== index));
    triggerChange();
  };

  // 切换编辑状态
  const toggleEdit = (i, editing, type) => {
    // console.log(i, editing);
    type === 'value'
      ? setState(
          state.map((item, index) =>
            index === i ? { ...item, valueEdit: !editing } : item
          )
        )
      : setState(
          state.map((item, index) =>
            index === i ? { ...item, keyEdit: !editing } : item
          )
        );
  };

  // inputChange
  const handleChange = (v, i, type) => {
    type === 'value'
      ? setState(
          state.map((item, index) =>
            index === i ? { ...item, value: v } : item
          )
        )
      : setState(
          state.map((item, index) => (index === i ? { ...item, key: v } : item))
        );
  };

  const handleOver = (e, index, editing, type) => {
    const v = e.target.value;
    if (v.trim() === '') {
      e.target.focus();
      return setWarningVisible(true);
    }
    setWarningVisible(false);
    toggleEdit(index, editing, type);
    triggerChange();
    editFlag = false;
  };

  // changeIncomingValue
  const triggerChange = () => {
    let newValue = {};
    state.forEach((item) => {
      newValue[item.key] = item.value;
    });
    console.log(newValue);
    onChange && onChange(newValue);
  };

  return (
    <div ref={ref} className={styles.root}>
      <Button
        onClick={handleAdd}
        type="primary"
        style={{ margin: '0 16px 16px' }}
      >
        添加
      </Button>
      {warningVisible ? (
        <Alert message="值不可以为空!" type="error" showIcon />
      ) : (
        ''
      )}
      <Table
        pagination={false}
        bordered={true}
        dataSource={state}
        columns={columns}
        rowKey={(record, i) => i}
      />
    </div>
  );
}

export default React.forwardRef(EditableTable);

css:

.root {
  :global {
    .ant-alert {
      display: inline-block;
      width: auto;
      font-size: 12px;
    }
    .ant-table-row {
      td {
        padding: 0 !important;
        height: 50px;
        line-height: 50px;
        overflow: hidden;
      }
    }
  }
}

使用:

                              <Form.Item>
                                {getFieldDecorator('authHeaders', {
                                  initialValue:
                                    state.dataAccessInfo?.content?.authInfo
                                      ?.headers || {}
                                })(<EditableTable></EditableTable>)}
                              </Form.Item>