refactor(@vben-core/form-ui): migrate to Zod 4 and TanStack Form (#8176)
* build(@vben-core/form-ui): update form validation dependencies * refactor(@vben-core/form-ui): replace vee-validate with TanStack Form * test(@vben-core/form-ui): cover TanStack Form migration * docs(@vben/docs): document Zod 4 form migration * refactor(project): update form adapters * refactor(project): migrate application form consumers * refactor(@vben/playground): migrate form examples * docs(@vben/docs): fix validate return type and required rule empty check in form docs * fix(@vben-core/form-ui): resolve oxlint and eslint errors - rewrite nested ternary expressions as if/else in form-field-array.vue and form-runtime.ts - sort @tanstack/vue-form before @vben-core/composables in package.json * chore: fix merge artifacts and formatting - move dependencies before devDependencies in root package.json - update preferences snapshot for widget positioning fields - format form-array demo README * refactor(@vben-core/form-ui): optimize form runtime and value flow - add fine-grained field selectors and atomic dependency resolution - expose raw and formatted value snapshots with focused regression coverage * fix(@vben/layouts): dispose sortable instance on unmount * docs(@vben/docs): document form runtime API changes - list added, changed, removed, and deprecated form APIs - document atomic dependencies and raw versus formatted values
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import type {
|
||||
VbenFormProps as FormProps,
|
||||
VbenFormSchema as FormSchema,
|
||||
FormValues,
|
||||
} from '@vben/common-ui';
|
||||
|
||||
import type { ComponentPropsMap, ComponentType } from './component';
|
||||
@@ -22,7 +23,7 @@ async function initSetupVbenForm() {
|
||||
Upload: 'fileList',
|
||||
},
|
||||
},
|
||||
defineRules: {
|
||||
rules: {
|
||||
// 输入项目必填国际化适配
|
||||
required: (value, _params, ctx) => {
|
||||
if (value === undefined || value === null || value.length === 0) {
|
||||
@@ -41,9 +42,18 @@ async function initSetupVbenForm() {
|
||||
});
|
||||
}
|
||||
|
||||
const useVbenForm = useForm<ComponentType, ComponentPropsMap>;
|
||||
function useVbenForm<TValues extends FormValues = FormValues>(
|
||||
options: FormProps<ComponentType, ComponentPropsMap, TValues>,
|
||||
) {
|
||||
return useForm<TValues, ComponentType, ComponentPropsMap>(options);
|
||||
}
|
||||
|
||||
export { initSetupVbenForm, useVbenForm, z };
|
||||
|
||||
export type VbenFormSchema = FormSchema<ComponentType, ComponentPropsMap>;
|
||||
export type VbenFormProps = FormProps<ComponentType, ComponentPropsMap>;
|
||||
export type VbenFormSchema<TValues extends FormValues = FormValues> =
|
||||
FormSchema<ComponentType, ComponentPropsMap, TValues>;
|
||||
export type VbenFormProps<TValues extends FormValues = FormValues> = FormProps<
|
||||
ComponentType,
|
||||
ComponentPropsMap,
|
||||
TValues
|
||||
>;
|
||||
|
||||
@@ -46,7 +46,7 @@ const formSchema = computed((): VbenFormSchema[] => {
|
||||
rules(values) {
|
||||
const { password } = values;
|
||||
return z
|
||||
.string({ required_error: $t('authentication.passwordTip') })
|
||||
.string({ error: $t('authentication.passwordTip') })
|
||||
.min(1, { message: $t('authentication.passwordTip') })
|
||||
.refine((value) => value === password, {
|
||||
message: $t('authentication.confirmPasswordTip'),
|
||||
|
||||
@@ -38,7 +38,7 @@ const formSchema = computed((): VbenFormSchema[] => {
|
||||
rules(values) {
|
||||
const { newPassword } = values;
|
||||
return z
|
||||
.string({ required_error: '请再次输入新密码' })
|
||||
.string({ error: '请再次输入新密码' })
|
||||
.min(1, { message: '请再次输入新密码' })
|
||||
.refine((value) => value === newPassword, {
|
||||
message: '两次输入的密码不一致',
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type {
|
||||
VbenFormProps as FormProps,
|
||||
VbenFormSchema as FormSchema,
|
||||
FormValues,
|
||||
} from '@vben/common-ui';
|
||||
|
||||
import type { ComponentPropsMap, ComponentType } from './component';
|
||||
@@ -22,7 +23,7 @@ async function initSetupVbenForm() {
|
||||
Upload: 'fileList',
|
||||
},
|
||||
},
|
||||
defineRules: {
|
||||
rules: {
|
||||
// 输入项目必填国际化适配
|
||||
required: (value, _params, ctx) => {
|
||||
if (value === undefined || value === null || value.length === 0) {
|
||||
@@ -40,9 +41,18 @@ async function initSetupVbenForm() {
|
||||
},
|
||||
});
|
||||
}
|
||||
const useVbenForm = useForm<ComponentType, ComponentPropsMap>;
|
||||
function useVbenForm<TValues extends FormValues = FormValues>(
|
||||
options: FormProps<ComponentType, ComponentPropsMap, TValues>,
|
||||
) {
|
||||
return useForm<TValues, ComponentType, ComponentPropsMap>(options);
|
||||
}
|
||||
|
||||
export { initSetupVbenForm, useVbenForm, z };
|
||||
|
||||
export type VbenFormSchema = FormSchema<ComponentType, ComponentPropsMap>;
|
||||
export type VbenFormProps = FormProps<ComponentType, ComponentPropsMap>;
|
||||
export type VbenFormSchema<TValues extends FormValues = FormValues> =
|
||||
FormSchema<ComponentType, ComponentPropsMap, TValues>;
|
||||
export type VbenFormProps<TValues extends FormValues = FormValues> = FormProps<
|
||||
ComponentType,
|
||||
ComponentPropsMap,
|
||||
TValues
|
||||
>;
|
||||
|
||||
@@ -46,7 +46,7 @@ const formSchema = computed((): VbenFormSchema[] => {
|
||||
rules(values) {
|
||||
const { password } = values;
|
||||
return z
|
||||
.string({ required_error: $t('authentication.passwordTip') })
|
||||
.string({ error: $t('authentication.passwordTip') })
|
||||
.min(1, { message: $t('authentication.passwordTip') })
|
||||
.refine((value) => value === password, {
|
||||
message: $t('authentication.confirmPasswordTip'),
|
||||
|
||||
@@ -38,7 +38,7 @@ const formSchema = computed((): VbenFormSchema[] => {
|
||||
rules(values) {
|
||||
const { newPassword } = values;
|
||||
return z
|
||||
.string({ required_error: '请再次输入新密码' })
|
||||
.string({ error: '请再次输入新密码' })
|
||||
.min(1, { message: '请再次输入新密码' })
|
||||
.refine((value) => value === newPassword, {
|
||||
message: '两次输入的密码不一致',
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type {
|
||||
VbenFormProps as FormProps,
|
||||
VbenFormSchema as FormSchema,
|
||||
FormValues,
|
||||
} from '@vben/common-ui';
|
||||
|
||||
import type { ComponentPropsMap, ComponentType } from './component';
|
||||
@@ -16,7 +17,7 @@ async function initSetupVbenForm() {
|
||||
CheckboxGroup: 'model-value',
|
||||
},
|
||||
},
|
||||
defineRules: {
|
||||
rules: {
|
||||
required: (value, _params, ctx) => {
|
||||
if (value === undefined || value === null || value.length === 0) {
|
||||
return $t('ui.formRules.required', [ctx.label]);
|
||||
@@ -33,9 +34,18 @@ async function initSetupVbenForm() {
|
||||
});
|
||||
}
|
||||
|
||||
const useVbenForm = useForm<ComponentType, ComponentPropsMap>;
|
||||
function useVbenForm<TValues extends FormValues = FormValues>(
|
||||
options: FormProps<ComponentType, ComponentPropsMap, TValues>,
|
||||
) {
|
||||
return useForm<TValues, ComponentType, ComponentPropsMap>(options);
|
||||
}
|
||||
|
||||
export { initSetupVbenForm, useVbenForm, z };
|
||||
|
||||
export type VbenFormSchema = FormSchema<ComponentType, ComponentPropsMap>;
|
||||
export type VbenFormProps = FormProps<ComponentType, ComponentPropsMap>;
|
||||
export type VbenFormSchema<TValues extends FormValues = FormValues> =
|
||||
FormSchema<ComponentType, ComponentPropsMap, TValues>;
|
||||
export type VbenFormProps<TValues extends FormValues = FormValues> = FormProps<
|
||||
ComponentType,
|
||||
ComponentPropsMap,
|
||||
TValues
|
||||
>;
|
||||
|
||||
@@ -46,7 +46,7 @@ const formSchema = computed((): VbenFormSchema[] => {
|
||||
rules(values) {
|
||||
const { password } = values;
|
||||
return z
|
||||
.string({ required_error: $t('authentication.passwordTip') })
|
||||
.string({ error: $t('authentication.passwordTip') })
|
||||
.min(1, { message: $t('authentication.passwordTip') })
|
||||
.refine((value) => value === password, {
|
||||
message: $t('authentication.confirmPasswordTip'),
|
||||
|
||||
@@ -38,7 +38,7 @@ const formSchema = computed((): VbenFormSchema[] => {
|
||||
rules(values) {
|
||||
const { newPassword } = values;
|
||||
return z
|
||||
.string({ required_error: '请再次输入新密码' })
|
||||
.string({ error: '请再次输入新密码' })
|
||||
.min(1, { message: '请再次输入新密码' })
|
||||
.refine((value) => value === newPassword, {
|
||||
message: '两次输入的密码不一致',
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type {
|
||||
VbenFormProps as FormProps,
|
||||
VbenFormSchema as FormSchema,
|
||||
FormValues,
|
||||
} from '@vben/common-ui';
|
||||
|
||||
import type { ComponentPropsMap, ComponentType } from './component';
|
||||
@@ -20,7 +21,7 @@ async function initSetupVbenForm() {
|
||||
Upload: 'fileList',
|
||||
},
|
||||
},
|
||||
defineRules: {
|
||||
rules: {
|
||||
required: (value, _params, ctx) => {
|
||||
if (value === undefined || value === null || value.length === 0) {
|
||||
return $t('ui.formRules.required', [ctx.label]);
|
||||
@@ -37,9 +38,18 @@ async function initSetupVbenForm() {
|
||||
});
|
||||
}
|
||||
|
||||
const useVbenForm = useForm<ComponentType, ComponentPropsMap>;
|
||||
function useVbenForm<TValues extends FormValues = FormValues>(
|
||||
options: FormProps<ComponentType, ComponentPropsMap, TValues>,
|
||||
) {
|
||||
return useForm<TValues, ComponentType, ComponentPropsMap>(options);
|
||||
}
|
||||
|
||||
export { initSetupVbenForm, useVbenForm, z };
|
||||
|
||||
export type VbenFormSchema = FormSchema<ComponentType, ComponentPropsMap>;
|
||||
export type VbenFormProps = FormProps<ComponentType, ComponentPropsMap>;
|
||||
export type VbenFormSchema<TValues extends FormValues = FormValues> =
|
||||
FormSchema<ComponentType, ComponentPropsMap, TValues>;
|
||||
export type VbenFormProps<TValues extends FormValues = FormValues> = FormProps<
|
||||
ComponentType,
|
||||
ComponentPropsMap,
|
||||
TValues
|
||||
>;
|
||||
|
||||
@@ -46,7 +46,7 @@ const formSchema = computed((): VbenFormSchema[] => {
|
||||
rules(values) {
|
||||
const { password } = values;
|
||||
return z
|
||||
.string({ required_error: $t('authentication.passwordTip') })
|
||||
.string({ error: $t('authentication.passwordTip') })
|
||||
.min(1, { message: $t('authentication.passwordTip') })
|
||||
.refine((value) => value === password, {
|
||||
message: $t('authentication.confirmPasswordTip'),
|
||||
|
||||
@@ -38,7 +38,7 @@ const formSchema = computed((): VbenFormSchema[] => {
|
||||
rules(values) {
|
||||
const { newPassword } = values;
|
||||
return z
|
||||
.string({ required_error: '请再次输入新密码' })
|
||||
.string({ error: '请再次输入新密码' })
|
||||
.min(1, { message: '请再次输入新密码' })
|
||||
.refine((value) => value === newPassword, {
|
||||
message: '两次输入的密码不一致',
|
||||
|
||||
@@ -50,7 +50,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
modalApi.close();
|
||||
},
|
||||
onConfirm: async () => {
|
||||
await formApi.validateAndSubmitForm();
|
||||
await formApi.validateAndSubmit();
|
||||
// modalApi.close();
|
||||
},
|
||||
onOpenChange(isOpen: boolean) {
|
||||
|
||||
@@ -24,7 +24,7 @@ const [Form, formApi] = useVbenForm({
|
||||
component: 'VbenFormFieldArray',
|
||||
fieldName: 'members',
|
||||
label: '项目成员',
|
||||
// 初始化为空数组,供内部 useFieldArray 使用
|
||||
// 初始化为空数组,供数组编辑器使用
|
||||
defaultValue: [],
|
||||
componentProps: {
|
||||
min: 1,
|
||||
@@ -113,9 +113,7 @@ async function getFormValues() {
|
||||
<template #header-extra>
|
||||
<NButton class="mr-2" @click="setFormValues">设置表单值</NButton>
|
||||
<NButton class="mr-2" @click="getFormValues">获取表单值</NButton>
|
||||
<NButton type="primary" @click="formApi.submitForm()">
|
||||
提交校验
|
||||
</NButton>
|
||||
<NButton type="primary" @click="formApi.submit()"> 提交校验 </NButton>
|
||||
</template>
|
||||
<Form />
|
||||
</NCard>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type {
|
||||
VbenFormProps as FormProps,
|
||||
VbenFormSchema as FormSchema,
|
||||
FormValues,
|
||||
} from '@vben/common-ui';
|
||||
|
||||
import type { ComponentPropsMap, ComponentType } from './component';
|
||||
@@ -22,7 +23,7 @@ async function initSetupVbenForm() {
|
||||
Upload: 'fileList',
|
||||
},
|
||||
},
|
||||
defineRules: {
|
||||
rules: {
|
||||
// 输入项目必填国际化适配
|
||||
required: (value, _params, ctx) => {
|
||||
if (value === undefined || value === null || value.length === 0) {
|
||||
@@ -41,9 +42,18 @@ async function initSetupVbenForm() {
|
||||
});
|
||||
}
|
||||
|
||||
const useVbenForm = useForm<ComponentType, ComponentPropsMap>;
|
||||
function useVbenForm<TValues extends FormValues = FormValues>(
|
||||
options: FormProps<ComponentType, ComponentPropsMap, TValues>,
|
||||
) {
|
||||
return useForm<TValues, ComponentType, ComponentPropsMap>(options);
|
||||
}
|
||||
|
||||
export { initSetupVbenForm, useVbenForm, z };
|
||||
|
||||
export type VbenFormSchema = FormSchema<ComponentType, ComponentPropsMap>;
|
||||
export type VbenFormProps = FormProps<ComponentType, ComponentPropsMap>;
|
||||
export type VbenFormSchema<TValues extends FormValues = FormValues> =
|
||||
FormSchema<ComponentType, ComponentPropsMap, TValues>;
|
||||
export type VbenFormProps<TValues extends FormValues = FormValues> = FormProps<
|
||||
ComponentType,
|
||||
ComponentPropsMap,
|
||||
TValues
|
||||
>;
|
||||
|
||||
@@ -46,7 +46,7 @@ const formSchema = computed((): VbenFormSchema[] => {
|
||||
rules(values) {
|
||||
const { password } = values;
|
||||
return z
|
||||
.string({ required_error: $t('authentication.passwordTip') })
|
||||
.string({ error: $t('authentication.passwordTip') })
|
||||
.min(1, { message: $t('authentication.passwordTip') })
|
||||
.refine((value) => value === password, {
|
||||
message: $t('authentication.confirmPasswordTip'),
|
||||
|
||||
@@ -38,7 +38,7 @@ const formSchema = computed((): VbenFormSchema[] => {
|
||||
rules(values) {
|
||||
const { newPassword } = values;
|
||||
return z
|
||||
.string({ required_error: '请再次输入新密码' })
|
||||
.string({ error: '请再次输入新密码' })
|
||||
.min(1, { message: '请再次输入新密码' })
|
||||
.refine((value) => value === newPassword, {
|
||||
message: '两次输入的密码不一致',
|
||||
|
||||
Reference in New Issue
Block a user