关于 Ant Design 中单行多列表单布局教程

1,694 阅读5分钟

Ant Design 目前被广泛应用于企业级后台项目,甚至会被作为对外站点的 UI 库选择,构建 C 端业务。

目前我所在的公司就是使用 Ant Design 来构建网页。开发过程中比较常用的一些组件和它的使用方式,我也写了几篇文章,有兴趣的读者可以阅读:

  1. 如何基于 Ant Design <Form> 创建动态表单?
  2. 关于 Ant Design 上传组件 <Upload> 的入门教程
  3. 关于 Ant Design 选择组件 <Select> 的入门教程

本期我们将继续这个系列,讨论的对象是关于如何实现单行多列表单布局。如果你经常跟表单页面打交道,那么对这个布局方式应该并不陌生。

老规矩,我们先从创建项目开始。

创建项目

使用 Vite 的 React 模板。

npm create vite@latest my-react-app -- --template react

Scaffolding project in /Users/xt02121/pg/my-react-app...

Done. Now run:

  cd my-react-app
  npm install
  npm run dev

进入项目,安装依赖:

cd my-react-app
npm install
npm run dev

使用 VS Code 打开项目:

code .

安装 @vitejs/plugin-react-swc 依赖,加快项目编译(可选):

npm i -D @vitejs/plugin-react-swc
// vite.config.js
import { defineConfig } from 'vite'
+ import react from '@vitejs/plugin-react-swc'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()]
})

安装 Ant Design 依赖:

npm install antd

删除 src/index.css 中的内容,src/App.jsx 改成下面这样:

import { FormDemo } from './FormDemo'

function App() {

  return (
    <FormDemo />
  )
}

export default App

创建 FormDemo.jsx

import { Form, Input, Button } from 'antd'

export function FormDemo() {
  return <Form style={{ width: 600 }}>
    <Form.Item label="用户名">
      <Input placeholder="请输入用户名" />
    </Form.Item>
    <Form.Item label="密码">
      <Input.Password placeholder="请输入密码" />
    </Form.Item>
    <Form.Item>
      <Button htmlType="submit">登录</Button>
    </Form.Item>
  </Form>
}

查看效果:

接下来进入正文,来看看 Ant Design 中如何实现单行多列的表单布局。

<Form> layout="inline"

<Form> 提供了一个 layout prop,可以帮助我们最快实现行内多列布局。

- <Form style={{ width: 600 }}>
+ <Form layout='inline' style={{ width: 600 }}>

效果如下:

你会发现,设置成行业布局以后,每一个 .ant-form-item 会设置 margin-inline-end: 16px; 外边距,在默认文档流下,就是右外边距。

<Space size={16}>

当然,在不设置 <Form> layout prop 情况下,我们还可以通过 <Space> 控制表单项在一行展示。

- import { Form, Input, Button } from 'antd'
+ import { Form, Input, Button, Space } from 'antd'

<Form style={{ width: 600 }}>
+ <Space size={16}>
  {/* ... */}
+ </Space>
<Form>

移除 layout prop,同时将 <Form.Item> 们包裹在 <Space> 之中(size 指定项目间的间隔,这里设置成 16px)。

查看效果:

发现与上面一样。可以看到,<Space> 其实就是一个 Flex 容器,其中的元素默认就是横向排列的,设置的 size 其实就是在指定 gap 属性,约定项目间的间隔。

当然,有时还会遇到一个表单字段是由多个输入框构成的情况。

这个时候,就要搭配 <Form.Item label> + <From.Item noStyle> 一起使用了。

<Form.Item label="BirthDate" style={{ marginBottom: 0 }}>
  <Space size={16}>
    <Form.Item name="year" noStyle rules={[{ required: true }]}>
      <Input placeholder="Input birth year" />
    </Form.Item>
    <Form.Item name="month" noStyle rules={[{ required: true }]}>
      <Input placeholder="Input birth month" />
    </Form.Item>
  </Space>
</Form.Item>

没有 name 和 rules 的 <Form.Item label="BirthDate"> 就只是单纯用来展示 Label 标签的;而设置了 noStyle<Form.Item> 不会实际渲染 HTML 结构,但依然会保留表单字段收集和校验的能力。

<Form.Item label><From.Item noStyle> 之间的 <Space> 则是用来保证两个输入字段可以一行排列。

<Space.Compact>

除了 <Space>,还有 <Space.Compact> 帮助我们实现更加紧凑的表单布局。

<Space.Compact>
  <Form.Item>
    <Input placeholder="请输入用户名" />
  </Form.Item>
  <Form.Item>
    <Input.Password placeholder="请输入密码" />
  </Form.Item>
  <Form.Item>
    <Button htmlType="submit">登录</Button>
  </Form.Item>
</Space.Compact>

查看效果:

发现表单是一行排列了,但表单之间的分割线会有点粗。这是因为 <Form.Item> 中的元素默认是有边框样式的,那么拼在一起自然会多出 1 像素的粗度。

这个问题可以通过给 <Form.Item> 设置 noStyle prop 搞定:

<Space.Compact>
  <Form.Item noStyle>
    <Input placeholder="请输入用户名" />
  </Form.Item>
  <Form.Item noStyle>
    <Input.Password placeholder="请输入密码" />
  </Form.Item>
  <Form.Item noStyle>
    <Button htmlType="submit">登录</Button>
  </Form.Item>
</Space.Compact>

<Form.Item> 设置 noStyle 后,最终渲染出来的 HTML 中不包含 <Form.Item> 的结构(不过 <Form.Item> 的校验逻辑还会保留),相当于把内部元素的结构暴露出来了。

效果如下:

发现元素之间的边线变成 1 恢复正常了。

这是因为除第一个和最后一个的元素的边框都设置成 0 了。

<Row><Col> vs <Space>

不过使用 <Space> 实现多表单一行布局会有个限制,就是即便表单容器很宽,但内部输入框的宽度也不会自动扩展到容器的边界。

以以下代码为例:

<Form>
  <Form.Item label="地址">
    <Space size={16}>
      <Form.Item
        name={["address", "province"]}
        noStyle
        rules={[{ required: true, message: "Province is required" }]}
      >
        <Select placeholder="Select province">
          <Select.Option value="Zhejiang">Zhejiang</Select.Option>
          <Select.Option value="Jiangsu">Jiangsu</Select.Option>
        </Select>
      </Form.Item>

      <Form.Item
        name={["address", "street"]}
        noStyle
        rules={[{ required: true, message: "Street is required" }]}
      >
        <Input placeholder="Input street" />
      </Form.Item>
    </Space>
  </Form.Item>
</Form>

渲染效果如下:

但实际内容区宽度占据了整个剩余的宽度的:

这个就可以考虑使用 <Row><Col> 了。

<Form.Item label="地址">
  <Row gutter={16}>
    <Col span={12}>
      <Form.Item
        name={["address", "province"]}
        noStyle
        rules={[{ required: true, message: "Province is required" }]}
      >
        <Select placeholder="Select province">
          <Select.Option value="Zhejiang">Zhejiang</Select.Option>
          <Select.Option value="Jiangsu">Jiangsu</Select.Option>
        </Select>
      </Form.Item>
    </Col>
    <Col span={12}>

      <Form.Item
        name={["address", "street"]}
        noStyle
        rules={[{ required: true, message: "Street is required" }]}
      >
        <Input placeholder="Input street" />
      </Form.Item>
    </Col>
  </Row>
</Form.Item>
  1. <Space size={16}> 替换为 <Row gutter={16}>,同时
  2. 使用 <Col> 包裹每一个 <Form.Item>,同时指定 span prop。Ant Design 设计系统将每一行 <Row> 分成 24 等份,<Col span={12}> 就表示当前的 <Col> 占据 1/2 行宽。

效果如下:

至此,我们就实现了能够填充预留宽度的多列布局方式。

总结

本文我讲解了如何在 Ant Design 中实现单行多列表单的布局方式。包括:<Form> layout="inline"、<Space size={16}><Space.Compact> 以及 <Row><Col>

  1. <Form> layout="inline" 是最简单的实现方式
  2. <Space size={16}><Space.Compact> 则是用来实现局部字段一行布局
  3. 当你期望表单能够自动扩展占据剩余宽度的效果时,可以考虑 <Row><Col> 的组织方式

从 1 到 3,组织方式是一点点复杂的,而灵活度是慢慢增加的,你可以根据实际场景需要选择合适的方法。

好了,希望本文的介绍对你的工作能有所帮助。感谢你的阅读,再见。