import React, { useRef, useEffect, useImperativeHandle, useState } from 'react'; import type { FormProps, FormItemProps, FormInstance } from 'antd'; import { Spin } from 'antd'; import { ConfigProvider } from 'antd'; import { Form } from 'antd'; import { ConfigProviderWrap } from '@ant-design/pro-provider'; import type { ProFieldValueType, SearchTransformKeyFn, ProRequestData, } from '@ant-design/pro-utils'; import { conversionSubmitValue, transformKeySubmitValue, useMountMergeState, ProFormContext, runFunction, useFetchData, } from '@ant-design/pro-utils'; import { useUrlSearchParams } from '@umijs/use-params'; import type { NamePath } from 'antd/lib/form/interface'; import namePathSet from 'rc-util/lib/utils/set'; import FieldContext from '../FieldContext'; import type { SubmitterProps } from '../components/Submitter'; import Submitter from '../components/Submitter'; import type { GroupProps, FieldProps } from '../interface'; export type CommonFormProps< T extends Record = Record, U extends Record = Record, > = { submitter?: | SubmitterProps<{ form?: FormInstance; }> | false; /** * 支持异步操作,更加方便 * * @name 表单结束后调用 */ onFinish?: (formData: T) => Promise; /** @name 获取真正的可以获得值的 from */ formRef?: React.MutableRefObject< | (FormInstance & { getFieldsFormatValue?: () => T; }) | undefined >; /** @name 同步结果到 url 中 */ syncToUrl?: boolean | ((values: T, type: 'get' | 'set') => T); /** @name 额外的 url 参数 中 */ extraUrlParams?: Record; /** * 同步结果到 initialValues,默认为true如果为false,reset的时将会忽略从url上获取的数据 * * @name 是否将 url 参数写入 initialValues */ syncToInitialValues?: boolean; /** * 如果为 false,会原样保存。 * * @default true * @param 要不要值中的 Null 和 undefined */ omitNil?: boolean; /** * 格式化 Date 的方式,默认转化为 string * * @see date -> YYYY-MM-DD * @see dateTime -> YYYY-MM-DD HH:mm:SS * @see time -> HH:mm:SS */ dateFormatter?: 'number' | 'string' | false; /** 表单初始化成功,比如布局,label等计算完成 */ onInit?: (values: T) => void; /** 发起网络请求的参数 */ params?: U; /** 发起网络请求的参数,返回值会覆盖给 initialValues */ request?: ProRequestData; }; export type BaseFormProps> = { contentRender?: ( items: React.ReactNode[], submitter: React.ReactElement | undefined, form: FormInstance, ) => React.ReactNode; fieldProps?: FieldProps; onInit?: (values: T) => void; formItemProps?: FormItemProps; groupProps?: GroupProps; } & Omit & CommonFormProps; const genParams = ( syncUrl: BaseFormProps['syncToUrl'], params: Record, type: 'get' | 'set', ) => { if (syncUrl === true) { return params; } return runFunction(syncUrl, params, type); }; function BaseForm>(props: BaseFormProps) { const { children, contentRender, submitter, fieldProps, formItemProps, groupProps, dateFormatter = 'string', form: userForm, formRef: propsFormRef, onInit, extraUrlParams = {}, syncToUrl, syncToInitialValues = true, onReset, omitNil = true, ...rest } = props; const [form] = Form.useForm(); /** 同步 url 上的参数 */ const [urlSearch, setUrlSearch] = useUrlSearchParams({}); const formRef = useRef(userForm || form); const fieldsValueType = useRef< Record< string, { valueType: ProFieldValueType; dateFormat: string; } > >({}); /** 保存 transformKeyRef,用于对表单key transform */ const transformKeyRef = useRef>({}); const [loading, setLoading] = useMountMergeState(false); const items = React.Children.toArray(children); const submitterProps: SubmitterProps = typeof submitter === 'boolean' || !submitter ? {} : submitter; const transformKey = (values: any, omit: boolean, parentKey?: NamePath) => transformKeySubmitValue( conversionSubmitValue(values, dateFormatter, fieldsValueType.current, omit, parentKey), transformKeyRef.current, omit, ); const formatValues = { /** 获取格式化之后所有数据 */ getFieldsFormatValue: (nameList?: NamePath[] | true) => { return transformKey(formRef.current.getFieldsValue(nameList!), omitNil); }, /** 获取格式化之后的单个数据 */ getFieldFormatValue: (nameList?: NamePath) => { return transformKey(formRef.current.getFieldValue(nameList!), omitNil, nameList); }, }; // 初始化给一个默认的 form useImperativeHandle(propsFormRef, () => ({ ...formRef.current, ...formatValues, })); /** 渲染提交按钮与重置按钮 */ const submitterNode = submitter === false ? undefined : ( { const finalValues = transformKey(formRef.current.getFieldsValue(), omitNil); submitterProps?.onReset?.(finalValues); onReset?.(finalValues); // 如果 syncToUrl,清空一下数据 if (syncToUrl) { // 把没有的值设置为未定义可以删掉 url 的参数 const params = Object.keys( transformKey(formRef.current.getFieldsValue(), false), ).reduce((pre, next) => { return { ...pre, [next]: finalValues[next] || undefined, }; }, extraUrlParams); /** 在同步到 url 上时对参数进行转化 */ setUrlSearch(genParams(syncToUrl, params, 'set')); } }} form={userForm || form} submitButtonProps={{ loading, ...submitterProps.submitButtonProps, }} /> ); const content = contentRender ? contentRender(items, submitterNode, formRef.current) : items; useEffect(() => { const finalValues = transformKey(formRef.current.getFieldsValue(true), omitNil); onInit?.(finalValues); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); // 如果为 false,不需要触发设置进去 const [urlParamsMergeInitialValues, setUrlParamsMergeInitialValues] = useState(() => { if (!syncToUrl) { return {}; } return genParams(syncToUrl, urlSearch, 'get'); }); useEffect(() => { if (syncToInitialValues) return; window.requestAnimationFrame(() => { setUrlParamsMergeInitialValues({}); }); }, [syncToInitialValues]); useEffect(() => { if (!syncToUrl) return; setUrlSearch({ ...urlSearch, ...extraUrlParams, }); // eslint-disable-next-line react-hooks/exhaustive-deps }, [extraUrlParams, syncToUrl]); return ( // 增加国际化的能力,与 table 组件可以统一 { if (Array.isArray(name)) { transformKeyRef.current = namePathSet(transformKeyRef.current, name, transform); fieldsValueType.current = namePathSet(fieldsValueType.current, name, { valueType, dateFormat, }); } }, }} >
{ if (event.key === 'Enter') { formRef.current?.submit(); } }} form={userForm || form} {...rest} // 组合 urlSearch 和 initialValues initialValues={{ ...urlParamsMergeInitialValues, ...rest.initialValues, }} onValuesChange={(changedValues, values) => { rest?.onValuesChange?.( transformKey(changedValues, omitNil), transformKey(values, omitNil), ); }} onFinish={async () => { if (!rest.onFinish) { return; } setLoading(true); try { const finalValues = transformKey(formRef.current.getFieldsValue(), omitNil); await rest.onFinish(finalValues); if (syncToUrl) { // 把没有的值设置为未定义可以删掉 url 的参数 const params = Object.keys( transformKey(formRef.current.getFieldsValue(), false), ).reduce((pre, next) => { return { ...pre, [next]: finalValues[next] || undefined, }; }, extraUrlParams); /** 在同步到 url 上时对参数进行转化 */ setUrlSearch(genParams(syncToUrl, params, 'set')); } setLoading(false); } catch (error) { // console.log(error); setLoading(false); } }} > {rest.component !== false && ( )} {(formInstance) => { if (propsFormRef) propsFormRef.current = { ...(formInstance as FormInstance), ...formatValues, }; formRef.current = formInstance as FormInstance; return null; }} {content}
); } function RequestForm>(props: BaseFormProps) { const { request, params, initialValues, ...rest } = props; const [initialData] = useFetchData({ request, params, }); if (!initialData && props.request) { return (
); } return ( ); } export type { FormProps, FormItemProps, FormInstance }; export default RequestForm;