This commit is contained in:
dap 2024-09-21 19:12:42 +08:00
commit d1efb4278b
9 changed files with 171 additions and 11 deletions

View File

@ -147,7 +147,7 @@ export async function javascript(): Promise<Linter.Config[]> {
'no-template-curly-in-string': 'error',
'no-this-before-super': 'error',
'no-throw-literal': 'error',
'no-undef': 'error',
'no-undef': 'off',
'no-undef-init': 'error',
'no-unexpected-multiline': 'error',
'no-unmodified-loop-condition': 'error',

View File

@ -15,6 +15,22 @@ export async function vue(): Promise<Linter.Config[]> {
{
files: ['**/*.vue'],
languageOptions: {
// globals: {
// computed: 'readonly',
// defineEmits: 'readonly',
// defineExpose: 'readonly',
// defineProps: 'readonly',
// onMounted: 'readonly',
// onUnmounted: 'readonly',
// reactive: 'readonly',
// ref: 'readonly',
// shallowReactive: 'readonly',
// shallowRef: 'readonly',
// toRef: 'readonly',
// toRefs: 'readonly',
// watch: 'readonly',
// watchEffect: 'readonly',
// },
parser: parserVue,
parserOptions: {
ecmaFeatures: {

View File

@ -137,12 +137,12 @@ type ApplicationOptions = ApplicationPluginOptions;
type LibraryOptions = LibraryPluginOptions;
type DefineApplicationOptions = (config?: ConfigEnv) => Promise<{
type DefineApplicationOptions = (config: ConfigEnv) => Promise<{
application?: ApplicationOptions;
vite?: UserConfig;
}>;
type DefineLibraryOptions = (config?: ConfigEnv) => Promise<{
type DefineLibraryOptions = (config: ConfigEnv) => Promise<{
library?: LibraryOptions;
vite?: UserConfig;
}>;

View File

@ -6,7 +6,7 @@ import { FormApi } from '../src/form-api';
vi.mock('@vben-core/shared/utils', () => ({
bindMethods: vi.fn(),
createMerge: vi.fn((mergeFn) => {
return (stateOrFn, prev) => {
return (stateOrFn: any, prev: any) => {
mergeFn(prev, 'key', stateOrFn);
return { ...prev, ...stateOrFn };
};
@ -144,3 +144,64 @@ describe('formApi', () => {
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 items in the schema array must have a valid `fieldName` property to be updated',
);
});
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');
});
});

View File

@ -5,7 +5,7 @@ import type {
ValidationOptions,
} from 'vee-validate';
import type { FormActions, VbenFormProps } from './types';
import type { FormActions, FormSchema, VbenFormProps } from './types';
import { toRaw } from 'vue';
@ -186,6 +186,37 @@ export class FormApi {
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>) {
const form = await this.getForm();
return await form.validate(opts);

View File

@ -52,7 +52,6 @@ const {
license,
version,
// vite inject-metadata
// eslint-disable-next-line no-undef
} = __VBEN_ADMIN_METADATA__ || {};
const vbenDescriptionItems: DescriptionItem[] = [

View File

@ -143,6 +143,19 @@ watch(
},
);
watch(
() => preferences.app.layout,
async (val) => {
if (val === 'sidebar-mixed-nav' && preferences.sidebar.hidden) {
updatePreferences({
sidebar: {
hidden: false,
},
});
}
},
);
const slots = useSlots();
const headerSlots = computed(() => {
return Object.keys(slots).filter((key) => key.startsWith('header-'));

View File

@ -12,6 +12,6 @@
@apply dark:border-border/60 dark:border;
}
.ant-app .form-valid-error .ant-select-selector {
.form-valid-error .ant-select-selector {
border-color: hsl(var(--destructive));
}

View File

@ -41,12 +41,25 @@ const [BaseForm, formApi] = useVbenForm({
label: 'field2',
},
{
component: 'Input',
component: 'Select',
componentProps: {
placeholder: '请输入',
allowClear: true,
filterOption: true,
options: [
{
label: '选项1',
value: '1',
},
{
label: '选项2',
value: '2',
},
],
placeholder: '请选择',
showSearch: true,
},
fieldName: 'field3',
label: 'field3',
fieldName: 'fieldOptions',
label: '下拉选',
},
],
// 321
@ -75,9 +88,35 @@ function handleClick(
| 'showSubmitButton'
| 'updateActionAlign'
| 'updateResetButton'
| 'updateSchema'
| 'updateSubmitButton',
) {
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': {
formApi.setState({
commonConfig: {
@ -181,6 +220,7 @@ function handleClick(
<template>
<Page description="表单组件api操作示例。" title="表单组件">
<Space class="mb-5 flex-wrap">
<Button @click="handleClick('updateSchema')">updateSchema</Button>
<Button @click="handleClick('labelWidth')">更改labelWidth</Button>
<Button @click="handleClick('resetLabelWidth')">还原labelWidth</Button>
<Button @click="handleClick('disabled')">禁用表单</Button>