fix(@vben-core/form-ui): restore lock screen form submission (#8195)
* fix(@vben-core/form-ui): avoid stale form context access * fix(@vben/playground): constrain date range codec fields * test(@vben-core/form-ui): harden benchmark fixtures * docs(@vben/docs): document form runtime and benchmarks * fix(@vben/docs): 明确 getValues/submit 返回值及 submit 参数类型
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
import { defineComponent, h } from 'vue';
|
||||
|
||||
export const TestInput = defineComponent({
|
||||
inheritAttrs: false,
|
||||
emits: ['update:modelValue'],
|
||||
setup(_props, { attrs, emit }) {
|
||||
function handleInput(event: Event) {
|
||||
const target = event.target;
|
||||
if (target instanceof HTMLInputElement) {
|
||||
emit('update:modelValue', target.value);
|
||||
}
|
||||
}
|
||||
|
||||
return () =>
|
||||
h('input', {
|
||||
...attrs,
|
||||
onInput: handleInput,
|
||||
value: attrs.modelValue ?? '',
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -61,6 +61,20 @@ describe('formApi', () => {
|
||||
expect(values).toEqual({ name: 'test' });
|
||||
});
|
||||
|
||||
it('should set a field error through the public api', async () => {
|
||||
const setFieldError = vi.fn();
|
||||
const formActions: any = {
|
||||
meta: {},
|
||||
setFieldError,
|
||||
values: {},
|
||||
};
|
||||
|
||||
formApi.mount(formActions, new Map());
|
||||
await formApi.setFieldError('password', 'Invalid password');
|
||||
|
||||
expect(setFieldError).toHaveBeenCalledWith('password', 'Invalid password');
|
||||
});
|
||||
|
||||
it('should format schema values when getting values', async () => {
|
||||
formApi.setState({
|
||||
schema: [
|
||||
|
||||
@@ -1,38 +1,19 @@
|
||||
import type { FormSchema } from '../src/types';
|
||||
|
||||
import { flushPromises, mount } from '@vue/test-utils';
|
||||
import { defineComponent, h, nextTick } from 'vue';
|
||||
import { nextTick } from 'vue';
|
||||
|
||||
import { afterAll, bench, describe } from 'vitest';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { setupVbenForm } from '../src/config';
|
||||
import { useVbenForm } from '../src/use-vben-form';
|
||||
import { TestInput } from './benchmark-fixtures';
|
||||
|
||||
const BENCHMARK_OPTIONS = { time: 750, warmupTime: 150 } as const;
|
||||
const FIELD_COUNT = 100;
|
||||
const MOUNT_FIELD_COUNT = 50;
|
||||
|
||||
const TestInput = defineComponent({
|
||||
inheritAttrs: false,
|
||||
emits: ['update:modelValue'],
|
||||
setup(_props, { attrs, emit }) {
|
||||
function handleInput(event: Event) {
|
||||
const target = event.target;
|
||||
if (target instanceof HTMLInputElement) {
|
||||
emit('update:modelValue', target.value);
|
||||
}
|
||||
}
|
||||
|
||||
return () =>
|
||||
h('input', {
|
||||
...attrs,
|
||||
onInput: handleInput,
|
||||
value: attrs.modelValue ?? '',
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
function createFlatSchema(
|
||||
fieldCount: number,
|
||||
withRules: boolean = false,
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { flushPromises, mount } from '@vue/test-utils';
|
||||
import { defineComponent, h, nextTick } from 'vue';
|
||||
import { nextTick } from 'vue';
|
||||
|
||||
import { afterAll, bench, describe } from 'vitest';
|
||||
|
||||
import { setupVbenForm } from '../src/config';
|
||||
import { FormApi } from '../src/form-api';
|
||||
import { encodeFormValues } from '../src/form-codec';
|
||||
import { useVbenForm } from '../src/use-vben-form';
|
||||
import { TestInput } from './benchmark-fixtures';
|
||||
|
||||
interface ContactValues {
|
||||
enabled: boolean;
|
||||
@@ -30,26 +30,6 @@ interface PerformanceFormValues extends Record<string, unknown> {
|
||||
|
||||
const ROW_COUNT = 100;
|
||||
|
||||
const TestInput = defineComponent({
|
||||
inheritAttrs: false,
|
||||
emits: ['update:modelValue'],
|
||||
setup(_props, { attrs, emit }) {
|
||||
function handleInput(event: Event) {
|
||||
const target = event.target;
|
||||
if (target instanceof HTMLInputElement) {
|
||||
emit('update:modelValue', target.value);
|
||||
}
|
||||
}
|
||||
|
||||
return () =>
|
||||
h('input', {
|
||||
...attrs,
|
||||
onInput: handleInput,
|
||||
value: attrs.modelValue ?? '',
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
function createFormValues(): PerformanceFormValues {
|
||||
return {
|
||||
contacts: Array.from({ length: ROW_COUNT }, (_, index) => ({
|
||||
@@ -82,10 +62,24 @@ const codec = {
|
||||
};
|
||||
|
||||
const formValues = createFormValues();
|
||||
const codecFormApi = new FormApi<PerformanceFormValues>({ codec });
|
||||
codecFormApi.mount({ meta: {}, values: formValues } as never, new Map());
|
||||
|
||||
setupVbenForm({ config: {}, rules: {} });
|
||||
const [CodecForm, codecFormApi] = useVbenForm<PerformanceFormValues>({
|
||||
codec,
|
||||
schema: [
|
||||
{
|
||||
component: TestInput,
|
||||
defaultValue: formValues.contacts,
|
||||
fieldName: 'contacts',
|
||||
},
|
||||
{
|
||||
component: TestInput,
|
||||
defaultValue: formValues.settings,
|
||||
fieldName: 'settings',
|
||||
},
|
||||
],
|
||||
showDefaultActions: false,
|
||||
});
|
||||
const codecWrapper = mount(CodecForm);
|
||||
const [ArrayForm, arrayFormApi] = useVbenForm({
|
||||
schema: [
|
||||
{
|
||||
@@ -115,6 +109,7 @@ let arraySchemaIteration = 0;
|
||||
|
||||
afterAll(() => {
|
||||
arrayWrapper.unmount();
|
||||
codecWrapper.unmount();
|
||||
});
|
||||
|
||||
describe('form codec performance', () => {
|
||||
|
||||
@@ -404,6 +404,11 @@ export class FormApi<
|
||||
}
|
||||
}
|
||||
|
||||
async setFieldError(fieldName: FormFieldName<TFormValues>, error?: string) {
|
||||
const form = await this.getForm();
|
||||
form.setFieldError(fieldName, error);
|
||||
}
|
||||
|
||||
async setFieldValue<TFieldName extends FormFieldName<TFormValues>>(
|
||||
field: TFieldName,
|
||||
value: FormFieldValue<TFormValues, NoInfer<TFieldName>>,
|
||||
|
||||
@@ -37,38 +37,38 @@ const date = useDateFormat(now, 'YYYY-MM-DD dddd', { locales: locale.value });
|
||||
const showUnlockForm = ref(false);
|
||||
const { lockScreenPassword } = storeToRefs(accessStore);
|
||||
|
||||
const [Form, { form, validate, getFieldComponentRef }] = useVbenForm(
|
||||
reactive({
|
||||
commonConfig: {
|
||||
hideLabel: true,
|
||||
hideRequiredMark: true,
|
||||
},
|
||||
schema: computed(() => [
|
||||
{
|
||||
component: 'VbenInputPassword' as const,
|
||||
componentProps: {
|
||||
placeholder: $t('ui.widgets.lockScreen.placeholder'),
|
||||
},
|
||||
fieldName: 'password',
|
||||
label: $t('authentication.password'),
|
||||
rules: z.string().min(1, { message: $t('authentication.passwordTip') }),
|
||||
const [Form, { getFieldComponentRef, getRawValues, setFieldError, validate }] =
|
||||
useVbenForm(
|
||||
reactive({
|
||||
commonConfig: {
|
||||
hideLabel: true,
|
||||
hideRequiredMark: true,
|
||||
},
|
||||
]),
|
||||
showDefaultActions: false,
|
||||
}),
|
||||
);
|
||||
|
||||
const validPass = computed(
|
||||
() => lockScreenPassword?.value === form?.values?.password,
|
||||
);
|
||||
schema: computed(() => [
|
||||
{
|
||||
component: 'VbenInputPassword' as const,
|
||||
componentProps: {
|
||||
placeholder: $t('ui.widgets.lockScreen.placeholder'),
|
||||
},
|
||||
fieldName: 'password',
|
||||
label: $t('authentication.password'),
|
||||
rules: z
|
||||
.string()
|
||||
.min(1, { message: $t('authentication.passwordTip') }),
|
||||
},
|
||||
]),
|
||||
showDefaultActions: false,
|
||||
}),
|
||||
);
|
||||
|
||||
async function handleSubmit() {
|
||||
const { valid } = await validate();
|
||||
if (valid) {
|
||||
if (validPass.value) {
|
||||
const { password } = await getRawValues();
|
||||
if (lockScreenPassword?.value === password) {
|
||||
accessStore.unlockScreen();
|
||||
} else {
|
||||
form.setFieldError('password', $t('authentication.passwordErrorTip'));
|
||||
await setFieldError('password', $t('authentication.passwordErrorTip'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -339,7 +339,6 @@ async function init() {
|
||||
'query',
|
||||
formOptions.value ? ((await formApi.getValues()) ?? {}) : {},
|
||||
);
|
||||
// props.api.reload(formApi.form?.values ?? {});
|
||||
}
|
||||
|
||||
// form 由 vben-form代替,所以不适配formConfig,这里给出警告
|
||||
|
||||
Reference in New Issue
Block a user