#Component Overview
ProComponents was developed to reduce the cost of implementing CRUD in the middle and backend, with the idea of reducing the necessary state maintenance and focusing more on the business.
- ProLayout solves the layout problem and provides out-of-the-box menu and breadcrumb functionality
- ProTable solves table issues, abstracts web requests and table formatting
- ProForm solves form issues, pre-defines common layouts and behaviors
- ProCard provides card slicing and raster layout capabilities
- ProDescriptions provides the ability to use the same configuration as a table
- ProSkeleton Page level skeleton screen
#Form layout toggle
The main feature of ProForm is that it has a lot of pre-defined layouts, so if you need to switch you just need to change the Layout of the outer wrapper, here is a demo.
import {
AlipayCircleOutlined,
LockOutlined,
PlusOutlined,
TaobaoCircleOutlined,
UserOutlined,
WeiboCircleOutlined,
} from '@ant-design/icons';
import {
DrawerForm,
LightFilter,
LoginForm,
ModalForm,
ProForm,
ProFormDateRangePicker,
ProFormDigit,
ProFormRadio,
ProFormSelect,
ProFormText,
QueryFilter,
StepsForm,
} from '@ant-design/pro-components';
import { Button, Space, message } from 'antd';
import { useState } from 'react';
const iconStyles = {
marginInlineStart: '16px',
color: 'rgba(0, 0, 0, 0.2)',
fontSize: '24px',
verticalAlign: 'middle',
cursor: 'pointer',
};
const waitTime = (time: number = 100) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(true);
}, time);
});
};
const Demo = () => {
const Components = {
ProForm,
ModalForm,
DrawerForm,
QueryFilter,
LightFilter,
StepsForm,
LoginForm,
};
const [type, setType] = useState<keyof typeof Components>('ProForm');
if (type === 'StepsForm') {
return (
<>
<ProFormRadio.Group
style={{
margin: 16,
}}
radioType="button"
fieldProps={{
value: type,
onChange: (e) => setType(e.target.value),
}}
options={[
'LightFilter',
'ProForm',
'ModalForm',
'DrawerForm',
'QueryFilter',
'StepsForm',
'LoginForm',
]}
/>
<StepsForm
onFinish={async () => {
await waitTime(2000);
message.success('Submission successful');
}}
>
<StepsForm.StepForm title="Step One">
<ProForm.Group>
<ProFormText
width="md"
name="name"
label="Contract Customer Name"
tooltip="Up to 24 characters"
placeholder="Please enter a name"
/>
<ProFormText
width="md"
name="company"
label="Our Company Name"
placeholder="Please enter a name"
/>
</ProForm.Group>
<ProForm.Group>
<ProFormText
name={['contract', 'name']}
width="md"
label="Contract Name"
placeholder="Please enter a name"
/>
<ProFormDateRangePicker
width="md"
name={['contract', 'createTime']}
label="Contract Effective Time"
/>
</ProForm.Group>
</StepsForm.StepForm>
<StepsForm.StepForm title="Step Two">
<ProForm.Group>
<ProFormSelect
options={[
{
value: 'chapter',
label: 'Effective after stamping',
},
]}
readonly
width="xs"
name="useMode"
label="Contract Agreed Effective Method"
/>
<ProFormSelect
width="xs"
options={[
{
value: 'time',
label: 'Terminate after performance',
},
]}
name="unusedMode"
label="Contract Agreed Invalid Method"
/>
</ProForm.Group>
</StepsForm.StepForm>
<StepsForm.StepForm title="Step Three">
<ProFormText width="sm" name="id" label="Main Contract Number" />
<ProFormText
name="project"
width="md"
disabled
label="Project Name"
initialValue="示例项目"
/>
<ProFormText
width="xs"
name="mangerName"
disabled
label="Business Manager"
initialValue="书琰"
/>
</StepsForm.StepForm>
</StepsForm>
</>
);
}
const FormComponents = Components[type as 'LoginForm'];
if (type === 'LoginForm') {
return (
<>
<ProFormRadio.Group
style={{
margin: 16,
}}
radioType="button"
fieldProps={{
value: type,
onChange: (e) => setType(e.target.value),
}}
options={[
'LightFilter',
'ProForm',
'ModalForm',
'DrawerForm',
'QueryFilter',
'StepsForm',
'LoginForm',
]}
/>
<FormComponents
title="Github"
subTitle="The world's largest code hosting platform"
actions={
<Space>
Other login methods
<AlipayCircleOutlined style={iconStyles} />
<TaobaoCircleOutlined style={iconStyles} />
<WeiboCircleOutlined style={iconStyles} />
</Space>
}
>
<ProFormText
name="username"
fieldProps={{
size: 'large',
prefix: <UserOutlined className={'prefixIcon'} />,
}}
placeholder={'Username: admin or user'}
rules={[
{
required: true,
message: 'Please enter your username!',
},
]}
/>
<ProFormText.Password
name="password"
fieldProps={{
size: 'large',
prefix: <LockOutlined className={'prefixIcon'} />,
}}
placeholder={'Password: ant.design'}
rules={[
{
required: true,
message: 'Please enter your password!',
},
]}
/>
</FormComponents>
</>
);
}
return (
<>
<ProFormRadio.Group
style={{
margin: 16,
}}
radioType="button"
fieldProps={{
value: type,
onChange: (e) => setType(e.target.value),
}}
options={[
'LightFilter',
'ProForm',
'ModalForm',
'DrawerForm',
'QueryFilter',
'StepsForm',
'LoginForm',
]}
/>
<div
style={{
margin: 24,
}}
>
<FormComponents
// @ts-ignore
labelWidth="auto"
trigger={
<Button type="primary">
<PlusOutlined />
New Form
</Button>
}
onFinish={async () => {
await waitTime(500);
message.success('Submission successful');
}}
initialValues={{
name: 'Ant Design Co., Ltd.',
useMode: 'chapter',
}}
>
<ProForm.Group>
<ProFormText
width="md"
name="name"
label="Contract Customer Name"
tooltip="Up to 24 characters"
placeholder="Please enter a name"
/>
<ProFormText
width="md"
name="company"
label="Our Company Name"
placeholder="Please enter a name"
/>
</ProForm.Group>
<ProForm.Group>
<ProFormText
name={['contract', 'name']}
width="md"
label="Contract Name"
placeholder="Please enter a name"
/>
<ProFormDateRangePicker
width="md"
name={['contract', 'createTime']}
label="Contract Effective Time"
/>
</ProForm.Group>
<ProForm.Group>
<ProFormSelect
options={[
{
value: 'chapter',
label: 'Effective after stamping',
},
]}
readonly
width="xs"
name="useMode"
label="Contract Agreed Effective Method"
/>
<ProFormSelect
width="xs"
options={[
{
value: 'time',
label: 'Terminate after performance',
},
]}
name="unusedMode"
label="Contract Agreed Invalid Method"
/>
</ProForm.Group>
<ProFormText width="sm" name="id" label="Main Contract Number" />
<ProFormText
name="project"
width="md"
disabled
label="Project Name"
initialValue="示例项目"
/>
<ProFormText
width="xs"
name="mangerName"
disabled
label="Business Manager"
initialValue="书琰"
/>
<ProFormDigit
name="percent"
label="Percent"
width="xs"
min={1}
max={10}
fieldProps={{
precision: 2,
addonAfter: '%',
}}
/>
</FormComponents>
</div>
</>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
#Configuring Use with the Web Request Library
ProTable, ProList uses a new data structure which is very easy to use if you use the parameters we have agreed upon.
const msg: {
data: T[];
page: number;
success: boolean;
total: number;
} = {
data: [],
page: 1,
success: true,
total: 0,
};If your backend data uses a familiar url, we could use a request to convert it, but it would be a pain to configure each table. If you're using umi's request, we can define a global transformer. We need to configure this in app.tsx
import type { RequestConfig } from 'umi';
export const request: RequestConfig = {
errorConfig: {
adaptor: (resData) => {
return {
...resData,
total: resData.sum,
success: resData.ok,
errorMessage: resData.message,
};
},
},
};When using UMI request:
import { request } from 'umi';
<ProTable request={request('/list')} />;If fetch is used, you can customize fetch.
const request = (url, options) => {
return fetch(url, options)
.then((res) => res.json())
.then((resData) => {
return Promise.resolve({
...resData,
total: resData.sum,
success: resData.ok,
errorMessage: resData.message,
});
});
};
// when used
<ProTable request={request('/list')} />;#General Configuration
ProTable, ProDescriptions share a common set of configurations that can use the same columns and requests to generate data, the only difference being that Table requires an array, while ProDescriptions only requires an object. Here are the specific configurations.
/**
* Commonly supported render for each component
*/
export type ProSchema<T = unknown, U = string, Extra = unknown> = {
/**
* @name Determines the unique value of this column
*/
key?: (string | number);
/**
* @name The key mapped to the entity
* @description supports a number, [a,b] will be converted to obj.a.b
*/
dataIndex?: string | number | (string | number)[];
/**
* Select how to render the corresponding pattern
*/
valueType?: ((entity: T, type: ProSchemaComponentTypes) => U) | U;
/**
* @name title
* @description supports ReactNode and methods
*/
title?:
| ((
schema: ProSchema<T, U, Extra>,
type: ProSchemaComponentTypes,
dom: React.ReactNode,
ReactNode, ) => React.)
| ReactNode;
/**
*@name shows an icon, hover shows some hints
*/
tooltip?: LabelTooltipType | string;
render?: (
dom: React.ReactNode,
entity: T,
index: number,
action: ProCoreActionType,
schema: ProSchema<T, U, Extra>,
) => React.ReactNode;
/**
* @name Customize the edit schema
* @description returns a node that will automatically wrap value and onChange
*/
formItemRender?: (
item: ProSchema<T, U, Extra>,
config: {
index?: number;
value?: any;
onSelect?: (value: any) => void;
type: ProSchemaComponentTypes;
defaultRender: (newItem: ProSchema<T, U, Extra>) => JSX.Element | null;
},
form: FormInstance,
) => React.ReactNode;
/**
* @name Custom render
* @description must return string
*/
renderText?: (text: any, record: T, index: number, action: ProCoreActionType) => any;
fieldProps?: any;
/**
* @name The type of the mapped value
*/
valueEnum?: ProSchemaValueEnumObj | ProSchemaValueEnumMap;
/**
* @name request enum from server
*/
request?: ProFieldRequestData<ProSchema>;
/**
* @name Parameter requested from the server, changes will trigger a reload
*/
params?: {
[key: string]: any;
};
/**
* @name hidden in descriptions
*/
hideInDescriptions?: boolean;
} & Extra;