feat: add updateSchema to form api (#4453)
* feat: add updateSchema to form api * chore: typo * chore: typopull/48/MERGE
parent
68dbe04bef
commit
60cffb0dec
|
@ -6,7 +6,7 @@ import { FormApi } from '../src/form-api';
|
||||||
vi.mock('@vben-core/shared/utils', () => ({
|
vi.mock('@vben-core/shared/utils', () => ({
|
||||||
bindMethods: vi.fn(),
|
bindMethods: vi.fn(),
|
||||||
createMerge: vi.fn((mergeFn) => {
|
createMerge: vi.fn((mergeFn) => {
|
||||||
return (stateOrFn, prev) => {
|
return (stateOrFn: any, prev: any) => {
|
||||||
mergeFn(prev, 'key', stateOrFn);
|
mergeFn(prev, 'key', stateOrFn);
|
||||||
return { ...prev, ...stateOrFn };
|
return { ...prev, ...stateOrFn };
|
||||||
};
|
};
|
||||||
|
@ -144,3 +144,64 @@ describe('formApi', () => {
|
||||||
expect(isValid).toBe(true);
|
expect(isValid).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('updateSchema', () => {
|
||||||
|
let instance: FormApi;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
instance = new FormApi();
|
||||||
|
instance.state = {
|
||||||
|
schema: [
|
||||||
|
{ component: 'text', fieldName: 'name' },
|
||||||
|
{ component: 'number', fieldName: 'age', label: 'Age' },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update the schema correctly when fieldName matches', () => {
|
||||||
|
const newSchema = [
|
||||||
|
{ component: 'text', fieldName: 'name' },
|
||||||
|
{ component: 'number', fieldName: 'age', label: 'Age' },
|
||||||
|
];
|
||||||
|
|
||||||
|
instance.updateSchema(newSchema);
|
||||||
|
|
||||||
|
expect(instance.state?.schema?.[0]?.component).toBe('text');
|
||||||
|
expect(instance.state?.schema?.[1]?.label).toBe('Age');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should log an error if fieldName is missing in some items', () => {
|
||||||
|
const newSchema: any[] = [
|
||||||
|
{ component: 'textarea', fieldName: 'name' },
|
||||||
|
{ component: 'number' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const consoleErrorSpy = vi
|
||||||
|
.spyOn(console, 'error')
|
||||||
|
.mockImplementation(() => {});
|
||||||
|
|
||||||
|
instance.updateSchema(newSchema);
|
||||||
|
|
||||||
|
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
||||||
|
'All children of the form Schema array that need to be updated must contain the `field` field',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not update schema if fieldName does not match', () => {
|
||||||
|
const newSchema = [{ component: 'textarea', fieldName: 'unknown' }];
|
||||||
|
|
||||||
|
instance.updateSchema(newSchema);
|
||||||
|
|
||||||
|
expect(instance.state?.schema?.[0]?.component).toBe('text');
|
||||||
|
expect(instance.state?.schema?.[1]?.component).toBe('number');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not update schema if updatedMap is empty', () => {
|
||||||
|
const newSchema: any[] = [{ component: 'textarea' }];
|
||||||
|
|
||||||
|
instance.updateSchema(newSchema);
|
||||||
|
|
||||||
|
expect(instance.state?.schema?.[0]?.component).toBe('text');
|
||||||
|
expect(instance.state?.schema?.[1]?.component).toBe('number');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -5,7 +5,7 @@ import type {
|
||||||
ValidationOptions,
|
ValidationOptions,
|
||||||
} from 'vee-validate';
|
} from 'vee-validate';
|
||||||
|
|
||||||
import type { FormActions, VbenFormProps } from './types';
|
import type { FormActions, FormSchema, VbenFormProps } from './types';
|
||||||
|
|
||||||
import { toRaw } from 'vue';
|
import { toRaw } from 'vue';
|
||||||
|
|
||||||
|
@ -186,6 +186,37 @@ export class FormApi {
|
||||||
this.stateHandler.reset();
|
this.stateHandler.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateSchema(schema: Partial<FormSchema>[]) {
|
||||||
|
const updated: Partial<FormSchema>[] = [...schema];
|
||||||
|
const hasField = updated.every(
|
||||||
|
(item) => Reflect.has(item, 'fieldName') && item.fieldName,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!hasField) {
|
||||||
|
console.error(
|
||||||
|
'All items in the schema array must have a valid `fieldName` property to be updated',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const currentSchema = [...(this.state?.schema ?? [])];
|
||||||
|
|
||||||
|
const updatedMap: Record<string, any> = {};
|
||||||
|
|
||||||
|
updated.forEach((item) => {
|
||||||
|
if (item.fieldName) {
|
||||||
|
updatedMap[item.fieldName] = item;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
currentSchema.forEach((schema, index) => {
|
||||||
|
const updatedData = updatedMap[schema.fieldName];
|
||||||
|
if (updatedData) {
|
||||||
|
currentSchema[index] = merge(updatedData, schema) as FormSchema;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.setState({ schema: currentSchema });
|
||||||
|
}
|
||||||
|
|
||||||
async validate(opts?: Partial<ValidationOptions>) {
|
async validate(opts?: Partial<ValidationOptions>) {
|
||||||
const form = await this.getForm();
|
const form = await this.getForm();
|
||||||
return await form.validate(opts);
|
return await form.validate(opts);
|
||||||
|
|
|
@ -41,12 +41,25 @@ const [BaseForm, formApi] = useVbenForm({
|
||||||
label: 'field2',
|
label: 'field2',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: 'Input',
|
component: 'Select',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
placeholder: '请输入',
|
allowClear: true,
|
||||||
|
filterOption: true,
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: '选项1',
|
||||||
|
value: '1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '选项2',
|
||||||
|
value: '2',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
placeholder: '请选择',
|
||||||
|
showSearch: true,
|
||||||
},
|
},
|
||||||
fieldName: 'field3',
|
fieldName: 'fieldOptions',
|
||||||
label: 'field3',
|
label: '下拉选',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
// 大屏一行显示3个,中屏一行显示2个,小屏一行显示1个
|
// 大屏一行显示3个,中屏一行显示2个,小屏一行显示1个
|
||||||
|
@ -75,9 +88,35 @@ function handleClick(
|
||||||
| 'showSubmitButton'
|
| 'showSubmitButton'
|
||||||
| 'updateActionAlign'
|
| 'updateActionAlign'
|
||||||
| 'updateResetButton'
|
| 'updateResetButton'
|
||||||
|
| 'updateSchema'
|
||||||
| 'updateSubmitButton',
|
| 'updateSubmitButton',
|
||||||
) {
|
) {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
|
case 'updateSchema': {
|
||||||
|
formApi.updateSchema([
|
||||||
|
{
|
||||||
|
componentProps: {
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: '选项1',
|
||||||
|
value: '1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '选项2',
|
||||||
|
value: '2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '选项3',
|
||||||
|
value: '3',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
fieldName: 'fieldOptions',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 'labelWidth': {
|
case 'labelWidth': {
|
||||||
formApi.setState({
|
formApi.setState({
|
||||||
commonConfig: {
|
commonConfig: {
|
||||||
|
@ -181,6 +220,7 @@ function handleClick(
|
||||||
<template>
|
<template>
|
||||||
<Page description="表单组件api操作示例。" title="表单组件">
|
<Page description="表单组件api操作示例。" title="表单组件">
|
||||||
<Space class="mb-5 flex-wrap">
|
<Space class="mb-5 flex-wrap">
|
||||||
|
<Button @click="handleClick('updateSchema')">updateSchema</Button>
|
||||||
<Button @click="handleClick('labelWidth')">更改labelWidth</Button>
|
<Button @click="handleClick('labelWidth')">更改labelWidth</Button>
|
||||||
<Button @click="handleClick('resetLabelWidth')">还原labelWidth</Button>
|
<Button @click="handleClick('resetLabelWidth')">还原labelWidth</Button>
|
||||||
<Button @click="handleClick('disabled')">禁用表单</Button>
|
<Button @click="handleClick('disabled')">禁用表单</Button>
|
||||||
|
|
Loading…
Reference in New Issue