#ProFormFields 表单项
一个表单除了 Form 之外还是需要一系列的表单项,ProForm 自带了数量可观的表单项,这些组件本质上是 Form.Item 和 组件的结合,我们可以把他们当成一个 FormItem 来使用,并且支持各种 props。每个表单项都支持 fieldProps 属性来支持设置输入组件的props。 我们支持了 placeholder 的透传,你可以直接在组件上设置 placeholder。
每个表单项同时也支持了 readonly ,不同的组件会有不同的只读样式,与 disable 相比 readonly 展示更加友好。生成的 dom 也更小,比如 ProFormDigit 会自动格式化小数位数。
ProFormText 是 FormItem + Input 的产物,可以类比于以下的代码:
const ProFormText = (props) => {
return (
<ProForm.Item {...props}>
<Input placeholder={props.placeholder} {...props.fieldProps} />
</ProForm.Item>
);
};所以我们给 ProFormText 设置的 props 其实是 Form.Item 的,fieldProps 才是包含的组件的,要切记。
#组件列表
| 组件 | 使用场景 |
|---|---|
| ProFormText | 用于输入各类文本 |
| ProFormDigit | 用于输入数字,它自带了一个格式化 (保留 2 位小数,最小值为 0),有需要你可以关掉它。 |
| ProFormText.Password | 用于输入密码 |
| ProFormTextArea | 用于输入多行文本 |
| ProFormCaptcha | 用于输入验证码, 一般需要与发送验证码接口一起使用 |
| ProFormDatePicker | 日期选择器用于输入日期 |
| ProFormDateTimePicker | 日期 + 时间选择器,用于输入日期和时间 |
| ProFormDateRangePicker | 日期区间选择器用于输入一个日期区间 |
| ProFormDateTimeRangePicker | 日期 + 时间区间选择器,用于输入一个日期 + 时间的区间 |
| ProFormSelect | 支持 request 和 valueEnum 两种方式来生成子项,用于从两项以上中选择一项 |
| ProFormTreeSelect | 支持 request 和 valueEnum 两种方式来生成子项,用于从两项以上中选择一项 |
| ProFormCheckbox | 在 Checkbox 基础上支持了 layout,也支持 request 和 valueEnum 两种方式来生成子项 |
| ProFormRadio.Group | 在 Radio 基础上也支持 request 和 valueEnum 两种方式来生成子项,用于单选某项,但是可以展示出来所有选项。 |
| ProFormSlider | 当用户需要在数值区间 / 自定义区间内进行选择时,可为连续或离散值。 |
| ProFormSwitch | 用于输入互斥的两个选项,一般是 true 和 false |
| ProFormUploadButton | 按钮样式的上传文件 |
| ProFormUploadDragger | 区域的上传文件,一般用于突出上传文件的表单中 |
| ProFormMoney | 通用金额输入组件 |
| ProFormSegmented | 分段控制器 |
#代码示例
import {
ProForm,
ProFormCascader,
ProFormCheckbox,
ProFormColorPicker,
ProFormDigit,
ProFormDigitRange,
ProFormGroup,
ProFormRadio,
ProFormRate,
ProFormSegmented,
ProFormSelect,
ProFormSlider,
ProFormSwitch,
ProFormText,
ProFormTreeSelect,
ProFormUploadButton,
ProFormUploadDragger,
} from '@ant-design/pro-components';
import { message, Switch } from 'antd';
import { useState } from 'react';
export const waitTime = (time: number = 100) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(true);
}, time);
});
};
const Demo = () => {
const [readonly, setReadonly] = useState(false);
return (
<div
style={{
padding: 24,
}}
>
<Switch
style={{
marginBlockEnd: 16,
}}
checked={readonly}
checkedChildren="Edit"
unCheckedChildren="Read Only"
onChange={setReadonly}
/>
<ProForm
readonly={readonly}
name="field-set-components-other-demo"
initialValues={{
name: 'demo_user',
password: 'demo123',
select: 'china',
select2: '520000201604258831',
useMode: { label: 'Unresolved', value: 'open', key: 'open' },
'select-multiple': ['green', 'blue'],
radio: 'a',
'radio-vertical': 'b',
'radio-button': 'b',
'checkbox-group': ['A', 'B', 'C'],
'input-number-range': [2, 4],
'input-number': 3,
switch: true,
slider: 66,
rate: 3.5,
segmented: 'open',
segmented2: 'open',
treeSelect: [
{
title: 'Child Node3',
value: '0-1-0',
},
],
}}
onFinish={async () => {
message.success('Submitted');
}}
>
<ProFormGroup title="Text Types">
<ProFormText width="md" name="name" label="Name" />
<ProFormText.Password width="md" name="password" label="Password" />
</ProFormGroup>
<ProFormGroup
title="Selection Types"
collapsible
style={{
gap: '0 32px',
}}
>
<ProFormSelect
name="select"
label="Select"
valueEnum={{
china: 'China',
usa: 'U.S.A',
}}
placeholder="Please select a country"
rules={[{ required: true, message: 'Please select your country!' }]}
/>
<ProFormSelect
name="select2"
label="Select with Search"
showSearch
debounceTime={300}
request={async ({ keyWords }) => {
await waitTime(100);
return [
{ value: keyWords, label: 'Target' },
{ value: '520000201604258831', label: 'Patricia Lopez' },
{ value: '520000198509222123', label: 'Jose Martinez' },
{ value: '210000200811194757', label: 'Elizabeth Thomas' },
{ value: '530000198808222758', label: 'Scott Anderson' },
{ value: '500000198703236285', label: 'George Jackson' },
{ value: '610000199906148074', label: 'Linda Hernandez' },
{ value: '150000197210168659', label: 'Sandra Hall' },
];
}}
placeholder="Please select a country"
rules={[{ required: true, message: 'Please select your country!' }]}
/>
<ProFormSelect
width="md"
fieldProps={{
labelInValue: true,
}}
request={async () => [
{ label: 'All', value: 'all' },
{ label: 'Unresolved', value: 'open' },
{ label: 'Resolved', value: 'closed' },
{ label: 'In Progress', value: 'processing' },
]}
name="useMode"
label="Contract Agreed Effective Method"
/>
<ProFormSelect
name="select-multiple"
label="Select[multiple]"
valueEnum={{
red: 'Red',
green: 'Green',
blue: 'Blue',
}}
fieldProps={{
mode: 'multiple',
}}
placeholder="Please select favorite colors"
rules={[
{
required: true,
message: 'Please select your favorite colors!',
type: 'array',
},
]}
/>
<ProFormCascader
label="Address"
request={async () => [
{
value: 'zhejiang',
label: 'Zhejiang',
children: [
{
value: 'hangzhou',
label: 'Hangzhou',
children: [
{
value: 'xihu',
label: 'West Lake',
},
],
},
],
},
{
value: 'jiangsu',
label: 'Jiangsu',
children: [
{
value: 'nanjing',
label: 'Nanjing',
children: [
{
value: 'zhonghuamen',
label: 'Zhong Hua Men',
},
],
},
],
},
]}
fieldProps={{
changeOnSelect: true,
}}
name="area"
/>
<ProFormTreeSelect
name="treeSelect"
label={'TreeSelect Request'}
placeholder="Please tree select"
allowClear
width={330}
secondary
request={async () => {
return [
{
title: 'Node1',
value: '0-0',
children: [
{
title: 'Child Node1',
value: '0-0-0',
},
],
},
{
title: 'Node2',
value: '0-1',
children: [
{
title: 'Child Node3',
value: '0-1-0',
},
{
title: 'Child Node4',
value: '0-1-1',
},
{
title: 'Child Node5',
value: '0-1-2',
},
],
},
];
}}
fieldProps={{
suffixIcon: null,
showSearch: {
autoClearSearchValue: true,
filterTreeNode: true,
treeNodeFilterProp: 'title',
onSearch: (value) => {
console.log('onSearch', value);
},
},
popupMatchSelectWidth: false,
labelInValue: true,
multiple: true,
fieldNames: {
label: 'title',
},
}}
/>
<ProFormRadio.Group
name="radio"
label="Radio.Group"
options={[
{
label: 'item 1',
value: 'a',
},
{
label: 'item 2',
value: 'b',
},
{
label: 'item 3',
value: 'c',
},
]}
/>
<ProFormRadio.Group
name="radio-vertical"
layout="vertical"
label="Radio.Group"
options={[
{
label: 'item 1',
value: 'a',
},
{
label: 'item 2',
value: 'b',
},
{
label: 'item 3',
value: 'c',
},
]}
/>
<ProFormRadio.Group
name="radio-button"
label="Radio.Button"
radioType="button"
options={[
{
label: 'item 1',
value: 'a',
},
{
label: 'item 2',
value: 'b',
},
{
label: 'item 3',
value: 'c',
},
]}
/>
<ProFormCheckbox.Group
name="checkbox-group"
label="Checkbox.Group"
options={['A', 'B', 'C', 'D', 'E', 'F']}
/>
<ProFormColorPicker label="Color Picker" name="color" />
</ProFormGroup>
<ProFormGroup title="Number Types">
<ProFormDigitRange
label="InputNumberRange"
name="input-number-range"
separator="-"
placeholder={['Min', 'Max']}
separatorWidth={60}
/>
<ProFormDigit
label="InputNumber"
name="input-number"
width="sm"
min={1}
max={10}
/>
<ProFormSwitch name="switch" label="Switch" />
<ProFormSlider
name="slider"
label="Slider"
width="lg"
marks={{
0: 'A',
20: 'B',
40: 'C',
60: 'D',
80: 'E',
100: 'F',
}}
/>
<ProFormRate name="rate" label="Rate" />
<ProFormUploadButton name="pic" label="Upload" />
<ProFormUploadDragger name="drag-pic" label="Drag and Drop Upload" />
<ProFormSegmented
name="segmented"
label="Segmented Control"
valueEnum={{
open: 'Unresolved',
closed: 'Resolved',
}}
/>
<ProFormSegmented
name="segmented2"
label="Segmented Control - Remote Data"
request={async () => [
{ label: 'All', value: 'all' },
{ label: 'Unresolved', value: 'open' },
{ label: 'Resolved', value: 'closed' },
{ label: 'In Progress', value: 'processing' },
]}
/>
</ProFormGroup>
</ProForm>
</div>
);
};
export default Demo;
import { ProForm, ProFormSelect } from '@ant-design/pro-components';
import { Switch } from 'antd';
import { useState } from 'react';
export default () => {
const [readonly, setReadonly] = useState(false);
return (
<div
style={{
padding: 24,
}}
>
<Switch
style={{
marginBlockEnd: 16,
}}
checked={readonly}
checkedChildren="Edit"
unCheckedChildren="Read Only"
onChange={setReadonly}
/>
<ProForm name="search-select-demo" readonly={readonly}>
<ProForm.Group>
<ProFormSelect.SearchSelect
name="userQuery"
label="Query Selector - request"
fieldProps={{
labelInValue: true,
style: {
minWidth: 140,
},
}}
debounceTime={300}
request={async ({ keyWords = '' }) => {
return [
{ label: 'All', value: 'all' },
{ label: 'Unresolved', value: 'open' },
{ label: 'Unresolved (Assigned)', value: 'assignees' },
{ label: 'Resolved', value: 'closed' },
{ label: 'In Progress', value: 'processing' },
].filter(({ value, label }) => {
return value.includes(keyWords) || label.includes(keyWords);
});
}}
/>
<ProFormSelect.SearchSelect
name="userQuery2"
label="Query Selector - valueEnum"
fieldProps={{
style: {
minWidth: 140,
},
}}
valueEnum={{
all: { text: 'All', status: 'Default' },
open: {
text: 'Unresolved',
status: 'Error',
},
closed: {
text: 'Resolved',
status: 'Success',
},
processing: {
text: 'In Progress',
status: 'Processing',
},
}}
/>
<ProFormSelect.SearchSelect
name="userQuery3"
label="Query Selector - options"
fieldProps={{
labelInValue: false,
style: {
minWidth: 140,
},
}}
options={[
{ label: 'All', value: 'all' },
{ label: 'Unresolved', value: 'open' },
{ label: 'Resolved', value: 'closed' },
{ label: 'In Progress', value: 'processing' },
]}
/>
</ProForm.Group>
</ProForm>
</div>
);
};
import {
ProForm,
ProFormDependency,
ProFormFieldSet,
ProFormSelect,
ProFormText,
} from '@ant-design/pro-components';
import { Switch } from 'antd';
import { useState } from 'react';
export default () => {
const [readonly, setReadonly] = useState(false);
return (
<div
style={{
padding: 24,
}}
>
<Switch
style={{
marginBlockEnd: 16,
}}
checked={readonly}
checkedChildren="编辑"
unCheckedChildren="只读"
onChange={setReadonly}
/>
<ProForm
name="field-set-form-fieldset-demo"
readonly={readonly}
initialValues={{
list: ['1', '2', '3'],
}}
>
<ProForm.Item label="互相依赖的表单">
<ProFormDependency name={['list']}>
{({ list }) => {
return <div>{JSON.stringify(list, null, 2)}</div>;
}}
</ProFormDependency>
</ProForm.Item>
<ProFormFieldSet name="list" label="组件列表">
<ProFormText width="md" />
<ProFormSelect
width="md"
request={async () => [
{ label: '全部', value: 'all' },
{ label: '未解决', value: 'open' },
{ label: '已解决', value: 'closed' },
{ label: '解决中', value: 'processing' },
]}
name="useMode"
label="合同约定生效方式"
/>
<ProFormText width="md" />
</ProFormFieldSet>
<ProFormFieldSet
name="list"
label="组件列表- Input.Group"
type="group"
rules={[
{
validator: (_, value) => {
const [name, name1, name2] = value || [];
if (!name) {
throw new Error('第一个值不能为空');
}
if (!name1) {
throw new Error('第二个值不能为空');
}
if (!name2) {
throw new Error('第三个值不能为空');
}
},
},
]}
transform={(value: any) => ({
list: value,
startTime: value[0],
endTime: value[1],
})}
>
<ProFormText width="md" />
<ProFormText width="md" />
<ProFormText width="md" />
</ProFormFieldSet>
<ProFormFieldSet
name="list"
label="组件列表"
transform={(value: any) => ({
list: value,
startTime: value[0],
endTime: value[1],
})}
>
<ProFormText width="md" readonly />
-
<ProFormText width="md" readonly />
-
<ProFormText width="md" readonly />
</ProFormFieldSet>
</ProForm>
</div>
);
};
import {
ProForm,
ProFormDateMonthRangePicker,
ProFormDatePicker,
ProFormDateQuarterRangePicker,
ProFormDateRangePicker,
ProFormDateTimePicker,
ProFormDateTimeRangePicker,
ProFormDateWeekRangePicker,
ProFormDateYearRangePicker,
ProFormTimePicker,
} from '@ant-design/pro-components';
import { Switch } from 'antd';
import dayjs from 'dayjs';
import { useState } from 'react';
import { FIXED_BASE_DATE, FIXED_BASE_TIMESTAMP } from '../../mockData';
/** 与 FIXED_BASE_TIMESTAMP(2024-01-15 10:00)对齐:范围一律「起 ≤ 止」 */
const DAY_MS = 86400000;
const rangeStartTs = FIXED_BASE_TIMESTAMP - DAY_MS;
const rangeEndTs = FIXED_BASE_TIMESTAMP;
export default () => {
const [readonly, setReadonly] = useState(false);
return (
<div
style={{
padding: 24,
}}
>
<Switch
style={{
marginBlockEnd: 16,
}}
checked={readonly}
checkedChildren="编辑"
unCheckedChildren="只读"
onChange={setReadonly}
/>
<ProForm
name="field-set-datatime-demo"
readonly={readonly}
initialValues={{
date: FIXED_BASE_TIMESTAMP,
dateYm: FIXED_BASE_TIMESTAMP,
dateWeek: FIXED_BASE_DATE,
dateMonth: FIXED_BASE_TIMESTAMP,
dateQuarter: FIXED_BASE_TIMESTAMP,
dateYear: FIXED_BASE_TIMESTAMP,
dateTime: FIXED_BASE_TIMESTAMP,
time: '00:01:05',
timeRange: ['05:00:00', '11:00:00'],
dateWeekRange: [dayjs('2024-01-01'), dayjs('2024-01-13')],
dateMonthRange: [dayjs('2024-01-01'), dayjs('2024-03-01')],
dateQuarterRange: [dayjs('2024-01-01'), dayjs('2024-06-30')],
dateYearRange: [dayjs('2022-01-01'), dayjs('2024-01-01')],
dateTimeRange: [rangeStartTs, rangeEndTs],
dateRange: [rangeStartTs, rangeEndTs],
}}
onFinish={async (values) => {
console.log('field-set-datatime-demo submit:', values);
}}
>
<ProForm.Group title="日期相关">
<ProFormDatePicker name="date" label="日期" />
<ProFormDatePicker.Month
name="dateYm"
label="年月"
fieldProps={{
format: 'YY-MM',
}}
/>
<ProFormTimePicker name="time" label="时间" />
<ProFormTimePicker.RangePicker name="timeRange" label="时间范围" />
<ProFormDatePicker.Week name="dateWeek" label="周" />
<ProFormDateWeekRangePicker name="dateWeekRange" label="周范围" />
<ProFormDatePicker.Month name="dateMonth" label="月" />
<ProFormDateMonthRangePicker
name="dateMonthRange"
label="月范围"
/>
<ProFormDatePicker.Quarter name="dateQuarter" label="季度" />
<ProFormDateQuarterRangePicker
name="dateQuarterRange"
label="季度范围"
/>
<ProFormDatePicker.Year name="dateYear" label="年" />
<ProFormDateYearRangePicker name="dateYearRange" label="年范围" />
<ProFormDateTimePicker
name="dateTime"
label="日期时间"
fieldProps={{
showTime: true,
format: 'YYYY-MM-DD HH:mm:ss',
}}
/>
<ProFormDateRangePicker name="dateRange" label="日期范围" />
<ProFormDateTimeRangePicker
name="dateTimeRange"
label="日期时间范围"
/>
</ProForm.Group>
</ProForm>
</div>
);
};
import {
ProForm,
ProFormUploadButton,
ProFormUploadDragger,
} from '@ant-design/pro-components';
const Demo = () => {
return (
<ProForm name="field-set-upload-demo">
<ProFormUploadButton
title="上传按钮的文本变啦"
name="upload"
label="Upload"
max={2}
fieldProps={{
name: 'file',
}}
action="/upload.do"
extra="extra"
/>
<ProFormUploadButton
name="upload"
label="Upload"
max={2}
fieldProps={{
name: 'file',
listType: 'picture-card',
}}
action="/upload.do"
extra="extra"
/>
<ProFormUploadDragger max={4} label="Dragger" name="dragger" />
</ProForm>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
import {
ProForm,
ProFormCheckbox,
ProFormDatePicker,
ProFormDateRangePicker,
ProFormDateTimePicker,
ProFormDateTimeRangePicker,
ProFormDependency,
ProFormDigit,
ProFormFieldSet,
ProFormList,
ProFormRadio,
ProFormRate,
ProFormSelect,
ProFormSlider,
ProFormSwitch,
ProFormText,
ProFormTimePicker,
ProFormUploadButton,
ProFormUploadDragger,
} from '@ant-design/pro-components';
import { message } from 'antd';
import { FIXED_BASE_TIMESTAMP } from '../../mockData';
const options = [
{ id: 1, label: '选项 A', value: 1 },
{ id: 2, label: '选项 B', value: 2 },
{ id: 3, label: '选项 C', value: 3 },
{ id: 4, label: '选项 D', value: 4 },
{ id: 5, label: '选项 E', value: 5 },
{ id: 6, label: '选项 F', value: 6 },
{ id: 7, label: '选项 G', value: 7 },
{ id: 8, label: '选项 H', value: 8 },
{ id: 9, label: '选项 I', value: 9 },
{ id: 10, label: '选项 J', value: 10 },
{ id: 11, label: '选项 K', value: 11 },
{ id: 12, label: '选项 L', value: 12 },
];
const Demo = () => (
<div
style={{
padding: 24,
}}
>
<ProForm
readonly
name="field-set-components-other-readonly-demo"
initialValues={{
'select-multiple': ['red', 'green'],
'select-multiple2': [1, 2, 3],
useMode: 'all',
switch: true,
'input-number': 3,
'checkbox-group': ['A', 'B'],
rate: 3.5,
name: '示例科技有限公司',
radio: 'a',
list: ['1', '2', '3'],
list2: ['1', '2', '3', '5'],
select: 'china',
'radio-button': 'a',
dragger: [
{
uid: '1',
name: '合同.pdf',
status: 'done',
response: 'Server Error 500', // custom error message to show
url: 'https://gw.alipayobjects.com/zos/antfincdn/7%24YOiS6YIm/huaban.png',
},
{
uid: '2',
name: '营业执照.jpg',
status: 'done',
url: 'https://gw.alipayobjects.com/zos/antfincdn/7%24YOiS6YIm/huaban.png',
},
{
uid: '3',
name: '身份证.jpg',
status: 'error',
response: 'Server Error 500', // custom error message to show
url: 'https://gw.alipayobjects.com/zos/antfincdn/7%24YOiS6YIm/huaban.png',
},
],
upload: [
{
uid: '1',
name: '合同.pdf',
status: 'done',
response: 'Server Error 500', // custom error message to show
url: 'https://gw.alipayobjects.com/zos/antfincdn/7%24YOiS6YIm/huaban.png',
},
{
uid: '2',
name: '营业执照.jpg',
status: 'done',
url: 'https://gw.alipayobjects.com/zos/antfincdn/7%24YOiS6YIm/huaban.png',
},
{
uid: '3',
name: '身份证.jpg',
status: 'error',
response: 'Server Error 500', // custom error message to show
url: 'https://gw.alipayobjects.com/zos/antfincdn/7%24YOiS6YIm/huaban.png',
},
],
date: FIXED_BASE_TIMESTAMP,
dateWeek: FIXED_BASE_TIMESTAMP,
dateMonth: FIXED_BASE_TIMESTAMP,
dateQuarter: FIXED_BASE_TIMESTAMP,
dateYear: FIXED_BASE_TIMESTAMP,
dateTime: FIXED_BASE_TIMESTAMP,
time: '00:01:05',
timeRange: ['05:00:00', '11:00:00'],
dateTimeRange: [
FIXED_BASE_TIMESTAMP,
FIXED_BASE_TIMESTAMP - 1000 * 60 * 60 * 24,
],
dateRange: [
FIXED_BASE_TIMESTAMP,
FIXED_BASE_TIMESTAMP - 1000 * 60 * 60 * 24,
],
}}
onFinish={async () => {
message.success('Submitted');
}}
>
<ProForm.Group title="基础数据">
<ProFormText
width="md"
name="name"
label="name"
fieldProps={{
prefix: 'prefix',
suffix: 'suffix',
}}
/>
<ProFormSelect
name="select"
label="Select"
valueEnum={{
china: 'China',
usa: 'U.S.A',
}}
placeholder="Please select a country"
rules={[{ required: true, message: 'Please select your country!' }]}
/>
<ProFormSelect
width="md"
request={async () => [
{ label: '全部', value: 'all' },
{ label: '未解决', value: 'open' },
{ label: '已解决', value: 'closed' },
{ label: '解决中', value: 'processing' },
]}
name="useMode"
label="合同约定生效方式"
/>
<ProFormSelect
name="select-multiple"
label="Select[multiple]"
valueEnum={{
red: 'Red',
green: 'Green',
blue: 'Blue',
}}
fieldProps={{
mode: 'multiple',
}}
placeholder="Please select favorite colors"
rules={[
{
required: true,
message: 'Please select your favorite colors!',
type: 'array',
},
]}
/>
<ProFormSelect
name="select-multiple2"
label="Select[multiple]"
options={options}
fieldProps={{
mode: 'multiple',
}}
placeholder="Please select favorite colors"
rules={[
{
required: true,
message: 'Please select your favorite colors!',
type: 'array',
},
]}
/>
<ProFormDigit
label="InputNumber"
name="input-number"
min={1}
max={10}
/>
<ProFormSwitch
name="switch"
label="Switch"
unCheckedChildren="不同意"
checkedChildren="同意"
/>
<ProFormSlider
name="slider"
label="Slider"
marks={{
0: 'A',
20: 'B',
40: 'C',
60: 'D',
80: 'E',
100: 'F',
}}
/>
</ProForm.Group>
<ProFormRadio.Group
name="radio"
label="Radio.Group"
options={[
{
label: 'item 1',
value: 'a',
},
{
label: 'item 2',
value: 'b',
},
{
label: 'item 3',
value: 'c',
},
]}
/>
<ProForm.Group>
<ProFormText hidden label="text1" />
<ProFormText label="text2" />
</ProForm.Group>
<ProFormRadio.Group
name="radio-button"
label="Radio.Button"
radioType="button"
options={[
{
label: 'item 1',
value: 'a',
},
{
label: 'item 2',
value: 'b',
},
{
label: 'item 3',
value: 'c',
},
]}
/>
<ProFormCheckbox.Group
name="checkbox-group"
label="Checkbox.Group"
options={['A', 'B', 'C', 'D', 'E', 'F']}
/>
<ProFormRate name="rate" label="Rate" />
<ProFormUploadButton
name="upload"
label="Upload"
max={2}
action="/upload.do"
extra="longgggggggggggggggggggggggggggggggggg"
/>
<ProFormList
name="textList"
label="地址列表"
initialValue={[
{
name: '书琰',
addr: '杭州市滨江区',
},
{
name: '逄一',
addr: '北京市海淀区',
},
]}
>
<ProFormText name="name" label="姓名" />
<ProFormText name="addr" label="地址" />
</ProFormList>
<ProFormFieldSet
name="list"
label="组件列表"
transform={(value: any) => ({ startTime: value[0], endTime: value[1] })}
>
<ProFormText width="md" />
-
<ProFormText width="md" />
-
<ProFormText width="md" />
</ProFormFieldSet>
<ProFormFieldSet
name="list2"
label="组件列表自动增加"
transform={(value: any) => ({ startTime: value[0], endTime: value[1] })}
>
{(value) => {
return value?.map((e, index) => {
return (
<ProFormText
key={index}
width="md"
fieldProps={{
value: e,
}}
/>
);
});
}}
</ProFormFieldSet>
<ProFormUploadDragger max={4} label="Dragger" name="dragger" />
<ProForm.Group title="日期相关分组">
<ProFormDatePicker name="date" label="日期" />
<ProFormDatePicker
name="date"
fieldProps={{
format: 'YY-MM',
}}
label="年月"
/>
<ProFormTimePicker name="time" label="时间" />
<ProFormTimePicker.RangePicker name="timeRange" label="时间区间" />
<ProFormDatePicker.Week name="dateWeek" label="周" />
<ProFormDatePicker.Month name="dateMonth" label="月" />
<ProFormDatePicker.Quarter name="dateQuarter" label="季度" />
<ProFormDatePicker.Year name="dateYear" label="年" />
<ProFormDateTimePicker
name="dateTime"
label="日期时间"
fieldProps={{
format: (value) => value.format('YYYY-MM-DD'),
}}
/>
<ProFormDateRangePicker name="dateRange" label="日期区间" />
<ProFormDateTimeRangePicker name="dateTimeRange" label="日期时间区间" />
</ProForm.Group>
<ProForm.Group title="其他相关分组">
<ProForm.Item label="互相依赖的表单">
<ProFormDependency name={['list']}>
{({ list }) => {
return <div>{JSON.stringify(list, null, 2)}</div>;
}}
</ProFormDependency>
</ProForm.Item>
<ProFormFieldSet name="list" label="组件列表">
<ProFormText width="md" />
<ProFormSelect
width="md"
request={async () => [
{ label: '全部', value: 'all' },
{ label: '未解决', value: 'open' },
{ label: '已解决', value: 'closed' },
{ label: '解决中', value: 'processing' },
]}
name="useMode"
label="合同约定生效方式"
/>
<ProFormText width="md" />
</ProFormFieldSet>
<ProFormFieldSet
name="list"
label="组件列表- Input.Group"
type="group"
rules={[
{
validator: (_, value) => {
const [name, name1, name2] = value || [];
if (!name) {
throw new Error('第一个值不能为空');
}
if (!name1) {
throw new Error('第二个值不能为空');
}
if (!name2) {
throw new Error('第三个值不能为空');
}
},
},
]}
transform={(value: any) => ({
list: value,
startTime: value[0],
endTime: value[1],
})}
>
<ProFormText width="md" />
<ProFormText width="md" />
<ProFormText width="md" />
</ProFormFieldSet>
<ProFormFieldSet
name="list"
label="组件列表"
transform={(value: any) => ({
list: value,
startTime: value[0],
endTime: value[1],
})}
>
<ProFormText width="md" />
-
<ProFormText width="md" />
-
<ProFormText width="md" />
</ProFormFieldSet>
</ProForm.Group>
</ProForm>
</div>
);
export default Demo;
import {
LightFilter,
ProFormFieldSet,
ProFormSelect,
ProFormText,
} from '@ant-design/pro-components';
export function App() {
return (
<div className="App">
<LightFilter
initialValues={{
name: [
{
label: '包含',
value: 'Like',
},
'123',
],
}}
placement={'bottomLeft'}
>
<ProFormFieldSet
label="筛选"
name={'name'}
transform={(value: any) => {
return {
filter: [
{
operator: value[0],
value: value[1],
},
],
};
}}
>
<ProFormSelect
fieldProps={{
labelInValue: true,
}}
valueEnum={{
Like: '包合',
NotLike: '不包合',
Eq: '等于',
NotEq: '不等于',
}}
/>
<ProFormText placeholder="请输入" />
</ProFormFieldSet>
</LightFilter>
</div>
);
}
export default () => (
<div style={{ padding: 24 }}>
<App />
</div>
);
#API
ProForm 自带的 Filed , 与 valueType 基本上一一对应。
#通用的属性
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| width | Field 的长度,我们归纳了常用的 Field 长度以及适合的场景,支持了一些枚举 "xs" , "sm" , "md" ,"lg" , "xl" | number | "xs" | "sm" | "md" | "lg" | "xl" | - |
| rowProps | RowProps | 开启 grid 模式时传递给 Row, 仅在ProFormGroup, ProFormList, ProFormFieldSet 中有效 | { gutter: 8 } |
| colProps | ColProps | 开启 grid 模式时传递给 Col | { xs: 24 } |
| tooltip | 会在 label 旁增加一个 icon,悬浮后展示配置的信息 | string | tooltipProps | - |
| secondary | 是否是次要控件,只针对 LightFilter 下有效 | boolean | false |
| allowClear | 支持清除,针对 LightFilter 下有效,主动设置情况下同时也会透传给 fieldProps | boolean | true |
#宽度
在某些场景下,我们需要根据页面展示效果对输入框进行自适应处理,除此以外一个表单区域应默认使用定宽规则。

XS=104px适用于短数字、短文本或选项。SM=216px适用于较短字段录入、如姓名、电话、ID 等。MD=328px标准宽度,适用于大部分字段长度。LG=440px适用于较长字段录入,如长网址、标签组、文件路径等。XL=552px适用于长文本录入,如长链接、描述、备注等,通常搭配自适应多行输入框或定高文本域使用。
#ProFormText
与 Input 相同。
<ProFormText
name="text"
label="名称"
placeholder="请输入名称"
fieldProps={inputProps}
/>#ProFormCaptcha
ProFormCaptcha 是为了支持中后台中常见的验证码功能开发的组件。
import type { CaptFieldRef } from '@ant-design/pro-components';
import { ProForm, ProFormCaptcha } from '@ant-design/pro-components';
import { Button, message } from 'antd';
import { useRef } from 'react';
const waitTime = (time: number = 100) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(true);
}, time);
});
};
const Demo = () => {
const captchaRef = useRef<CaptFieldRef | null | undefined>();
const inputRef = useRef();
return (
<ProForm
name="captcha-form-demo"
title="新建表单"
submitter={{
render: (props, doms) => {
return [
...doms,
<Button
htmlType="button"
onClick={() => {
captchaRef.current?.startTiming();
}}
key="start"
>
手动开始计数
</Button>,
<Button
htmlType="button"
onClick={() => {
captchaRef.current?.endTiming();
}}
key="end"
>
手动结束计数
</Button>,
];
},
}}
onFinish={async (_values) => {
await waitTime(2000);
message.success('提交成功');
return true;
}}
>
<ProFormCaptcha
onGetCaptcha={() => {
// dosomething()
return new Promise((resolve, reject) => {
reject();
});
}}
onTiming={(count) => {
if (count === 0) {
message.info('验证码倒计时结束');
}
}}
fieldRef={captchaRef}
fieldProps={{ ref: inputRef }}
name="code"
/>
</ProForm>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
<ProFormCaptcha
fieldProps={{
size: 'large',
prefix: <MailTwoTone />,
}}
captchaProps={{
size: 'large',
}}
// 手机号的 name,onGetCaptcha 会注入这个值
phoneName="phone"
name="captcha"
rules={[
{
required: true,
message: '请输入验证码',
},
]}
placeholder="请输入验证码"
// 如果需要失败可以 throw 一个错误出来,onGetCaptcha 会自动停止
// throw new Error("获取验证码错误")
onGetCaptcha={async (phone) => {
await waitTime(1000);
message.success(`手机号 ${phone} 验证码发送成功!`);
}}
/>| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| onGetCaptcha | 点击获取验证码的事件,如果配置了 phoneName 会自动注入 | (phone)=>Promise<void> | - |
| phoneName | 手机号的 name,onGetCaptcha 会注入这个值 | NamePath | - |
| onTiming | 计时数字监听 | (count: number)=>void | - |
| captchaProps | 获取验证码按钮的 props,与 antd 的 props 相同 | ButtonProps | - |
| countDown | 倒计时的秒数 | number | 60 |
| captchaTextRender | 渲染计时的文案 | (timing: boolean, count: number) => React.ReactNode | - |
#ProFormText.Password
与 Input.Password 相同。
<ProFormText.Password label="InputPassword" name="input-password" />#ProFormTextArea
与 Input.TextArea 相同。
<ProFormTextArea
name="text"
label="名称"
placeholder="请输入名称"
fieldProps={inputTextAreaProps}
/>#ProFormDigit
与 inputNumber 相同。它自带了一个格式化 (保留 2 位小数,最小值为 0),有需要你可以关掉它。
<ProFormDigit label="InputNumber" name="input-number" min={1} max={10} />如果要修改小数位数:
<ProFormDigit
label="InputNumber"
name="input-number"
min={1}
max={10}
fieldProps={{ precision: 0 }}
/>#ProFormDigitRange
与 inputNumber 类似。 它提供输入数字范围。
<ProFormDigitRange label="InputNumberRange" name="input-number-range" />#ProFormDatePicker
与 DatePicker 相同。
<ProFormDatePicker name="date" label="日期" />#ProFormDateTimePicker
与 DatePicker 相同。
<ProFormDateTimePicker name="datetime" label="日期时间" />#ProFormDateRangePicker
与 DatePicker.RangePicker 相同。
<ProFormDateRangePicker name="dateRange" label="日期" />#ProFormDateTimeRangePicker
与 DatePicker.RangePicker 相同。
<ProFormDateTimeRangePicker name="datetimeRange" label="日期时间" />#ProFormTimePicker
与 DatePicker 相同
<ProFormTimePicker name="time" label="时间" />
<ProFormTimePicker.RangePicker name="timeRange" label="时间区间" />#ProFormSelect
与 select 相同。支持了 request 和 valueEnum 两种方式来生成 options。
请求远程数据比较复杂,详细可以看这里。
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| valueEnum | 当前列值的枚举 valueEnum | Record | - |
| request | 从网络请求枚举数据 | ()=>Promise<{label,value}> | - |
| fetchDataOnSearch | 搜索时是否重新触发 request;设为 false 时仅初始化拉取一次,搜索走本地过滤 | boolean | true |
| debounceTime | 防抖动时间,与 request 配合使用 | number | - |
| params | 发起网络请求的参数,与 request 配合使用 | Record | - |
| fieldProps | antd 组件的 props | SelectProps | - |
有了 options 为什么要支持 valueEnum 呢? valueEnum 可以与 table,descriptions 共用,在工程化上有优势。
<>
<ProFormSelect
name="select"
label="Select"
valueEnum={{
open: '未解决',
closed: '已解决',
}}
placeholder="Please select a country"
rules={[{ required: true, message: 'Please select your country!' }]}
/>
<ProFormSelect
name="select2"
label="Select"
request={async () => [
{ label: '全部', value: 'all' },
{ label: '未解决', value: 'open' },
{ label: '已解决', value: 'closed' },
{ label: '解决中', value: 'processing' },
]}
placeholder="Please select a country"
rules={[{ required: true, message: 'Please select your country!' }]}
/>
</>联动的 ProFormSelect
<ProFormText name="name" label="姓名" />
<ProFormSelect
name="addr"
width="md"
label="与 name 联动的选择器"
// dependencies 的内容会注入 request 中
dependencies={['name']}
request={async (params) => [
{ label: params.name, value: 'all' },
{ label: 'Unresolved', value: 'open' },
{ label: 'Resolved', value: 'closed' },
{ label: 'Resolving', value: 'processing' },
]}
/>自定义选项:
<ProFormSelect
name="select"
label="Select"
options={[
{ label: '全部', value: 'all' },
{ label: '未解决', value: 'open' },
{ label: '已解决', value: 'closed' },
{ label: '解决中', value: 'processing' },
]}
fieldProps={{
optionItemRender(item) {
return item.label + ' - ' + item.value;
},
}}
placeholder="Please select a country"
rules={[{ required: true, message: 'Please select your country!' }]}
/>#ProFormTreeSelect
与 tree-select 相同。支持了 request 和 valueEnum 两种方式来生成 options。
请求远程数据比较复杂,详细可以看这里。
fieldProps.onOpenChange 只用于监听开关变化,组件会继续维护内部 open 状态,无需额外传入 open。
大量树节点被虚拟滚动截断时,请先升级到最新的 antd / rc-virtual-list。旧版本可临时使用 fieldProps={{ virtual: false }} 作为兼容方案。
关联勾选(treeCheckStrictly: false)不会把半选父节点放入 value。需要半选信息时,可从 onChange 第三个参数的 extra.allCheckedNodes 推导;开启 treeCheckStrictly 后父子节点将改为独立选择。
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| valueEnum | 当前列值的枚举 valueEnum | Record | - |
| request | 从网络请求枚举数据 | ()=>Promise<{label,value}> | - |
| debounceTime | 防抖动时间,与 request 配合使用 | number | - |
| params | 发起网络请求的参数,与 request 配合使用 | Record | - |
| fieldProps | antd 组件的 props | TreeSelectProps | - |
有了 options 为什么要支持 valueEnum 呢? valueEnum 可以与 table,descriptions 共用,在工程化上有优势。
<ProFormTreeSelect
name="name"
placeholder="Please select"
allowClear
width={330}
secondary
request={async () => {
return [
{
title: 'Node1',
value: '0-0',
children: [
{
title: 'Child Node1',
value: '0-0-0',
},
],
},
{
title: 'Node2',
value: '0-1',
children: [
{
title: 'Child Node3',
value: '0-1-0',
},
{
title: 'Child Node4',
value: '0-1-1',
},
{
title: 'Child Node5',
value: '0-1-2',
},
],
},
];
}}
// tree-select args
fieldProps={{
suffixIcon: null,
filterTreeNode: true,
showSearch: true,
popupMatchSelectWidth: false,
labelInValue: true,
autoClearSearchValue: true,
multiple: true,
treeNodeFilterProp: 'title',
fieldNames: {
label: 'title',
},
}}
/>#ProFormCheckbox
请求远程数据比较复杂,详细可以看这里。
与 checkbox 相同,但是支持了 options 与 layout。
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| options | 与 select 相同,根据 options 生成子节点,推荐使用。 | string[] | {label:ReactNode,value:string}[] | - |
| layout | 配置 checkbox 的样子,支持垂直vertical 和 horizontal | horizontal | vertical | - |
| request | 从网络请求枚举数据 | ()=>Promise<{label,value}> | - |
| params | 发起网络请求的参数,与 request 配合使用 | Record | - |
| fieldProps | antd 组件的 props | CheckboxProps | - |
<ProFormCheckbox.Group
name="checkbox"
layout="vertical"
label="行业分布"
options={['农业', '制造业', '互联网']}
/>#ProFormRadio.Group
请求远程数据比较复杂,详细可以看这里。
与 radio 相同,但是支持了 options。
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| options | 与 select 相同,根据 options 生成子节点,推荐使用。 | string[] | {label:ReactNode,value:string}[] | - |
| request | 从网络请求枚举数据 | ()=>Promise<{label,value}> | - |
| radioType | 设置是按钮模式还是 radio 模式 | default|button | default |
| params | 发起网络请求的参数,与 request 配合使用 | Record | - |
| fieldProps | antd 组件的 props | RadioProps | - |
<ProFormRadio.Group
name="radio-group"
label="Radio.Group"
options={[
{
label: 'item 1',
value: 'a',
},
{
label: 'item 2',
value: 'b',
},
{
label: 'item 3',
value: 'c',
},
]}
/>#ProFormCascader
与 cascader 相同,通过 fieldProps 配置 cascader 的数据。
请求远程数据比较复杂,详细可以看这里。
<ProFormCascader
name="area"
label="区域"
fieldProps={{
options: [
{
value: 'zhejiang',
label: 'Zhejiang',
children: [
{
value: 'hangzhou',
label: 'Hangzhou',
children: [
{
value: 'xihu',
label: 'West Lake',
},
],
},
],
},
],
}}
/>| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| options | 与 cascader 相同,根据 options 生成子节点 ,推荐使用。 | string[] | {label:ReactNode,value:string}[] | - |
| request | 从网络请求枚举数据 | ()=>Promise<{label,value}> | - |
| params | 发起网络请求的参数,与 request 配合使用 | Record | - |
| fieldProps | antd 组件的 props | CascaderProps | - |
#ProFormSwitch
与 switch 相同,通过 fieldProps 配置 switch 的数据。
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| fieldProps | antd 组件的 props | SwitchProps | - |
<ProFormSwitch name="switch" label="Switch" />#ProFormRate
与 rate 相同,通过 fieldProps 配置 rate 的数据。
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| fieldProps | antd 组件的 props | RateProps | - |
<ProFormRate name="rate" label="Rate" />#ProFormSlider
与 slider 相同,通过 fieldProps 配置 slider 的数据。
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| fieldProps | antd 组件的 props | SliderProps | - |
<ProFormSlider
name="slider"
label="Slider"
marks={{
0: 'A',
20: 'B',
40: 'C',
60: 'D',
80: 'E',
100: 'F',
}}
/>#ProFormUploadDragger
与 upload 相同。预设了 Dragger 的样式,其他与 Upload 相同。
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| icon | Dragger 的图标 | ReactNode | InboxOutlined |
| title | Dragger 的标题 | ReactNode | ' 单击 或拖动文件到此区域进行上传' |
| description | Dragger 的描述 | ReactNode | ' 支持单次或批量上传' |
| max | 最大的文件数量,到达数量之后上传按钮会失效 | number | - |
<ProFormUploadDragger label="Dragger" name="dragger" action="upload.do" />#ProFormUploadButton
与 upload 相同。预设了 Button 的样式,其他与 Upload 相同。
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| icon | Button 的图标 | ReactNode | UploadOutlined |
| title | Button 的标题 | ReactNode | 单击上传 |
| max | 最大上传数量,超过最大数量就会隐藏上传按钮 | number | - |
| buttonProps | 上传按钮的配置,如 buttonProps={{ type: "primary" }} | ButtonProps | - |
| disabled | 是否禁用按钮 | boolean | - |
| imageProps | 预览 Image 组件的额外配置,可以自定义预览行为、工具栏以及其他 Image 组件的属性。 | Omit<ImageProps, 'src'> | - |
<ProFormUploadButton label="upload" name="upload" action="upload.do" />#ProFormMoney
ProFormMoney 用于输入金额的输入框,支持根据全局国际化显示货币符号,支持输入负数、支持自定义货币符号等
<ProFormMoney
label="限制金额最小为0"
name="amount1"
locale="en-US"
initialValue={22.22}
min={0}
/>
<ProFormMoney
label="不限制金额大小"
name="amount2"
locale="en-GB"
initialValue={22.22}
/>
<ProFormMoney
label="货币符号跟随全局国际化"
name="amount3"
initialValue={22.22}
/>
<ProFormMoney
label="自定义货币符号"
name="amount4"
initialValue={22.22}
customSymbol="💰"
/>| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| locale | 单独设置的国际化地区值,根据不同的地区显示不同的货币符号,支持地区详见下面的地区目录 | string | zh-Hans-CN |
| customSymbol | 自定义金额符号 | string | - |
| numberPopoverRender | 自定义 Popover 的值,false 可以关闭他 | ((props: InputNumberProps, defaultText: string) => React.ReactNode)| boolean | false |
| numberFormatOptions | NumberFormat 的配置,文档可以查看 mdn | NumberFormatOptions | - |
| min | 最小值 | number | - |
| max | 最大值 | number | - |
#以下为地区编码与货币符号对照表
{
"ar-EG": "$",
"zh-CN": "¥",
"en-US": "$",
"en-GB": "£",
"vi-VN": "₫",
"it-IT": "€",
"ja-JP": "¥",
"es-ES": "€",
"ru-RU": "₽",
"sr-RS": "RSD",
"ms-MY": "RM",
"zh-TW": "NT$"
"fr-FR": "€",
"pt-BR": "R$",
"ko-KR": "₩",
"id-ID": "RP",
"de-DE": "€",
"fa-IR": "تومان",
"tr-TR": "₺",
"pl-PL": "zł",
"hr-HR": "kn",
}#ProFormSegmented
与 Segmented 相同。支持了 request 和 valueEnum 两种方式来生成 options。
请求远程数据比较复杂,详细可以看这里。
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| valueEnum | 当前列值的枚举 valueEnum | Record | - |
| request | 从网络请求枚举数据 | ()=>Promise<{label,value}> | - |
| debounceTime | 防抖动时间,与 request 配合使用 | number | - |
| params | 发起网络请求的参数,与 request 配合使用 | Record | - |
| fieldProps | antd 组件的 props | Segmented | - |
有了 options 为什么要支持 valueEnum 呢? valueEnum 可以与 table,descriptions 共用,在工程化上有优势。
<>
<ProFormSegmented
name="segmented"
label="segmented"
valueEnum={{
open: '未解决',
closed: '已解决',
}}
rules={[{ required: true, message: 'Please select your country!' }]}
/>
<ProFormSegmented
name="segmented"
label="segmented"
request={async () => [
{ label: '全部', value: 'all' },
{ label: '未解决', value: 'open' },
{ label: '已解决', value: 'closed' },
{ label: '解决中', value: 'processing' },
]}
rules={[{ required: true, message: 'Please select your country!' }]}
/>
</>