diff --git a/packages/vantui-doc/src/form/README.md b/packages/vantui-doc/src/form/README.md index f7a40802f..79327d43d 100644 --- a/packages/vantui-doc/src/form/README.md +++ b/packages/vantui-doc/src/form/README.md @@ -156,6 +156,65 @@ function Demo() { } ``` +### 支持多层级数据结构 + +FormItem 的 name 属性支持数组的形式, 数组项为字符串的时候挂载到对象上,为数字的时候挂载到数组上 +第一层固定为对象 + +```jsx +function Demo() { + const formIt = react.useRef(null) + + const multFormItems = function () { + let jsx = [] + for (let i = 0; i < 2; i++) { + jsx.push( + <> + e.detail.value} + > + + + e.detail.value} + > + + + , + ) + } + return jsx + } + + return ( +
+ {multFormItems()} + + + + ) +} +``` + ### 异步处理和自定义校验 - Uploader 的 onAfterRead 事件只返回变更的文件,展示的是多个文件的话需要重新设置 diff --git a/packages/vantui/src/form-item/index.tsx b/packages/vantui/src/form-item/index.tsx index 645f3999b..f5e88ac40 100644 --- a/packages/vantui/src/form-item/index.tsx +++ b/packages/vantui/src/form-item/index.tsx @@ -37,6 +37,7 @@ export function FormItem(props: FormItemProps) { const formInstance = useContext(FormContext) const { registerValidateFields, dispatch, unRegisterValidate } = formInstance const [, forceUpdate_] = useState({}) + const _name = Array.isArray(name) ? name.join('.') : name const onStoreChange = useMemo(() => { const onStoreChange = { @@ -50,29 +51,29 @@ export function FormItem(props: FormItemProps) { useEffect(() => { /* 注册表单 */ - name && - registerValidateFields(name, onStoreChange, { rules, required, label }) + _name && + registerValidateFields(_name, onStoreChange, { rules, required, label }) return function () { - name && unRegisterValidate(name) + _name && unRegisterValidate(_name) } // eslint-disable-next-line react-hooks/exhaustive-deps }, [onStoreChange]) const getControlled = (child: any) => { const props = { ...child.props } - if (!name) return props + if (!_name) return props const trigger_ = props[trigger] const handleChange = async (e: any) => { let value = null if (valueFormat) { - value = await valueFormat(e, name, formInstance) + value = await valueFormat(e, _name, formInstance) } else { value = e.detail } - dispatch({ type: 'setFieldsValue' }, name, value) + dispatch({ type: 'setFieldsValue' }, _name, value) if (trigger_) trigger_(e) } props[trigger] = handleChange @@ -82,10 +83,10 @@ export function FormItem(props: FormItemProps) { await handleChange(e) } - dispatch({ type: 'validateFieldValue' }, name) + dispatch({ type: 'validateFieldValue' }, _name) } } - props[valueKey] = dispatch({ type: 'getFieldValue' }, name) + props[valueKey] = dispatch({ type: 'getFieldValue' }, _name) return props } @@ -112,7 +113,7 @@ export function FormItem(props: FormItemProps) { diff --git a/packages/vantui/src/form/core/formstore.ts b/packages/vantui/src/form/core/formstore.ts index 20cbdf17d..27ceb8530 100644 --- a/packages/vantui/src/form/core/formstore.ts +++ b/packages/vantui/src/form/core/formstore.ts @@ -3,6 +3,7 @@ import { unstable_batchedUpdates } from 'react-dom' import { IFormInstanceAPI } from '../../../types/form' type IAPI = keyof IFormInstanceAPI +type Iname = string | Array /* 对外接口 */ const formInstanceApi = [ @@ -82,10 +83,11 @@ class FormStore { } registerValidateFields( - name: string, + name_: Iname, control: Record, model: Record, ): void { + const name = Array.isArray(name_) ? name_.join('.') : name_ if (this.defaultFormValue[name]) model['value'] = this.defaultFormValue[name] const validate = FormStore.createValidate(model) @@ -93,12 +95,14 @@ class FormStore { this.control[name] = control } - unRegisterValidate(name: string) { + unRegisterValidate(name_: Iname) { + const name = Array.isArray(name_) ? name_.join('.') : name_ delete this.model[name] delete this.control[name] } - notifyChange(name: string) { + notifyChange(name_: Iname) { + const name = Array.isArray(name_) ? name_.join('.') : name_ const controller = this.control[name] if (controller) controller?.changeValue() } @@ -110,7 +114,8 @@ class FormStore { }) } - setFieldsValue(name: string, modelValue: any): any { + setFieldsValue(name_: Iname, modelValue: any): any { + const name = Array.isArray(name_) ? name_.join('.') : name_ const model = this.model[name] if (!model) return false if (toString.call(modelValue) === '[Object, object]' && modelValue.value) { @@ -125,18 +130,62 @@ class FormStore { } } - setValueClearStatus(model: Record, name: string, value: any) { + setValueClearStatus(model: Record, name_: Iname, value: any) { + const name = Array.isArray(name_) ? name_.join('.') : name_ + model['value'] = value model['status'] = 'pendding' this.notifyChange(name) } + static transformMultilevelData(data: Record) { + const keys = Object.keys(data) + const hasMultiLevel = keys.some((item) => item.includes('.')) + if (hasMultiLevel) { + const res: Record = {} + for (let i = 0; i < keys.length; i++) { + const key = keys[i] + if (key) { + if (!key?.includes('.')) { + res[key] = data[key] + } else { + const ks = key.split('.') + let p = res + for (let j = 0; j < ks.length; j++) { + const k = ks[j] as string | number + if (p[k] && j !== ks.length - 1) { + p = p[k] + } else if (!p[k] && j !== ks.length - 1) { + const nextK = ks[j + 1] as string | number + if ( + !isNaN(Number(nextK)) && + typeof Number(nextK) === 'number' + ) { + p[k] = [] + } else if (typeof nextK === 'string') { + p[k] = {} + } + p = p[k] + } else { + p[k] = data[key] + } + } + } + } + } + + return res + } else { + return data + } + } + getFieldsValue() { const formData: any = {} Object.keys(this.model).forEach((modelName) => { formData[modelName] = this.model[modelName].value }) - return formData + return FormStore.transformMultilevelData(formData) } resetFields() { @@ -145,19 +194,25 @@ class FormStore { }) } - getFieldModel(name: string) { + getFieldModel(name_: Iname) { + const name = Array.isArray(name_) ? name_.join('.') : name_ + const model = this.model[name] return model ? model : {} } - getFieldValue(name: string) { + getFieldValue(name_: Iname) { + const name = Array.isArray(name_) ? name_.join('.') : name_ + const model = this.model[name] if (!model && this.defaultFormValue[name]) return this.defaultFormValue[name] return model ? model.value : null } - validateFieldValue(name: string, forceUpdate = true) { + validateFieldValue(name_: Iname, forceUpdate = true) { + const name = Array.isArray(name_) ? name_.join('.') : name_ + const model = this.model[name] /* 记录上次状态 */ const lastStatus = model.status diff --git a/packages/vantui/types/form.d.ts b/packages/vantui/types/form.d.ts index c69264433..72f5cad94 100644 --- a/packages/vantui/types/form.d.ts +++ b/packages/vantui/types/form.d.ts @@ -38,7 +38,7 @@ export interface FormItemProps extends StandardProps { /** * @description 对应表单字段名 */ - name: string + name: string | Array /** * @description 第一级操作表单组件 */ @@ -107,7 +107,7 @@ export interface FormItemProps extends StandardProps { */ valueFormat?: ( value: any, - name: string, + name: string | Array, IFormInstance: IFormInstanceAPI, ) => any /** @@ -127,7 +127,7 @@ export type IFormInstanceAPI = { * @description 注册校验规则 */ registerValidateFields: ( - name: string, + name: string | Array, control: Record, model: Record, ) => void @@ -138,7 +138,7 @@ export type IFormInstanceAPI = { /** * @description 注册校验规则 */ - unRegisterValidate: (name: string) => void + unRegisterValidate: (name: string | Array) => void /** * @description 重置表单 */ @@ -150,7 +150,10 @@ export type IFormInstanceAPI = { /** * @description 设置单个表单值 */ - setFieldsValue: (name: string, modelValue: any) => any + setFieldsValue: ( + name: string | Array, + modelValue: any, + ) => any /** * @description 获取所有表单值 */ @@ -158,7 +161,7 @@ export type IFormInstanceAPI = { /** * @description 获取单个表单值 */ - getFieldValue: (name: string) => any + getFieldValue: (name: string | Array) => any /** * @description 校验表单,并获取错误信息和所有表单值 */ @@ -175,7 +178,11 @@ export type IFormInstanceAPI = { ) => void, ) => void } & { - dispatch: (params: { type: string }, name?: string, ...arg: any[]) => any + dispatch: ( + params: { type: string }, + name?: string | Array, + ...arg: any[] + ) => any setCallback: Record< string, undefined | ((values?: Record) => void)