关键hooks API: useImperativeHandle、useRef,高阶组件: forwardRef,
import React, { ReactNode } from 'react';
import { IButton } from '@/types/antd/button';
// @ts-ignore
import { FilterFunc, GetFieldsValueConfig } from 'rc-field-form/es/interface';
type RadioGroupOption = {
buttonStyle?: 'outline' | 'solid';
name?: string;
options?: any[];
optionType?: 'default' | 'button',
size?: 'large' | 'middle' | 'small',
}
type SelectOptions = {
mode?: 'multiple' | 'tags';
options?: any[];
}
export type FormOptions = {
// 表单名称,会作为表单字段 id 前缀使用
name: string;
labelCol?: number;
wrapperCol?: number;
autoComplete?: string;
// label 是否显示冒号
colon?: boolean;
// label 标签的文本对齐方式
labelAlign?: 'right' | 'left';
// 表单布局
layout?: 'horizontal' | 'vertical' | 'inline';
// 设置字段组件的尺寸(仅限 antd 组件)
size?: 'small' | 'middle' | 'large';
// initialValues?: {[index: string]: any}
}
export type FormItemOptions = {
// 是否显示 label 后面的冒号
colon?: boolean;
// label 标签的文本
label?: ReactNode | string;
placeholder?: string;
name: string; // use is key
// 标签文本对齐方式
labelAlign?: 'left' | 'right';
labelCol?: number;
wrapperCol?: number;
noStyle?: boolean;
required?: boolean;
// 设置防抖,延迟毫秒数后进行校验
validateDebounce?: number;
// 设置字段校验的时机 : onChange
validateTrigger?: string | string[];
// input 图标
prefix?: React.ReactElement;
formItemType: string;
rules?: {[index: string]: string | boolean}[];
formButtons?: IButton[],
rows?: number;
allowClear?: boolean;
selectOptions?: SelectOptions;
radioOptions?: RadioGroupOption;
}
export interface IFormProps {
formOptions: FormOptions;
formValue: {[index: string]: any};
formItemOptions: FormItemOptions[];
emitSubmit?: (formData: any) => void;
}
// SystemForm 组件暴露的数据结构
export interface IFormCompExportData {
getFieldsValue: (() => any) & ((nameList: (true | any[]), filterFunc?: (FilterFunc | undefined)) => any) & ((config: GetFieldsValueConfig) => any)
}
子组件:Form的二次封装组件
import React, { forwardRef, useImperativeHandle } from 'react';
import { IFormCompExportData, IFormProps } from '@/types/antd/form';
import createFormIpt from './createFormIpt';
import { Form } from 'antd';
const SystemForm = (props: IFormProps, ref: React.Ref<any>) => {
const {
formOptions,
formItemOptions,
emitSubmit,
formValue,
} = props;
const [form] = Form.useForm();
// useImperativeHandle: 细化ref暴露的实例粒度
useImperativeHandle(ref, (): IFormCompExportData=>({
// 这里可以暴露SystemForm组件的所有内容 变量、方法、元素实例
// 避免暴露出 完整的Form表单实例form 这里选择暴露获取字段value方法getFieldsValue的引用
getFieldsValue: form.getFieldsValue
}), [])
const onFinish = (values: any) => {
emitSubmit && emitSubmit(values);
};
return (
<Form
form={form}
name={formOptions.name}
labelCol={{ span: formOptions.labelCol }}
wrapperCol={{ span: formOptions.wrapperCol }}
initialValues={formValue}
autoComplete={formOptions.autoComplete}
size={formOptions.size}
onFinish={onFinish}
>
{
formItemOptions.map(item => {
return (
<Form.Item
key={item.name}
label={item.label}
name={item.name}
rules={item.rules}
>
{/*<CreateFormIpt formItem={item} />*/}
{createFormIpt(item)}
</Form.Item>
);
})
}
</Form>
);
};
// 因为有forwardRef包裹,所以SystemForm组件才可以使用第二个参数ref
export default forwardRef(SystemForm);
import { FormItemOptions } from '@/types/antd/form';
import React from 'react';
import { Button, Input, Select, Radio } from 'antd';
const { TextArea} = Input;
const createFormIpt = (formItem: FormItemOptions) => {
if(formItem.name === 'password') {
return (
<Input type="password" prefix={formItem.prefix} placeholder={formItem.placeholder}/>
)
}
const iptRenderMapByFormItemType: {[index: string]: React.JSX.Element} = {
'input': <Input prefix={formItem.prefix} placeholder={formItem.placeholder} allowClear={formItem.allowClear}/>,
'textarea':<TextArea rows={formItem.rows} placeholder={formItem.placeholder} allowClear={formItem.allowClear} />,
'radio': <Radio.Group options={formItem.radioOptions?.options}/>,
'select': <Select
placeholder={formItem.placeholder}
mode={formItem.selectOptions?.mode}
allowClear={formItem.allowClear}
options={formItem.selectOptions?.options}
/>,
'button': <>
{formItem.formButtons?.map(itemBtn=>(
<Button
key={itemBtn.key}
type={itemBtn.buttonType}
loading={itemBtn.btnLoadingStatus}
block={itemBtn.block}
htmlType={itemBtn.htmlType}
>
{itemBtn.btnDesc}
</Button>
))}
</>,
}
return iptRenderMapByFormItemType[formItem.formItemType]
}
export default createFormIpt;
import { FormOptions, FormItemOptions } from '@/types/antd/form';
export const publishArticleFormValue = {
'title': '11s',
'content': '',
'summary': '',
'categoryId': '',
'tags': [],
'isComment': '0',
'isTop': '0',
'thumbnail': '',
}
// 文章表单
export const publishArticleFormOptions: FormOptions = {
name: 'publishArticle',
autoComplete: 'off',
size: 'large',
labelCol: 4,
wrapperCol: 20,
}
export const publishArticleFormData: FormItemOptions[] = [
{
label: '文章标题',
name: 'title',
placeholder: '请输入文章标题',
formItemType: 'input',
allowClear: true,
},
{
label: '文章摘要',
name: 'summary',
placeholder: '请输入文章摘要',
formItemType: 'textarea',
allowClear: true,
},
{
label: '分类',
name: 'categoryId',
placeholder: '请选择',
formItemType: 'select',
selectOptions: {
},
allowClear: true,
},
{
label: '标签',
name: 'tags',
placeholder: '请选择',
formItemType: 'select',
selectOptions: {
mode: 'multiple'
},
allowClear: true,
},
{
label: '允许评论',
name: 'isComment',
formItemType: 'radio',
radioOptions: {
options: [
{ label: '正常', value: '1' },
{ label: '停用', value: '0' }
]
}
},
{
label: '是否置顶',
name: 'isTop',
formItemType: 'radio',
radioOptions: {
options: [
{ label: '是', value: '1' },
{ label: '否', value: '0' }
]
}
},
]
import React, { useRef, useState } from 'react';
import { PageContainer } from '@ant-design/pro-components';
import {
publishArticleFormData,
publishArticleFormOptions,
publishArticleFormValue,
} from '@/pages/PublishArticle/data';
import { IFormCompExportData } from '@/types/antd/form';
const PublishArticle: React.FC = () => {
// useRef() 其 .current 属性被初始化为传入的参数(initialValue)
// 所以useRef的初始化数据类型和useImperativeHandle返回的handle对象数据类型是一致的
const publishArticleFormRef = useRef<IFormCompExportData>(null);
const test = () => {
const { getFieldsValue } = publishArticleFormRef.current as IFormCompExportData;
// 获取表单的所有值getFieldsValue(true)
console.log('表单收集的值', getFieldsValue(true));
}
return (
<PageContainer>
<BaseForm
formOptions={publishArticleFormOptions}
formItemOptions={publishArticleFormData}
formValue={publishArticleFormValue}
ref={publishArticleFormRef}
/>
<Button type="primary" onClick={test}>获取表单动态值</Button>
</PageContainer>
);
};
export default PublishArticle;