diff --git a/src/components/FormBuilder/FormItemsRenderer.d.ts b/src/components/FormBuilder/FormItemsRenderer.d.ts index 7dffad6..7b0616e 100644 --- a/src/components/FormBuilder/FormItemsRenderer.d.ts +++ b/src/components/FormBuilder/FormItemsRenderer.d.ts @@ -2,9 +2,13 @@ import type { ColProps } from "antd/es/col"; import type { FormItemProps, Rule } from "antd/es/form"; import type { FormListFieldData } from "antd/es/form/FormList"; import type { Gutter } from "antd/es/grid/row"; -import type { NamePath, Store } from "rc-field-form/lib/interface"; +import type { NamePath } from "rc-field-form/lib/interface"; import type { ReactElement, ReactNode } from "react"; import type { FORM_ITEM_RENDER_TYPE_MAP } from "../../enum/formItemRender"; +import type { DeepPartial } from "./FormBuilder"; + +export type FormListOptionName = [number, NamePath]; +export type FormListOptionDependencies = Array<(NamePath | number)[]> | NamePath; /** * 选择项数据类型 @@ -32,13 +36,13 @@ export interface ItemsFieldConfig { /** * Form.List 操作项 */ -export interface FormListOperations { +export interface FormListOperations { /** 当前表单项的数据字段信息 */ field: FormListFieldData; /** 当前项在列表中的索引位置 */ fieldIndex: number; /** 新增方法 */ - add: (defaultValue?: Store, insertIndex?: number) => void; + add: (defaultValue?: DeepPartial, insertIndex?: number) => void; /** 删除方法 */ remove: (index: number | number[]) => void; /** 移动方法 */ @@ -48,7 +52,7 @@ export interface FormListOperations { /** * Form.List 独有的属性 */ -export interface FormListUniqueProps { +export interface FormListUniqueProps { /** 是否显示新增按钮,默认 true */ showAddButton?: boolean; /** 是否显示删除按钮,默认 true */ @@ -58,9 +62,9 @@ export interface FormListUniqueProps { /** 删除按钮的文本,默认 '删除' */ removeButtonText?: string; /** 表单配置项 */ - options: (field: FormListFieldData, fieldIndex: number, operations: FormListOperations) => FormListOption[]; + options: (field: FormListFieldData, fieldIndex: number, operations: FormListOperations) => FormListOption[]; /** 点击新增按钮时的默认值 */ - addDefaultValue?: Store; + addDefaultValue?: DeepPartial; /** 点击新增按钮时插入的索引位置 */ addInsertIndex?: number; } @@ -78,7 +82,14 @@ type FormOptionProperty> { +export interface FormOptionBase< + Values = any, + AllValues = Values, + IsOnlyForLabel extends boolean = false, + IsCustomizeRender extends boolean = false, + Name = NamePath, + Dependencies = NamePath, +> { /** React 需要的 key,如果传递了唯一的 name,则不需要 */ key?: string; /** 表单项字段名 */ @@ -88,7 +99,7 @@ export interface FormOptionBase; /** 是否必填,默认 true,支持函数动态计算 */ - required?: FormOptionProperty boolean)>; + required?: FormOptionProperty boolean)>; /** 验证规则 */ rules?: FormOptionProperty; /** 是否使用字符验证限制 */ @@ -98,19 +109,19 @@ export interface FormOptionBase; /** 是否隐藏,默认 false,支持函数动态计算 */ - hidden?: WhenTrue boolean)>; + hidden?: WhenTrue boolean)>; /** 是否自定义渲染,完全交给外部控制渲染,默认 false */ customizeRender?: IsCustomizeRender; /** 传递给 Form.Item 的属性,支持函数动态计算 */ - formItemProps?: FormOptionProperty FormItemProps)>; + formItemProps?: FormOptionProperty FormItemProps)>; /** label 栅格配置,默认直接使用外层的 labelCol,如果 span 等于 24,是外层的 labelCol.span 一半 */ labelCol?: FormOptionProperty; /** wrapper 栅格配置,默认 24 - labelCol.span */ wrapperCol?: FormOptionProperty; /** 是否应该更新(用于表单联动) */ - shouldUpdate?: FormOptionProperty boolean)>; + shouldUpdate?: FormOptionProperty boolean)>; /** 依赖字段(用于表单联动) */ - dependencies?: FormOptionProperty; + dependencies?: FormOptionProperty; /** 是否仅用于保存标签,不渲染到页面上,只在表单中保存数据,默认 false */ onlyForLabel?: IsOnlyForLabel; } @@ -118,11 +129,19 @@ export interface FormOptionBase> = FormOptionBase & { +export type FormOptionByRender< + RenderType extends keyof FORM_ITEM_RENDER_TYPE_MAP, + Values = any, + AllValues = Values, + IsOnlyForLabel extends boolean = false, + IsCustomizeRender extends boolean = false, + Name = NamePath, + Dependencies = NamePath, +> = FormOptionBase & { /** 渲染类型(写字面量时 componentProps 会按该类型推导) */ render: RenderType; /** 传递给表单控件的属性,类型由 render 决定 */ - componentProps?: FormOptionProperty FORM_ITEM_RENDER_TYPE_MAP[RenderType])>; + componentProps?: FormOptionProperty FORM_ITEM_RENDER_TYPE_MAP[RenderType])>; /** 选项数据(用于 select、radio、checkbox) */ items?: FormOptionProperty; /** 字段键配置 */ @@ -130,17 +149,24 @@ export type FormOptionByRender; /** Form.List 独有的属性 */ - formListUniqueProps?: FormOptionProperty FormListUniqueProps) : never>; + formListUniqueProps?: FormOptionProperty FormListUniqueProps) : never>; }; /** * 不写 render 或 render 为 input 时的表单项(默认按输入框) */ -export type FormOptionDefault> = FormOptionBase & { +export type FormOptionDefault< + Values = any, + AllValues = Values, + IsOnlyForLabel extends boolean = false, + IsCustomizeRender extends boolean = false, + Name = NamePath, + Dependencies = NamePath, +> = FormOptionBase & { /** 渲染类型,默认 input */ render?: "input" | undefined; /** 传递给 Input 的属性 */ - componentProps?: FORM_ITEM_RENDER_TYPE_MAP["input"] | ((formValues: Values) => FORM_ITEM_RENDER_TYPE_MAP["input"]); + componentProps?: FORM_ITEM_RENDER_TYPE_MAP["input"] | ((formValues: AllValues) => FORM_ITEM_RENDER_TYPE_MAP["input"]); /** 选项数据(用于 select、radio、checkbox),input 时不需要 */ items?: never; /** 字段键配置,input 时不需要 */ @@ -154,7 +180,14 @@ export type FormOptionDefault> = FormOptionBase & { +export type FormOptionCustomRender< + Values = any, + AllValues = Values, + IsOnlyForLabel extends boolean = false, + IsCustomizeRender extends boolean = false, + Name = NamePath, + Dependencies = NamePath, +> = FormOptionBase & { /** 渲染类型,默认 ReactNode */ render: ReactNode; /** 传递给表单控件的属性,自定义渲染时不需要 */ @@ -170,32 +203,32 @@ export type FormOptionCustomRender - = | FormOptionDefault - | FormOptionDefault - | FormOptionDefault - | { [K in keyof FORM_ITEM_RENDER_TYPE_MAP]: FormOptionByRender }[keyof FORM_ITEM_RENDER_TYPE_MAP] - | { [K in keyof FORM_ITEM_RENDER_TYPE_MAP]: FormOptionByRender }[keyof FORM_ITEM_RENDER_TYPE_MAP] - | { [K in keyof FORM_ITEM_RENDER_TYPE_MAP]: FormOptionByRender }[keyof FORM_ITEM_RENDER_TYPE_MAP] - | FormOptionCustomRender - | FormOptionCustomRender - | FormOptionCustomRender; +export type FormOption + = | FormOptionDefault + | FormOptionDefault + | FormOptionDefault + | { [K in keyof FORM_ITEM_RENDER_TYPE_MAP]: FormOptionByRender }[keyof FORM_ITEM_RENDER_TYPE_MAP] + | { [K in keyof FORM_ITEM_RENDER_TYPE_MAP]: FormOptionByRender }[keyof FORM_ITEM_RENDER_TYPE_MAP] + | { [K in keyof FORM_ITEM_RENDER_TYPE_MAP]: FormOptionByRender }[keyof FORM_ITEM_RENDER_TYPE_MAP] + | FormOptionCustomRender + | FormOptionCustomRender + | FormOptionCustomRender; /** - * Form.List 子项表单配置项(使用 [number, NamePath] 作为 name 类型) + * Form.List 子项表单配置项 */ -export type FormListOption - = | FormOptionDefault]> - | FormOptionDefault]> - | FormOptionDefault]> - | { [K in keyof FORM_ITEM_RENDER_TYPE_MAP]: FormOptionByRender]> }[keyof FORM_ITEM_RENDER_TYPE_MAP] - | { [K in keyof FORM_ITEM_RENDER_TYPE_MAP]: FormOptionByRender]> }[keyof FORM_ITEM_RENDER_TYPE_MAP] - | { [K in keyof FORM_ITEM_RENDER_TYPE_MAP]: FormOptionByRender]> }[keyof FORM_ITEM_RENDER_TYPE_MAP] - | FormOptionCustomRender]> - | FormOptionCustomRender]> - | FormOptionCustomRender]>; +export type FormListOption + = | FormOptionDefault, FormListOptionDependencies> + | FormOptionDefault, FormListOptionDependencies> + | FormOptionDefault, FormListOptionDependencies> + | { [K in keyof FORM_ITEM_RENDER_TYPE_MAP]: FormOptionByRender, FormListOptionDependencies> }[keyof FORM_ITEM_RENDER_TYPE_MAP] + | { [K in keyof FORM_ITEM_RENDER_TYPE_MAP]: FormOptionByRender, FormListOptionDependencies> }[keyof FORM_ITEM_RENDER_TYPE_MAP] + | { [K in keyof FORM_ITEM_RENDER_TYPE_MAP]: FormOptionByRender, FormListOptionDependencies> }[keyof FORM_ITEM_RENDER_TYPE_MAP] + | FormOptionCustomRender, FormListOptionDependencies> + | FormOptionCustomRender, FormListOptionDependencies> + | FormOptionCustomRender, FormListOptionDependencies>; /** * FormItemsRenderer 组件属性 diff --git a/src/components/FormBuilder/FormItemsRenderer.js b/src/components/FormBuilder/FormItemsRenderer.js index 9321ee2..9f60c06 100644 --- a/src/components/FormBuilder/FormItemsRenderer.js +++ b/src/components/FormBuilder/FormItemsRenderer.js @@ -15,6 +15,7 @@ import { } from "antd"; import dayjs from "dayjs"; import { FORM_ITEM_RENDER_ENUM } from "../../enum/formItemRender"; +import { getDataType } from "../../utils"; const { TextArea } = Input; const { RangePicker } = DatePicker; @@ -211,7 +212,7 @@ const FormItemsRenderer = ({ // 获取key const getKey = (option) => { - return option.key || option.name; + return option.key || (getDataType(option.name) === "Array" ? option.name.join(".") : option.name); }; // 使用 style 控制显示/隐藏 @@ -425,7 +426,7 @@ const FormItemsRenderer = ({ }; // 渲染普通表单项 - const renderFormItem = ({ option, style, col, index }) => { + const renderFormItem = ({ option, style, col, index, preserve }) => { const formItemProps = getFormItemProps(option); delete formItemProps.dependencies; delete formItemProps.shouldUpdate; @@ -441,7 +442,7 @@ const FormItemsRenderer = ({ rules={getRules(option)} labelCol={col.labelCol} wrapperCol={col.wrapperCol} - preserve={false} + preserve={preserve} required={renderLabel(option) === " " ? false : getRequired(option.required)} colon={renderLabel(option) !== " "} {...formItemProps} @@ -453,7 +454,7 @@ const FormItemsRenderer = ({ }; // 渲染特殊类型的表单项 - const renderOtherTypeItem = ({ option, style, col, index }) => { + const renderOtherTypeItem = ({ option, style, col, index, preserve }) => { const componentProps = getComponentProps(option); // 如果是 customizeRender 类型,完全交给外部控制渲染 @@ -472,7 +473,7 @@ const FormItemsRenderer = ({ key={getKey(option) || index} name={option.name} noStyle - preserve={false} + preserve={preserve} > @@ -491,6 +492,25 @@ const FormItemsRenderer = ({ return null; }; + // 渲染需要动态更新的表单项 + const renderDynamicFormItem = ({ option, index, style, col, preserve }) => { + const formItemProps = getFormItemProps(option); + + return ( + + {() => { + return renderFormItem({ option, style, col, index, preserve }); + }} + + ); + }; + // 渲染 Form.List const renderFormList = ({ option, index, col, style }) => { const formListUniqueProps = getFormListUniqueProps(option); @@ -517,6 +537,7 @@ const FormItemsRenderer = ({ style, col, index: `${fieldIndex}_${listIndex}`, + preserve: true, }; const otherTypeItem = renderOtherTypeItem(params); if (otherTypeItem) @@ -525,6 +546,9 @@ const FormItemsRenderer = ({ if (listOption.render === FORM_ITEM_RENDER_ENUM.FORM_LIST) return renderFormList(params); + if ((listOption.shouldUpdate ?? listOption.dependencies) || (formItemProps.shouldUpdate ?? formItemProps.dependencies)) + return renderDynamicFormItem(params); + // 判断一个项是否需要显示按钮(不是 onlyForLabel 且不是隐藏的) const isShow = (opt) => { return !getHidden(opt.hidden) && !opt.onlyForLabel; @@ -619,25 +643,6 @@ const FormItemsRenderer = ({ ); }; - // 渲染需要动态更新的表单项 - const renderDynamicFormItem = ({ option, index, style, col }) => { - const formItemProps = getFormItemProps(option); - - return ( - - {() => { - return renderFormItem({ option, style, col, index }); - }} - - ); - }; - return ( <> {options.map((option, index) => { @@ -650,6 +655,7 @@ const FormItemsRenderer = ({ style, col, index, + preserve: false, }; // 处理特殊类型的表单项 diff --git a/src/components/FormBuilder/index.d.ts b/src/components/FormBuilder/index.d.ts index f5321e3..3b8b8bd 100644 --- a/src/components/FormBuilder/index.d.ts +++ b/src/components/FormBuilder/index.d.ts @@ -1,6 +1,7 @@ -import type { FormOption } from "./FormItemsRenderer"; +import type { FormListOption, FormListUniqueProps, FormOption } from "./FormItemsRenderer"; + import FormBuilder from "./FormBuilder"; -export type { FormOption }; +export type { FormListOption, FormListUniqueProps, FormOption }; export default FormBuilder; diff --git a/src/components/Search/index.d.ts b/src/components/Search/index.d.ts index 92a862b..6d66ab5 100644 --- a/src/components/Search/index.d.ts +++ b/src/components/Search/index.d.ts @@ -1,9 +1,9 @@ import type { FormInstance, FormProps } from "antd/es/form"; import type { ReactElement, ReactNode } from "react"; -import type { FormOption } from "../FormBuilder"; +import type { FormListOption, FormListUniqueProps, FormOption } from "../FormBuilder"; import type { DeepPartial } from "../FormBuilder/FormBuilder"; -export type { FormOption }; +export type { FormListOption, FormListUniqueProps, FormOption }; /** * Search 组件属性