Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin
This commit is contained in:
commit
d1efb4278b
@ -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',
|
||||
|
@ -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: {
|
||||
|
@ -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;
|
||||
}>;
|
||||
|
@ -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');
|
||||
});
|
||||
});
|
||||
|
@ -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);
|
||||
|
@ -52,7 +52,6 @@ const {
|
||||
license,
|
||||
version,
|
||||
// vite inject-metadata 插件注入的全局变量
|
||||
// eslint-disable-next-line no-undef
|
||||
} = __VBEN_ADMIN_METADATA__ || {};
|
||||
|
||||
const vbenDescriptionItems: DescriptionItem[] = [
|
||||
|
@ -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-'));
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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: '下拉选',
|
||||
},
|
||||
],
|
||||
// 大屏一行显示3个,中屏一行显示2个,小屏一行显示1个
|
||||
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user