diff --git a/packages/@core/ui-kit/form-ui/src/form-api.ts b/packages/@core/ui-kit/form-ui/src/form-api.ts index 6a440368..0ae8ed77 100644 --- a/packages/@core/ui-kit/form-ui/src/form-api.ts +++ b/packages/@core/ui-kit/form-ui/src/form-api.ts @@ -295,6 +295,7 @@ export class FormApi { return true; }); const filteredFields = fieldMergeFn(fields, form.values); + this.handleStringToArrayFields(filteredFields); form.setValues(filteredFields, shouldValidate); } @@ -304,6 +305,7 @@ export class FormApi { const form = await this.getForm(); await form.submitForm(); const rawValues = toRaw(await this.getValues()); + this.handleArrayToStringFields(rawValues); await this.state?.handleSubmit?.(rawValues); return rawValues; @@ -392,10 +394,53 @@ export class FormApi { return this.form; } + private handleArrayToStringFields = (originValues: Record) => { + const arrayToStringFields = this.state?.arrayToStringFields; + if (!arrayToStringFields || !Array.isArray(arrayToStringFields)) { + return; + } + + const processFields = (fields: string[], separator: string = ',') => { + this.processFields(fields, separator, originValues, (value, sep) => + Array.isArray(value) ? value.join(sep) : value, + ); + }; + + // 处理简单数组格式 ['field1', 'field2', ';'] 或 ['field1', 'field2'] + if (arrayToStringFields.every((item) => typeof item === 'string')) { + const lastItem = + arrayToStringFields[arrayToStringFields.length - 1] || ''; + const fields = + lastItem.length === 1 + ? arrayToStringFields.slice(0, -1) + : arrayToStringFields; + const separator = lastItem.length === 1 ? lastItem : ','; + processFields(fields, separator); + return; + } + + // 处理嵌套数组格式 [['field1'], ';'] + arrayToStringFields.forEach((fieldConfig) => { + if (Array.isArray(fieldConfig)) { + const [fields, separator = ','] = fieldConfig; + // 根据类型定义,fields 应该始终是字符串数组 + if (!Array.isArray(fields)) { + console.warn( + `Invalid field configuration: fields should be an array of strings, got ${typeof fields}`, + ); + return; + } + processFields(fields, separator); + } + }); + }; + private handleRangeTimeValue = (originValues: Record) => { const values = { ...originValues }; const fieldMappingTime = this.state?.fieldMappingTime; + this.handleStringToArrayFields(values); + if (!fieldMappingTime || !Array.isArray(fieldMappingTime)) { return values; } @@ -441,6 +486,80 @@ export class FormApi { return values; }; + private handleStringToArrayFields = (originValues: Record) => { + const arrayToStringFields = this.state?.arrayToStringFields; + if (!arrayToStringFields || !Array.isArray(arrayToStringFields)) { + return; + } + + const processFields = (fields: string[], separator: string = ',') => { + this.processFields(fields, separator, originValues, (value, sep) => { + if (typeof value !== 'string') { + return value; + } + // 处理空字符串的情况 + if (value === '') { + return []; + } + // 处理复杂分隔符的情况 + const escapedSeparator = sep.replaceAll( + /[.*+?^${}()|[\]\\]/g, + String.raw`\$&`, + ); + return value.split(new RegExp(escapedSeparator)); + }); + }; + + // 处理简单数组格式 ['field1', 'field2', ';'] 或 ['field1', 'field2'] + if (arrayToStringFields.every((item) => typeof item === 'string')) { + const lastItem = + arrayToStringFields[arrayToStringFields.length - 1] || ''; + const fields = + lastItem.length === 1 + ? arrayToStringFields.slice(0, -1) + : arrayToStringFields; + const separator = lastItem.length === 1 ? lastItem : ','; + processFields(fields, separator); + return; + } + + // 处理嵌套数组格式 [['field1'], ';'] + arrayToStringFields.forEach((fieldConfig) => { + if (Array.isArray(fieldConfig)) { + const [fields, separator = ','] = fieldConfig; + if (Array.isArray(fields)) { + processFields(fields, separator); + } else if (typeof originValues[fields] === 'string') { + const value = originValues[fields]; + if (value === '') { + originValues[fields] = []; + } else { + const escapedSeparator = separator.replaceAll( + /[.*+?^${}()|[\]\\]/g, + String.raw`\$&`, + ); + originValues[fields] = value.split(new RegExp(escapedSeparator)); + } + } + } + }); + }; + + private processFields = ( + fields: string[], + separator: string, + originValues: Record, + transformFn: (value: any, separator: string) => any, + ) => { + fields.forEach((field) => { + const value = originValues[field]; + if (value === undefined || value === null) { + return; + } + originValues[field] = transformFn(value, separator); + }); + }; + private updateState() { const currentSchema = this.state?.schema ?? []; const prevSchema = this.prevState?.schema ?? []; diff --git a/packages/@core/ui-kit/form-ui/src/types.ts b/packages/@core/ui-kit/form-ui/src/types.ts index 82d31af0..da706591 100644 --- a/packages/@core/ui-kit/form-ui/src/types.ts +++ b/packages/@core/ui-kit/form-ui/src/types.ts @@ -232,6 +232,12 @@ export type FieldMappingTime = [ )?, ][]; +export type ArrayToStringFields = Array< + | [string[], string?] // 嵌套数组格式,可选分隔符 + | string // 单个字段,使用默认分隔符 + | string[] // 简单数组格式,最后一个元素可以是分隔符 +>; + export interface FormSchema< T extends BaseFormComponentType = BaseFormComponentType, > extends FormCommonConfig { @@ -266,6 +272,10 @@ export interface FormFieldProps extends FormSchema { export interface FormRenderProps< T extends BaseFormComponentType = BaseFormComponentType, > { + /** + * 表单字段数组映射字符串配置 默认使用"," + */ + arrayToStringFields?: ArrayToStringFields; /** * 是否展开,在showCollapseButton=true下生效 */ @@ -296,6 +306,10 @@ export interface FormRenderProps< * 组件集合 */ componentMap: Record; + /** + * 表单字段映射到时间格式 + */ + fieldMappingTime?: FieldMappingTime; /** * 表单实例 */ @@ -308,10 +322,15 @@ export interface FormRenderProps< * 表单定义 */ schema?: FormSchema[]; + /** * 是否显示展开/折叠 */ showCollapseButton?: boolean; + /** + * 格式化日期 + */ + /** * 表单栅格布局 * @default "grid-cols-1" @@ -339,6 +358,11 @@ export interface VbenFormProps< * 表单操作区域class */ actionWrapperClass?: ClassType; + /** + * 表单字段数组映射字符串配置 默认使用"," + */ + arrayToStringFields?: ArrayToStringFields; + /** * 表单字段映射 */ @@ -359,6 +383,7 @@ export interface VbenFormProps< * 重置按钮参数 */ resetButtonOptions?: ActionButtonOptions; + /** * 是否显示默认操作按钮 * @default true