#Schema Form - JSON Form
SchemaForm is a tool that generates forms based on JSON Schema. SchemaForm maps to different form items based on valueType.
Tips: If you encounter performance issues or have higher performance requirements, please refer to the High Performance Code Example.
#API
SchemaForm provides the same API as ProForm and adds some additional APIs. The following are the new APIs for SchemaForm.
Use onValuesChange(changedValues, allValues) to observe form value changes; onChange is not the form value callback.
| Property | Type | Description |
|---|---|---|
layoutType | ProFormLayoutType | The layout mode of the form |
steps | StepFormProps[] | Step form configuration in layoutType=steps. Needs columns to be an array |
columns | ProFormColumnsType[] | ProFormColumnsType[][] | Form definition, generally a JSON object. For step forms, use a JSON array to generate multiple forms |
shouldUpdate | boolean | ((newValues: Record<string, any>, oldValues?: Record<string, any>) => boolean) | Fine-grained control over whether to render. If true, form items will automatically re-render.If false, form items will not update but can use dependencies to trigger updates.If function, it decides whether to re-render form items based on the return value |
title | ReactNode | Form title |
action | MutableRefObject<ProCoreActionType | undefined> | Action for operating the form, supporting reload and other operations |
formRef | MutableRefObject<ProFormInstance | undefined> | Get form instance, supports all methods of antd form |
open | boolean | Control the visibility of ModalForm and DrawerForm |
#ProFormLayoutType
| Property | Description |
|---|---|
Form | ProForm is the basic form type |
ModalForm | Modal form, supports all configurations of ModalForm |
DrawerForm | Drawer form, supports all configurations of DrawerForm |
StepsForm|StepForm | Step form. There are two modes: one uses steps and columns to generate, the other is implemented by nesting layoutType=StepForm within layoutType=StepsForm |
LightFilter | Light filter, supports all configurations of LightFilter |
QueryFilter | Query form, supports all configurations of QueryFilter |
Embed | Embedded mode, only generates form items, does not generate Form. Can be mixed |
#Schema Definition
The most important part of SchemaForm is the Schema type definition. We use the same form definition as the table, and extend some fields.
| Property | Type | Description |
|---|---|---|
key | React.Key | Determines the unique value of this column, generally used when dataIndex is duplicated |
dataIndex | React.Key | React.Key[] | The key mapped to the entity. Arrays will be converted [a,b] => Entity.a.b |
valueType | ProFieldValueType | How data is rendered. We come with a part, you can also customize valueType |
title | ReactNode |(props,type,dom)=> ReactNode | The content of the title, which is label in form |
tooltip | LabelTooltipType | string | Displays an icon next to the title, shown on hover |
width | number | string | Width.xs: 104px - short numbers, short textsm: 216px - short fields (name, phone)md: 328px - standard widthlg: 440px - long fields (url, tags)xl: 552px - long text (description, remarks) |
colSize | number | The grid size occupied by each form item. Total width = span * colSize. Default is 1 |
readonly | boolean | Whether it is read-only mode |
initialValue | any | Default value of the form |
valueEnum | (Entity)=> ValueEnum | ValueEnum | Supports object and Map. Map supports other basic types as keys |
fieldProps | (form,config)=>fieldProps| fieldProps | Props passed to the rendered component. Also passed when customizing |
formItemProps | (form,config)=>formItemProps | formItemProps | Configuration passed to Form.Item |
formItemProps.rules | Rule[] | Validation rules for form items. Note that if the current form item is formList, this rule only validates whether the list is empty, and only accepts the tuple [{required: boolean, message: string}] used to enable/disable non-empty validation and specify the empty list prompt message |
proFieldProps | proFieldProps | props set on ProField, internal property |
renderText | (text: any, record: Entity, index: number, action: ProCoreActionType) => any | The modified data will be consumed by the rendering component defined by valueType |
render | (dom,entity,index, action, schema) => React.ReactNode | Custom read-only mode dom. The render method only manages read-only mode. Edit mode needs to use formItemRender |
formItemRender | (schema,config,form,action) => React.ReactNode | Custom edit mode, returns a ReactNode, automatically wraps value and onChange. |
request | (params,props) => Promise<{label,value}[]> | Request network data remotely, generally used for selection components |
params | Record<string, any> | Extra parameters passed to request. The component does not process them, but changes will cause request to re-request data |
dependencies | NamePath[] | When dependent values change, trigger formItemRender, fieldProps, formItemProps to re-execute, and inject values into params Example |
hideInDescriptions | boolean | Hide in descriptions |
hideInForm | boolean | Hide in Form |
hideInTable | boolean | Hide in Table |
search | boolean | { transform: (value: any) => any } | Set false to hide in the Table query form; object config provides transform for converting submitted keys |
columns | ProFormColumnsType[] | (values) => ProFormColumnsType[] | Nested items. When valueType is dependency, please use (values) => ProFormColumnsType[]. Other cases use ProFormColumnsType[] |
colProps | ColProps | Passed to Col when grid mode is enabled |
rowProps | RowProps | Passed to Row when grid mode is enabled |
convertValue | (value, namePath)=> any | Convert value when getting, generally used to format data into a format received by the component, e.g. [a,b] => a,b |
transform | (value, namePath, allValues)=> any | Convert value when submitting, generally used to convert values into submitted data, e.g. string => { newName: string } |
order | number | Form sorting, default is index, larger order is more forward |
debounceTime | number | Request debounce time |
defaultKeyWords | string | Default keyword when searching |
ignoreFormItem | boolean | Do not wrap Form.Item, render component directly |
#valueEnum
valueEnum maps a field value to display text and status. It accepts an object, a Map, or a function that returns the enumeration for the current entity.
#Common ValueTypes
| ValueType | Description |
|---|---|
text | Text input |
textarea | Text area |
password | Password input |
digit | Digit input |
money | Money input |
select | Select |
checkbox | Checkbox |
radio | Radio |
date | Date picker |
dateRange | Date range picker |
time | Time picker |
switch | Switch |
group | Form group |
dependency | Dependency linkage |
formList | Form list |
#Difference between fieldProps and formItemProps
- fieldProps: Properties passed to the specific input component (such as Input, Select, DatePicker). For example
placeholder,allowClear,style, etc. - formItemProps: Properties passed to Ant Design's
Form.Item. For examplelabel,name,rules,extra,help, etc.
{
title: 'Title',
dataIndex: 'title',
formItemProps: {
rules: [{ required: true, message: 'This field is required' }],
extra: 'This is a supplementary explanation',
},
fieldProps: {
placeholder: 'Please enter title',
allowClear: true,
},
}#Code Examples - Layout Types
#Basic Form (Form)
The most basic usage of SchemaForm, generating standard form pages through JSON configuration. Supports defining form items via columns and switching layouts via layoutType.
import type {
ProFormColumnsType,
ProFormLayoutType,
} from '@ant-design/pro-components';
import { BetaSchemaForm, ProFormSelect } from '@ant-design/pro-components';
import { Alert, DatePicker, Space } from 'antd';
import { useState } from 'react';
import { FIXED_BASE_DATE } from '../../mockData';
const valueEnum = {
all: { text: '全部', status: 'Default' },
open: {
text: '未解决',
status: 'Error',
},
closed: {
text: '已解决',
status: 'Success',
disabled: true,
},
processing: {
text: '解决中',
status: 'Processing',
},
};
type DataItem = {
name: string;
state: string;
};
const columns: ProFormColumnsType<DataItem>[] = [
{
title: '标题',
dataIndex: 'title',
formItemProps: {
rules: [
{
required: true,
message: '此项为必填项',
},
],
},
width: 'md',
colProps: {
xs: 24,
md: 12,
},
initialValue: '默认值',
convertValue: (value) => {
return `标题:${value}`;
},
transform: (value) => {
return {
title: `${value}-转换`,
};
},
},
{
title: '状态',
dataIndex: 'state',
valueType: 'select',
valueEnum,
width: 'md',
colProps: {
xs: 24,
md: 12,
},
},
{
title: '标签',
dataIndex: 'labels',
width: 'md',
colProps: {
xs: 12,
md: 4,
},
},
{
valueType: 'switch',
title: '开关',
dataIndex: 'Switch',
fieldProps: {
style: {
width: '200px',
},
},
width: 'md',
colProps: {
xs: 12,
md: 20,
},
},
{
title: '创建时间',
key: 'showTime',
valueType: 'dateRange',
dataIndex: 'createName',
initialValue: [FIXED_BASE_DATE.add(-1, 'm'), FIXED_BASE_DATE],
formItemRender: () => <DatePicker.RangePicker />,
width: 'md',
colProps: {
xs: 24,
md: 12,
},
},
{
title: '更新时间',
dataIndex: 'updateName',
valueType: 'dateRange',
initialValue: [FIXED_BASE_DATE.add(-1, 'm'), FIXED_BASE_DATE],
formItemRender: () => <DatePicker.RangePicker />,
width: 'md',
colProps: {
xs: 24,
md: 12,
},
},
{
title: '分组',
valueType: 'group',
columns: [
{
title: '状态',
dataIndex: 'groupState',
valueType: 'select',
width: 'xs',
colProps: {
xs: 12,
},
valueEnum,
},
{
title: '标题',
width: 'md',
dataIndex: 'groupTitle',
colProps: {
xs: 12,
},
formItemProps: {
rules: [
{
required: true,
message: '此项为必填项',
},
],
},
},
],
},
{
title: '列表',
valueType: 'formList',
dataIndex: 'list',
initialValue: [{ state: 'all', title: '标题' }],
colProps: {
xs: 24,
sm: 12,
},
columns: [
{
valueType: 'group',
columns: [
{
title: '状态',
dataIndex: 'state',
valueType: 'select',
colProps: {
xs: 24,
sm: 12,
},
width: 'xs',
valueEnum,
},
{
title: '标题',
dataIndex: 'title',
width: 'md',
formItemProps: {
rules: [
{
required: true,
message: '此项为必填项',
},
],
},
colProps: {
xs: 24,
sm: 12,
},
},
],
},
{
valueType: 'dateTime',
initialValue: FIXED_BASE_DATE.toDate(),
dataIndex: 'currentTime',
width: 'md',
},
],
},
{
title: 'FormSet',
valueType: 'formSet',
dataIndex: 'formSet',
colProps: {
xs: 24,
sm: 12,
},
rowProps: {
gutter: [16, 0],
},
columns: [
{
title: '状态',
dataIndex: 'groupState',
valueType: 'select',
width: 'md',
valueEnum,
},
{
width: 'xs',
title: '标题',
dataIndex: 'groupTitle',
tooltip: '标题过长会自动收缩',
formItemProps: {
rules: [
{
required: true,
message: '此项为必填项',
},
],
},
},
],
},
{
title: '创建时间',
dataIndex: 'created_at',
valueType: 'dateRange',
width: 'md',
colProps: {
span: 24,
},
transform: (value) => {
return {
startTime: value[0],
endTime: value[1],
};
},
},
];
const Demo = () => {
const [layoutType, setLayoutType] = useState<ProFormLayoutType>('Form');
return (
<>
<Space
style={{
width: '100%',
}}
orientation="vertical"
>
<Alert
type="warning"
title="QueryFilter 和 lightFilter 暂不支持grid模式"
style={{
marginBlockEnd: 24,
}}
/>
<ProFormSelect
label="布局方式"
options={[
'Form',
'ModalForm',
'DrawerForm',
'LightFilter',
'QueryFilter',
'StepsForm',
'StepForm',
'Embed',
]}
fieldProps={{
value: layoutType,
onChange: (e) => setLayoutType(e),
}}
/>
</Space>
<BetaSchemaForm<DataItem>
name="schema-form-schema-demo"
trigger={<a>点击我</a>}
layoutType={layoutType}
steps={[
{
title: 'ProComponent',
},
]}
rowProps={{
gutter: [16, 16],
}}
colProps={{
span: 12,
}}
grid={layoutType !== 'LightFilter' && layoutType !== 'QueryFilter'}
onFinish={async () => {
}}
columns={(layoutType === 'StepsForm' ? [columns] : columns) as any}
/>
</>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
#Modal & Drawer Form
By setting layoutType to ModalForm or DrawerForm, you can quickly convert forms into modal or drawer modes without manually managing open state.
import type { ProFormLayoutType } from '@ant-design/pro-components';
import { BetaSchemaForm, ProFormSelect } from '@ant-design/pro-components';
import { Alert, Button, Space } from 'antd';
import { useState } from 'react';
type DataItem = {
name: string;
state: string;
};
const Demo = () => {
const [layoutType, setLayoutType] = useState<ProFormLayoutType>('ModalForm');
return (
<>
<Space
style={{
width: '100%',
}}
orientation="vertical"
>
<Alert
type="warning"
title="QueryFilter 和 lightFilter 暂不支持grid模式"
style={{
marginBlockEnd: 24,
}}
/>
<ProFormSelect
label="布局方式"
options={['ModalForm', 'DrawerForm']}
fieldProps={{
value: layoutType,
onChange: (e) => setLayoutType(e),
}}
/>
</Space>
<BetaSchemaForm<DataItem>
name="schema-form-modal-and-drawer-form-demo"
trigger={<Button>点击我</Button>}
layoutType={layoutType as 'ModalForm'}
onFinish={async () => {
}}
{...(layoutType === 'ModalForm'
? {
modalProps: { destroyOnHidden: true },
}
: {
drawerProps: { destroyOnHidden: true },
})}
columns={[
{
title: '标题',
dataIndex: 'title',
formItemProps: {
rules: [
{
required: true,
message: '此项为必填项',
},
],
},
width: 'md',
},
]}
/>
</>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
#Step Form (StepsForm)
By setting layoutType to StepsForm or StepForm, you can generate step forms. In this case, columns should be a 2D array, where each array element represents the configuration of form items for a step.
import type {
ProFormColumnsType,
ProFormInstance,
} from '@ant-design/pro-components';
import { BetaSchemaForm } from '@ant-design/pro-components';
import { message } from 'antd';
import { useRef } from 'react';
const valueEnum = {
all: { text: '全部', status: 'Default' },
open: {
text: '未解决',
status: 'Error',
},
closed: {
text: '已解决',
status: 'Success',
disabled: true,
},
processing: {
text: '解决中',
status: 'Processing',
},
};
type DataItem = {
name: string;
state: string;
};
const columns: ProFormColumnsType<DataItem>[][] = [
[
{
title: '标题',
dataIndex: 'title',
formItemProps: {
rules: [
{
required: true,
message: '此项为必填项',
},
],
},
width: 'm',
},
{
title: '状态',
dataIndex: 'state',
valueType: 'select',
valueEnum,
width: 'm',
},
],
[
{
title: '标签',
dataIndex: 'labels',
width: 'm',
},
{
title: '创建时间',
key: 'showTime',
dataIndex: 'createName',
valueType: 'date',
},
{
title: '分组',
valueType: 'group',
columns: [
{
title: '状态',
dataIndex: 'groupState',
valueType: 'select',
width: 'xs',
valueEnum,
},
{
title: '标题',
width: 'md',
dataIndex: 'groupTitle',
formItemProps: {
rules: [
{
required: true,
message: '此项为必填项',
},
],
},
},
],
},
],
[
{
title: '列表',
valueType: 'formList',
dataIndex: 'list',
initialValue: [{ state: 'all', title: '标题' }],
columns: [
{
valueType: 'group',
columns: [
{
title: '状态',
dataIndex: 'state',
valueType: 'select',
width: 'xs',
valueEnum,
},
{
title: '标题',
dataIndex: 'title',
formItemProps: {
rules: [
{
required: true,
message: '此项为必填项',
},
],
},
width: 'm',
},
],
},
],
},
{
title: 'FormSet',
valueType: 'formSet',
dataIndex: 'formSet',
columns: [
{
title: '状态',
dataIndex: 'groupState',
valueType: 'select',
width: 'xs',
valueEnum,
},
{
title: '标题',
dataIndex: 'groupTitle',
tooltip: '标题过长会自动收缩',
formItemProps: {
rules: [
{
required: true,
message: '此项为必填项',
},
],
},
width: 'm',
},
],
},
{
title: '创建时间',
dataIndex: 'created_at',
valueType: 'dateRange',
transform: (value) => {
return {
startTime: value[0],
endTime: value[1],
};
},
},
],
];
const Demo = () => {
const formRef = useRef<ProFormInstance>();
return (
<BetaSchemaForm<DataItem>
name="schema-form-steps-form-demo"
layoutType="StepsForm"
steps={[
{
title: '第一步',
},
{
title: '第二步',
},
{
title: '第三步',
},
]}
onCurrentChange={(_current) => {
}}
formRef={formRef}
onFinish={async () => {
return new Promise((resolve) => {
message.success('提交成功');
setTimeout(() => {
resolve(true);
formRef.current?.resetFields();
}, 2000);
});
}}
columns={columns}
/>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
#Embedded Mode (Embed)
By setting layoutType to Embed, you can generate only form items without the Form container. This is useful when you need to embed SchemaForm into an existing ProForm or other containers.
import type { ProFormColumnsType } from '@ant-design/pro-components';
import {
BetaSchemaForm,
ProForm,
ProFormSelect,
ProFormText,
} from '@ant-design/pro-components';
const valueEnum = {
all: { text: '全部', status: 'Default' },
open: {
text: '未解决',
status: 'Error',
},
closed: {
text: '已解决',
status: 'Success',
disabled: true,
},
processing: {
text: '解决中',
status: 'Processing',
},
};
type DataItem = {
name: string;
state: string;
};
const columns: ProFormColumnsType<DataItem>[] = [
{
title: '标题',
dataIndex: 'title',
formItemProps: {
rules: [
{
required: true,
message: '此项为必填项',
},
],
},
width: 'm',
},
{
title: '状态',
dataIndex: 'state',
valueType: 'select',
valueEnum,
width: 'm',
},
];
const Demo = () => {
return (
<ProForm name="schema-form-embed-demo">
<h1>ProForm </h1>
<ProFormText name="username" />
<ProFormSelect
name="select-multiple"
label="多选"
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',
},
]}
/>
<h1>表单1 </h1>
<BetaSchemaForm<DataItem> layoutType="Embed" columns={columns} />
<h1>表单2</h1>
<BetaSchemaForm<DataItem>
layoutType="Embed"
columns={[
{
title: '创建时间',
key: 'showTime',
dataIndex: 'createName',
valueType: 'date',
},
{
title: '分组',
valueType: 'group',
columns: [
{
title: '状态',
dataIndex: 'groupState',
valueType: 'select',
width: 'xs',
valueEnum,
},
{
title: '标题',
width: 'md',
dataIndex: 'groupTitle',
formItemProps: {
rules: [
{
required: true,
message: '此项为必填项',
},
],
},
},
],
},
]}
/>
</ProForm>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
#Advanced Usage
#Dependency Linkage
Using valueType: 'dependency' can achieve complex form linkages. When dependent fields change, the current item update is automatically triggered.
普通json表单
嵌套json表单
import type { ProFormColumnsType } from '@ant-design/pro-components';
import { BetaSchemaForm, ProForm } from '@ant-design/pro-components';
const valueEnum = {
money: { text: '按金额' },
discount: { text: '按折扣' },
};
type DataItem = {
name: string;
state: string;
};
const columns: ProFormColumnsType<DataItem>[] = [
{
title: '优惠方式',
dataIndex: 'type',
formItemProps: {
rules: [
{
required: true,
message: '此项为必填项',
},
],
},
valueType: 'select',
valueEnum,
width: 'm',
},
{
valueType: 'dependency',
name: ['type'],
columns: ({ type }) => {
if (type === 'money') {
return [
{
dataIndex: 'money',
title: '优惠金额',
width: 'm',
valueType: 'money',
},
];
}
if (type === 'discount') {
return [
{
dataIndex: 'discount',
title: '折扣',
valueType: 'digit',
width: 'm',
fieldProps: {
precision: 2,
},
},
];
}
return [];
},
},
];
const Demo = () => {
return (
<>
<h1>普通json表单</h1>
<BetaSchemaForm<DataItem>
onFinish={async () => {
}}
columns={columns}
/>
<h1>嵌套json表单</h1>
<ProForm
name="schema-form-dependency-demo"
initialValues={{
type: 'money',
}}
>
<BetaSchemaForm<DataItem>
layoutType="Embed"
onFinish={async () => {
}}
columns={columns}
/>
</ProForm>
</>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
#High Performance Mode
For large forms, you can use shouldUpdate and dependencies for fine-grained rendering control to avoid re-rendering the entire form.
- dependencies: Specify dependent fields, update only when dependent fields change.
- shouldUpdate: Custom update logic, return false to prevent update.
import type { ProFormColumnsType } from '@ant-design/pro-components';
import { BetaSchemaForm } from '@ant-design/pro-components';
import { Input } from 'antd';
const valueEnum = {
all: { text: '全部', status: 'Default' },
open: {
text: '未解决',
status: 'Error',
},
closed: {
text: '已解决',
status: 'Success',
disabled: true,
},
processing: {
text: '解决中',
status: 'Processing',
},
};
type DataItem = {
name: string;
state: string;
};
const columns: ProFormColumnsType<DataItem>[] = [
{
title: '标题',
dataIndex: 'title',
initialValue: '必填',
formItemProps: {
rules: [
{
required: true,
message: '此项为必填项',
},
],
},
width: 'm',
},
{
title: '状态',
dataIndex: 'state',
valueType: 'select',
valueEnum,
width: 'm',
tooltip: '当title为disabled时状态无法选择',
dependencies: ['title'],
fieldProps: (form) => {
if (form.getFieldValue('title') === 'disabled') {
return {
disabled: true,
placeholder: 'disabled',
};
} else {
return {
placeholder: 'normal',
};
}
},
},
{
title: '标签',
dataIndex: 'labels',
width: 'm',
tooltip: '当title为必填时此项将为必填',
dependencies: ['title'],
formItemProps(form) {
if (form.getFieldValue('title') === '必填') {
return {
rules: [
{
required: true,
},
],
};
} else {
return {};
}
},
},
{
valueType: 'dependency',
name: ['title'],
columns: ({ title }) => {
return title !== 'hidden'
? [
{
title: 'title为hidden时隐藏',
dataIndex: 'hidden',
valueType: 'date',
formItemRender: () => {
return <Input />;
},
},
]
: [];
},
},
{
title: '创建时 间',
key: 'showTime',
dataIndex: 'createName',
valueType: 'date',
},
{
valueType: 'divider',
},
];
const Demo = () => {
return (
<>
<BetaSchemaForm<DataItem>
shouldUpdate={false}
layoutType="Form"
onFinish={async () => {
}}
columns={columns}
/>
</>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
import type { ProFormColumnsType } from '@ant-design/pro-components';
import { BetaSchemaForm } from '@ant-design/pro-components';
import { Input } from 'antd';
const valueEnum = {
all: { text: '全部', status: 'Default' },
open: {
text: '未解决',
status: 'Error',
},
closed: {
text: '已解决',
status: 'Success',
disabled: true,
},
processing: {
text: '解决中',
status: 'Processing',
},
};
type DataItem = {
name: string;
state: string;
title: string;
};
const columns: ProFormColumnsType<DataItem>[] = [
{
title: '标题',
dataIndex: 'title',
initialValue: '必填',
formItemProps: {
rules: [
{
required: true,
message: '此项为必填项',
},
],
},
width: 'm',
},
{
title: '状态',
dataIndex: 'state',
valueType: 'select',
valueEnum,
width: 'm',
tooltip: '当title为disabled时状态无法选择',
fieldProps: (form) => {
if (form.getFieldValue('title') === 'disabled') {
return {
disabled: true,
placeholder: 'disabled',
};
} else {
return {
placeholder: 'normal',
};
}
},
},
{
title: '标签',
dataIndex: 'labels',
width: 'm',
tooltip: '当title为必填时此项将为必填',
dependencies: ['title'],
formItemProps(form) {
if (form.getFieldValue('title') === '必填') {
return {
rules: [
{
required: true,
},
],
};
} else {
return {};
}
},
},
{
valueType: 'dependency',
name: ['title'],
columns: ({ title }) => {
return title !== 'hidden'
? [
{
title: 'title为hidden时隐藏',
dataIndex: 'hidden',
valueType: 'date',
formItemRender: () => {
return <Input />;
},
},
]
: [];
},
},
{
title: '创建时间',
key: 'showTime',
dataIndex: 'createName',
valueType: 'date',
},
];
const Demo = () => {
return (
<>
<BetaSchemaForm<DataItem>
shouldUpdate={(newValues, oldValues) => {
if (newValues.title !== oldValues?.title) {
return true;
}
return false;
}}
layoutType="Form"
onFinish={async () => {
}}
columns={columns}
/>
</>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
#List Configuration (FormList)
Supports valueType: 'formList' to generate dynamically addable/removable list forms.
import type { ProFormColumnsType } from '@ant-design/pro-components';
import { BetaSchemaForm } from '@ant-design/pro-components';
import { Input } from 'antd';
const valueEnum = {
all: { text: '全部', status: 'Default' },
open: {
text: '未解决',
status: 'Error',
},
closed: {
text: '已解决',
status: 'Success',
disabled: true,
},
processing: {
text: '解决中',
status: 'Processing',
},
};
type DataItem = {
name: string;
state: string;
};
const columns: ProFormColumnsType<DataItem>[] = [
{
title: '标题',
dataIndex: 'title',
initialValue: '必填',
formItemProps: {
rules: [
{
required: true,
message: '此项为必填项',
},
],
},
width: 'm',
},
{
title: '必填但没显示*',
dataIndex: 'list',
valueType: 'formList',
formItemProps: {
rules: [{ required: true, message: '请填写列表' }],
},
columns: [
{
dataIndex: 'isSettlement',
valueType: 'switch',
formItemProps: {
rules: [{ required: true, message: '请填写1' }],
},
},
],
},
{
title: '状态',
dataIndex: 'state',
valueType: 'select',
valueEnum,
width: 'm',
tooltip: '当title为disabled时状态无法选择',
dependencies: ['title'],
fieldProps: (form) => {
if (form.getFieldValue('title') === 'disabled') {
return {
disabled: true,
placeholder: 'disabled',
};
} else {
return {
placeholder: 'normal',
};
}
},
},
{
title: '标签',
dataIndex: 'labels',
width: 'm',
tooltip: '当title为必填时此项将为必填',
dependencies: ['title'],
formItemProps(form) {
if (form.getFieldValue('title') === '必填') {
return {
rules: [
{
required: true,
},
],
};
} else {
return {};
}
},
},
{
valueType: 'dependency',
name: ['title'],
columns: ({ title }) => {
return title !== 'hidden'
? [
{
title: 'title为hidden时隐藏',
dataIndex: 'hidden',
valueType: 'date',
formItemRender: () => {
return <Input />;
},
},
]
: [];
},
},
{
title: '创建时间',
key: 'showTime',
dataIndex: 'createName',
valueType: 'date',
},
{
valueType: 'divider',
},
];
const Demo = () => {
return (
<>
<BetaSchemaForm<DataItem>
shouldUpdate={false}
layoutType="Form"
onFinish={async () => {
}}
columns={columns}
/>
</>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
#valueType mapping
import type { ProFieldValueType } from '@ant-design/pro-components';
import { BetaSchemaForm, ProFormSelect } from '@ant-design/pro-components';
import { useState } from 'react';
import { FIXED_BASE_TIMESTAMP } from '../../mockData';
const valueEnum = {
all: { text: '全部', status: 'Default' },
open: {
text: '未解决',
status: 'Error',
},
closed: {
text: '已解决',
status: 'Success',
disabled: true,
},
processing: {
text: '解决中',
status: 'Processing',
},
};
const options = [
{ value: `password`, label: `密码输入框`, initialValue: '123456' },
{ value: `money`, label: `金额输入`, initialValue: '123456' },
{ value: `textarea`, label: `文本域`, initialValue: '123456\n121212' },
{ value: `date`, label: `日期`, initialValue: FIXED_BASE_TIMESTAMP },
{ value: `dateTime`, label: `日期时间`, initialValue: FIXED_BASE_TIMESTAMP },
{ value: `dateWeek`, label: `周`, initialValue: FIXED_BASE_TIMESTAMP },
{ value: `dateMonth`, label: `月`, initialValue: FIXED_BASE_TIMESTAMP },
{
value: `dateQuarter`,
label: `季度输入`,
initialValue: FIXED_BASE_TIMESTAMP,
},
{ value: `dateYear`, label: `年份输入`, initialValue: FIXED_BASE_TIMESTAMP },
{
value: `dateRange`,
label: `日期区间`,
initialValue: [FIXED_BASE_TIMESTAMP, FIXED_BASE_TIMESTAMP],
},
{
value: `dateTimeRange`,
label: `日期时间区间`,
initialValue: [FIXED_BASE_TIMESTAMP, FIXED_BASE_TIMESTAMP],
},
{ value: `time`, label: `时间`, initialValue: FIXED_BASE_TIMESTAMP },
{
value: `timeRange`,
label: `时间区间`,
initialValue: [FIXED_BASE_TIMESTAMP, FIXED_BASE_TIMESTAMP],
},
{ value: `text`, label: `文本框`, initialValue: '123456' },
{ value: `select`, label: `下拉框`, initialValue: 'open' },
{ value: `checkbox`, label: `多选框`, initialValue: 'open' },
{ value: `rate`, label: `星级组件`, initialValue: '' },
{ value: `radio`, label: `单选框`, initialValue: 'open' },
{ value: `radioButton`, label: `按钮单选框`, initialValue: 'open' },
{ value: `progress`, label: `进度条`, initialValue: '10' },
{ value: `percent`, label: `百分比组件`, initialValue: '20' },
{ value: `digit`, label: `数字输入框`, initialValue: '200000' },
{ value: `second`, label: `秒格式化`, initialValue: 20000 },
{
value: `avatar`,
label: `头像`,
initialValue:
'https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg',
},
{ value: `code`, label: `代码框`, initialValue: '# 2121' },
{ value: `switch`, label: `开关`, initialValue: 'open' },
{
value: `fromNow`,
label: `相对于当前时间`,
initialValue: FIXED_BASE_TIMESTAMP,
},
{
value: `image`,
label: `图片`,
initialValue:
'https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg',
},
{
value: `jsonCode`,
label: `JSON代码框`,
initialValue: '{ "name":"demo_user" }',
},
{
value: `color`,
label: `颜色选择器`,
initialValue: '#1890ff',
},
];
type DataItem = {
name: string;
state: string;
};
const Demo = () => {
const [valueType, setValueType] = useState<ProFieldValueType>('text');
return (
<>
<ProFormSelect.SearchSelect
label="valueType 选择"
options={options}
width={200}
mode="single"
fieldProps={{
labelInValue: false,
value: valueType,
onChange: (value) => setValueType(value),
}}
/>
<BetaSchemaForm<DataItem>
name="schema-form-value-type-demo"
layoutType="Form"
onFinish={async () => {
}}
initialValues={options.reduce((pre, item) => {
return {
...pre,
[item.value]: item.initialValue,
};
}, {})}
columns={[
{
title: '分组',
valueType: 'group',
columns: [
{
valueType,
title: '编辑器',
dataIndex: valueType || 'text',
valueEnum,
formItemProps: {
rules: [
{
required: true,
message: '此项为必填项',
},
],
},
width: 'm',
},
{
valueType,
title: '只读',
dataIndex: valueType || 'text',
valueEnum,
readonly: true,
width: 'm',
},
],
},
]}
/>
</>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
#Custom valueType
import type { ProFormColumnsType } from '@ant-design/pro-components';
import { BetaSchemaForm, ProProvider } from '@ant-design/pro-components';
import type { InputRef } from 'antd';
import { Input, Space, Tag } from 'antd';
import React, { useContext, useRef, useState } from 'react';
import { DEMO_VALUE_ENUM } from '../../mockData';
const _valueEnum = {
0: 'close',
1: 'running',
2: 'online',
3: 'error',
};
export type TableListItem = {
key: number;
name: string;
status: {
label: string | number;
value: number;
}[];
};
const TagList: React.FC<{
value?: {
key: string;
label: string;
}[];
onChange?: (
value: {
key: string;
label: string;
}[],
) => void;
}> = ({ value, onChange }) => {
const ref = useRef<InputRef | null>(null);
const [newTags, setNewTags] = useState<
{
key: string;
label: string;
}[]
>([]);
const [inputValue, setInputValue] = useState<string>('');
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setInputValue(e.target.value);
};
const handleInputConfirm = () => {
let tempsTags = [...(value || [])];
if (
inputValue &&
tempsTags.filter((tag) => tag.label === inputValue).length === 0
) {
tempsTags = [
...tempsTags,
{ key: `new-${tempsTags.length}`, label: inputValue },
];
}
onChange?.(tempsTags);
setNewTags([]);
setInputValue('');
};
return (
<Space>
{(value || []).concat(newTags).map((item) => (
<Tag key={item.key}>{item.label}</Tag>
))}
<Input
ref={ref}
type="text"
size="small"
style={{ width: 78 }}
value={inputValue}
onChange={handleInputChange}
onBlur={handleInputConfirm}
onPressEnter={handleInputConfirm}
/>
</Space>
);
};
const columns: ProFormColumnsType<TableListItem, 'link' | 'tags'>[] = [
{
title: '标签',
valueType: 'group',
columns: [
{
title: '只读链接',
readonly: true,
dataIndex: 'name',
valueType: 'link',
},
{
title: '链接',
dataIndex: 'name',
valueType: 'link',
},
],
},
{
title: '路径',
valueType: 'group',
columns: [
{
title: '标签',
dataIndex: 'status',
key: 'status',
valueType: 'tags',
},
{
title: '只读标签',
readonly: true,
dataIndex: 'status',
key: 'status',
valueType: 'tags',
},
],
},
];
const Demo = () => {
const values = useContext(ProProvider);
return (
<ProProvider.Provider
value={{
...values,
valueTypeMap: {
link: {
render: (text) => <a>{text}</a>,
formItemRender: (text, props) => (
<Input placeholder="请输入链接" {...props?.fieldProps} />
),
},
tags: {
render: (text) => {
return (
<>
{[text].flat(1).map((item) => (
<Tag key={item.value}>{item.label}</Tag>
))}
</>
);
},
formItemRender: (text, props) => (
<TagList {...props} {...props?.fieldProps} />
),
},
},
}}
>
<BetaSchemaForm<TableListItem, 'link' | 'tags'>
initialValues={{
key: 1,
name: '用户认证服务',
status: [
{
value: 1,
label: DEMO_VALUE_ENUM[1],
},
{
value: 2,
label: DEMO_VALUE_ENUM[2],
},
],
}}
columns={columns}
title="自定义 valueType"
/>
</ProProvider.Provider>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);