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:
@@ -285,11 +285,16 @@ const [BaseForm, baseFormApi] = useVbenForm({
|
||||
},
|
||||
{
|
||||
component: 'DatePicker',
|
||||
dependencies: {
|
||||
resolve: ({ values }) => ({
|
||||
help: () =>
|
||||
[`这是一个可输出其他字段值的帮助信息${values.rate}`].map((value) =>
|
||||
h('p', value),
|
||||
),
|
||||
}),
|
||||
triggerFields: ['rate'],
|
||||
},
|
||||
fieldName: 'datePicker',
|
||||
help: (values) =>
|
||||
[`这是一个可输出其他字段值的帮助信息${values?.rate}`].map((v) =>
|
||||
h('p', v),
|
||||
),
|
||||
label: '日期选择框',
|
||||
},
|
||||
{
|
||||
|
||||
@@ -22,8 +22,8 @@ const layout = ref<FormLayout>('vertical');
|
||||
|
||||
function getNumberValidator(key: string, limit?: [number?, number?]) {
|
||||
let validator = z.number({
|
||||
required_error: `${key} 值不能为空`,
|
||||
invalid_type_error: `${key} 值只能为数字`,
|
||||
error: (issue) =>
|
||||
issue.input === undefined ? `${key} 值不能为空` : `${key} 值只能为数字`,
|
||||
});
|
||||
|
||||
if (limit) {
|
||||
@@ -257,14 +257,14 @@ function handleSetFormValue() {
|
||||
}
|
||||
|
||||
function handleResetFormValue() {
|
||||
baseFormApi.resetForm(undefined, { force: true });
|
||||
baseFormApi.reset(undefined, { force: true });
|
||||
}
|
||||
|
||||
async function handleSubmitFormValue() {
|
||||
const { valid } = await baseFormApi.validate();
|
||||
|
||||
if (valid) {
|
||||
baseFormApi.submitForm();
|
||||
baseFormApi.submit();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -57,7 +57,7 @@ const [Form] = useVbenForm({
|
||||
{
|
||||
component: markRaw(TwoFields),
|
||||
defaultValue: [undefined, ''],
|
||||
disabledOnChangeListener: false,
|
||||
changeEventFallback: true,
|
||||
fieldName: 'field4',
|
||||
formItemClass: 'col-span-1',
|
||||
label: '组合字段',
|
||||
|
||||
@@ -70,7 +70,7 @@ const [Form, formApi] = useVbenForm({
|
||||
fieldName: 'field4',
|
||||
// 界面显示的label
|
||||
label: '邮箱',
|
||||
rules: z.string().email('请输入正确的邮箱'),
|
||||
rules: z.email('请输入正确的邮箱'),
|
||||
},
|
||||
{
|
||||
component: 'InputNumber',
|
||||
@@ -184,8 +184,7 @@ const [Form, formApi] = useVbenForm({
|
||||
},
|
||||
fieldName: 'input-blur',
|
||||
formFieldProps: {
|
||||
validateOnChange: false,
|
||||
validateOnModelUpdate: false,
|
||||
validateOn: ['blur'],
|
||||
},
|
||||
help: 'blur时才会触发校验',
|
||||
label: 'blur触发',
|
||||
@@ -235,7 +234,7 @@ function onSubmit(values: Record<string, any>) {
|
||||
<Card title="基础组件校验示例">
|
||||
<template #extra>
|
||||
<Button @click="() => formApi.validate()">校验表单</Button>
|
||||
<Button class="mx-2" @click="() => formApi.resetValidate()">
|
||||
<Button class="mx-2" @click="() => formApi.clearValidation()">
|
||||
清空校验信息
|
||||
</Button>
|
||||
</template>
|
||||
|
||||
@@ -98,7 +98,7 @@ const [Form, formApi] = useVbenForm({
|
||||
|
||||
// 测试 validateAndSubmitForm(验证并提交)
|
||||
async function testValidateAndSubmit() {
|
||||
await formApi.validateAndSubmitForm();
|
||||
await formApi.validateAndSubmit();
|
||||
}
|
||||
|
||||
// 测试 validate(手动验证整个表单)
|
||||
@@ -112,13 +112,13 @@ async function testValidateField() {
|
||||
}
|
||||
|
||||
// 切换滚动功能
|
||||
function toggleScrollToError() {
|
||||
formApi.setState({ scrollToFirstError: scrollEnabled.value });
|
||||
function toggleScrollToError(checked: boolean) {
|
||||
formApi.setState({ scrollToFirstError: checked });
|
||||
}
|
||||
|
||||
// 填充部分数据测试
|
||||
async function fillPartialData() {
|
||||
await formApi.resetForm();
|
||||
await formApi.reset();
|
||||
await formApi.setFieldValue('username', '测试用户');
|
||||
await formApi.setFieldValue('email', 'test@example.com');
|
||||
}
|
||||
@@ -153,13 +153,13 @@ async function fillPartialData() {
|
||||
<h4 class="mb-3 font-medium">验证方法测试:</h4>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<Button type="primary" @click="testValidateAndSubmit">
|
||||
测试 validateAndSubmitForm()
|
||||
测试 validateAndSubmit()
|
||||
</Button>
|
||||
<Button @click="testValidate"> 测试 validate() </Button>
|
||||
<Button @click="testValidateField"> 测试 validateField() </Button>
|
||||
</div>
|
||||
<div class="mt-2 text-xs text-gray-500">
|
||||
<p>• validateAndSubmitForm(): 验证表单并提交</p>
|
||||
<p>• validateAndSubmit(): 验证表单并提交</p>
|
||||
<p>• validate(): 手动验证整个表单</p>
|
||||
<p>• validateField(): 验证单个字段(这里测试用户名字段)</p>
|
||||
</div>
|
||||
@@ -169,7 +169,7 @@ async function fillPartialData() {
|
||||
<h4 class="mb-3 font-medium">数据填充测试:</h4>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<Button @click="fillPartialData"> 填充部分数据 </Button>
|
||||
<Button @click="() => formApi.resetForm()"> 清空表单 </Button>
|
||||
<Button @click="() => formApi.reset()"> 清空表单 </Button>
|
||||
</div>
|
||||
<div class="mt-2 text-xs text-gray-500">
|
||||
<p>• 填充部分数据后验证,会滚动到第一个错误字段</p>
|
||||
|
||||
Reference in New Issue
Block a user