import React, { forwardRef, useImperativeHandle, useCallback, useMemo } from 'react'; import type { ReactNode } from 'react'; import { Form, FormProps } from 'antd'; import type { FormInstance } from 'antd'; import { trimString, checkFormItemEmpty } from '@fortissimo/util'; import type { SizeType } from 'antd/lib/config-provider/SizeContext'; import { DefaultShow } from './defaultShow'; import type { RecordData, KeyType, ValueType } from '../index'; import { globalConfig, globalDefaultConfig } from '../index'; export interface OptFormFieldDetail { key: K; isLayout?: boolean; name?: string; hide?: boolean; required?: boolean; component?: ReactNode; editComponent?: ReactNode; showComponent?: ReactNode; valuePropName?: string; editValuePropName?: string; showValuePropName?: string; defaultValue?: V; normalize?: (value: V, preValue: V) => V; validator?: (value: V) => string; linkValidatorKeys?: K[]; width?: string | number; labelCol?: number; tip?: ReactNode; labelTip?: ReactNode; trim?: boolean; } export type OptFormField = OptFormFieldDetail, ValueType>; export type OptEditFormField = Partial< Omit< OptFormField, | 'editComponent' | 'showComponent' | 'editValuePropName' | 'showValuePropName' > > & { key: KeyType }; export interface OptFormFieldGroup { title: string; fields: OptFormField[]; } export type OptFormMode = 'edit' | 'show'; export interface OptFormProps { className?: string; fields?: OptFormField[]; fieldGroups?: { title: string; fields: OptFormField[]; }[]; labelCol?: number | null; colNum?: number; mode: OptFormMode; onValueChange?: (data: Partial) => void; size?: SizeType; layout?: FormProps['layout']; requiredMark?: FormProps['requiredMark']; } export interface OptFormMethods { formRef: FormInstance; reset: () => void; check: () => void; getData: () => Partial; setData: (data: Partial) => void; } export const OptForm = forwardRef(function ( { fields = [], fieldGroups = [], labelCol = 3, colNum, mode, className, onValueChange, size, layout, requiredMark }: OptFormProps, ref ) { const [formRef] = Form.useForm(); const realColNum = useMemo( () => globalConfig.optFormColNum || colNum || globalDefaultConfig.optFormColNum || 1, [colNum] ); const isShow = useMemo(() => mode === 'show', [mode]); const noTrimKeys = useMemo( () => fields .filter((item) => item && item.trim === false) .map((item) => item.key), [fields] ); useImperativeHandle( ref, (): OptFormMethods => ({ formRef, reset: () => formRef.resetFields(), check: () => formRef.validateFields(), getData: () => { const data = formRef.getFieldsValue(); let trimData: typeof data = {}; Object.keys(data).forEach((key) => { if (!noTrimKeys.includes(key)) trimData[key] = data[key]; }); trimData = trimString(trimData); return { ...data, ...trimData }; }, setData: (data) => formRef.setFieldsValue(data) }) ); const fieldItemContent = useCallback( (item: OptFormField) => { if (!isShow && item.editComponent) return item.editComponent; if (isShow && item.showComponent) return item.showComponent; return item.component || ; }, [isShow] ); const valuePropName = useCallback( (item: OptFormField) => { if (!isShow && item.editValuePropName) return item.editValuePropName; if (isShow && item.showValuePropName) return item.showValuePropName; return item.valuePropName || 'value'; }, [isShow] ); const fieldItems = useCallback( (list: OptFormField[]) => { const keyMap: Record = {}; return list .filter((item) => item) .map((item, index) => { if (!keyMap[item.key]) keyMap[item.key] = 1; else keyMap[item.key] = keyMap[item.key] + 1; const key = `${item.key}${keyMap[item.key]}`; return (
); }); }, [realColNum, layout, isShow, valuePropName, fieldItemContent] ); return (
{ onValueChange && onValueChange(values); }} > {fieldItems(fields)} {fieldGroups.map((group, index) => (
{group.title}
{fieldItems(group.fields || [])}
))}
); });