antd 4.0 表单使用

837 阅读1分钟

先上图,功能很简单,记录分享

之前在开发中,基本都是使用antd3.x 版本,冷不丁切换到4.x 版本,有些不适应

开始

功能看起来是很简单的,主要使用了Form、Form.List,其中使用了ahooks中的useDynamicList

对比antd3.x版本,4.x版本 form

  1. 取消了绑定函数,采用name(依然支持嵌套)
  2. 4,x修改表单不会再触发表单的更新,需要手动控制更新
  3. 4.x增加了Form.List,将一些逻辑封装到了一起
  4. ......

具体代码,可以查看github

show code

// 部分代码
const EditForm: React.FC<Props> = () => {
  const [params, setParams] = useState({});
  const [editStatus, setEditStatus] = useState<boolean>(false);

  const { list, push, remove, getKey, resetList } = useDynamicList<ListItem>(
    defaultList,
  );

  const [form] = Form.useForm();

  // 提交
  const submit = () => {
    form.validateFields().then(values => {
      setParams(values);
    });
  };
  // 设置初值, 多条Form.List 需要动态设置初值
  useEffect(() => {
    if (editStatus) {
      resetList(editData);
      const grades: FormValues['grades'] = [];
      editData.forEach(item => {
        const teams: ListItem['teams'] = [];
        item.teams.forEach(routerItem =>
          teams.push({
            ...routerItem,
            teamName: item.teamName,
            teamPosition: item.teamPosition,
          }),
        );
        grades.push({
          class: item.class,
          teams,
        });
      });
      form.setFieldsValue({ grades: grades });
    } else {
      form.setFieldsValue({ grades: defaultList });
      resetList(defaultList);
    }
  }, [editStatus]);

  // 添加TabPane
  const addTabPane = () => {
    push({
      title: '',
      teams: [
        {
          axis: [{}],
        },
      ],
    });

    // 给表单添加默认值
    const values = form.getFieldValue('grades');
    values.push({
      teams: [
        {
          axis: [{}],
        },
      ],
    });
    form.setFieldsValue({ grades: values });
  };

  const layout = {
    labelCol: { span: 2 },
    wrapperCol: { span: 14 },
  };

  return (
    <>
      <Button
        style={{ marginBottom: 8 }}
        type="primary"
        onClick={() => setEditStatus(!editStatus)}
      >
        切换{editStatus ? '新增' : '编辑'}
      </Button>
      <Form {...layout} labelCol={{ span: 2 }} form={form}>
        <Tabs
          type="editable-card"
          hideAdd
          tabBarExtraContent={<Button onClick={addTabPane}>添加</Button>}
        >
          {list.map((_, index) => (
            <TabPane
              forceRender
              key={`${getKey(index)}`}
              closeIcon={<CloseOutlined onClick={() => remove(index)} />}
              tab={`班级 ${getKey(index) + 1}`}
            >
              <Item
                {...layout}
                label="班级"
                name={['grades', getKey(index), 'class']}
                rules={[
                  {
                    required: true,
                    message: '请填写班级',
                  },
                ]}
              >
                <Input placeholder="请填写班级" />
              </Item>
              <FormList parentIndex={getKey(index)} />
            </TabPane>
          ))}
        </Tabs>
      </Form>
      <Button
        type="primary"
        style={{ marginTop: 8, width: '100%' }}
        onClick={submit}
      >
        提交
      </Button>
      {Object.keys(params).length > 0 && (
        <pre>{JSON.stringify(params, null, '\t')}</pre>
      )}
    </>
  );
};

export default EditForm;

自定义FormList

const AxisFormList: React.FC<AxisFormListProps> = ({
  parentIndex,
  collapseIndex,
}) => {
  return (
    <List {...layout} name={[collapseIndex, 'axis']}>
      {(fields, { add, remove }) => (
        <>
          {fields.map(routerItem => (
            <Row style={{ width: '100%' }} key={routerItem.key}>
              <Item
                style={{ width: '40%' }}
                rules={[
                  {
                    required: true,
                    message: '请选择',
                  },
                  ({ getFieldValue }) => ({
                    validator(rule, value) {
                        // 自定义校验名称重复
                        if (!value) {
                            return Promise.resolve();
                        }
                      const axis = getFieldValue([
                          'grades',
                      parentIndex,
                      'teams',
                      collapseIndex,
                      'axis']).map((item: AxisItem) => item.row) as string[];
                      const index = axis.findIndex(item => item === value)
                      axis.splice(index, 1)
                      let infoPromise = Promise.resolve();
                      axis.forEach((checkItem: string) => {
                          if (checkItem === value) {
                            infoPromise = Promise.reject('排名称重复');
                          }
                      })
                      return infoPromise;
                    },
                  }),
                ]}
                {...routerItemLayout}
                label="排"
                initialValue=""
                fieldKey={[routerItem.fieldKey, 'row']}
                name={[routerItem.fieldKey, 'row']}
              >
                <Select>
                  <Select.Option value="one">第一排</Select.Option>
                  <Select.Option value="two">第二排</Select.Option>
                  <Select.Option value="three">第三排</Select.Option>
                </Select>
              </Item>
              <Item shouldUpdate noStyle>
               {/* 4.x触发更新需要包一层Item  */}
                {({ getFieldValue }) => {
                  const getDefaultValue =
                    getFieldValue([
                      'grades',
                      parentIndex,
                      'teams',
                      collapseIndex,
                      'axis',
                      routerItem.fieldKey,
                      'row',
                    ]) === 'three';
                  return (
                    <Item
                      style={{ width: '40%' }}
                      rules={[
                        {
                          required: !getDefaultValue && true,
                          message: '请填写',
                        },
                      ]}
                      {...routerItemLayout}
                      label="列"
                      fieldKey={[routerItem.fieldKey, 'column']}
                      name={[routerItem.fieldKey, 'column']}
                    >
                      <Input
                        disabled={getDefaultValue}
                      />
                    </Item>
                  );
                }}
              </Item>
              <Item>
                <DeleteOutlined onClick={() => remove(routerItem.name)} />
              </Item>
            </Row>
          ))}
          <Button
            type="dashed"
            style={{ marginTop: 8, width: '50%' }}
            onClick={() => add()}
          >
            添加
          </Button>
        </>
      )}
    </List>
  );
};

const FormList: React.FC<Props> = ({ parentIndex }) => {
  return (
    <List {...layout} name={['grades', parentIndex, 'teams']}>
      {(fields, { add, remove }) => (
        <>
          {fields.map((collapseItem, collapseIndex) => (
            <Collapse
              style={{ marginBottom: 24 }}
              defaultActiveKey={[`${collapseItem.fieldKey}`]}
              key={collapseItem.fieldKey}
            >
              <Panel
                forceRender
                extra={
                  <CloseOutlined onClick={() => remove(collapseItem.name)} />
                }
                header={
                  <Item
                    style={{
                      marginBottom: 0,
                      width: '50%',
                      display: 'inline-flex',
                    }}
                    rules={[
                      {
                        required: true,
                        message: '请填写小组名称',
                      },
                    ]}
                    {...routerItemLayout}
                    label="小组名称"
                    name={[collapseItem.name, 'teamName']}
                    fieldKey={[collapseItem.fieldKey, 'teamName']}
                  >
                    <Input
                      placeholder="请填写小组名称"
                      onClick={e => e.stopPropagation()}
                      addonBefore="小组名称"
                    />
                  </Item>
                }
                key={`${collapseItem.fieldKey}`}
              >
                <Item
                  rules={[
                    {
                      required: true,
                      message: '请填写小组位置',
                    },
                  ]}
                  {...layout}
                  label="小组位置"
                  name={[collapseItem.name, 'teamPosition']}
                  fieldKey={[collapseItem.fieldKey, 'teamPosition']}
                >
                  <Input placeholder="请填写小组位置" />
                </Item>
                <Item
                  style={{ position: 'absolute' }}
                  initialValue="NONE"
                  {...layout}
                  name={[collapseIndex, 'type']}
                >
                  <Input hidden />
                </Item>
                <AxisFormList
                  parentIndex={parentIndex}
                  collapseIndex={collapseIndex}
                />
              </Panel>
            </Collapse>
          ))}
          <Button
            type="dashed"
            style={{ marginTop: 8, width: '100%' }}
            onClick={() =>
              add({
                axis: [{}],
              })
            }
          >
            添加
          </Button>
        </>
      )}
    </List>
  );
};

export default FormList;

结束

  1. react地址
  2. antd地址
  3. ahooks地址