feat: 更新 useSafeNForm 组件,优化类型定义 [skip ci]

This commit is contained in:
严浩
2025-10-31 10:28:12 +08:00
parent 13b535c530
commit 4c7f052ea1
2 changed files with 39 additions and 57 deletions

View File

@@ -10,6 +10,7 @@ const { formValue, SafeNForm, SafeNFormItem, formRef } = useSafeNForm({
*/ */
user: { user: {
name: '', name: '',
age: 0,
}, },
phone: '', phone: '',
}, },
@@ -19,7 +20,7 @@ function handleSetUserName() {
set(formValue.value, 'user.name', 'Alice'); set(formValue.value, 'user.name', 'Alice');
} }
function handleValidateClick_Safe_Form() { function handleValidateClick() {
formRef.value?.validate((errors) => { formRef.value?.validate((errors) => {
if (!errors) { if (!errors) {
window.$nMessage!.success('Valid'); window.$nMessage!.success('Valid');
@@ -29,19 +30,6 @@ function handleValidateClick_Safe_Form() {
} }
}); });
} }
function handleValidateClick_Normal() {
formRefNormal.value?.validate((errors) => {
if (!errors) {
window.$nMessage!.success('Valid');
} else {
console.log(errors);
window.$nMessage!.error('Invalid');
}
});
}
const formRefNormal = useTemplateRef('formRefNormal');
</script> </script>
<template> <template>
@@ -60,7 +48,7 @@ const formRefNormal = useTemplateRef('formRefNormal');
<n-form-item <n-form-item
label="姓名" label="姓名"
path="user.name" path="user.name"
:rule="{ required: true, message: '请输入姓名', trigger: 'blur' }" :rule="{ required: true, message: '请输入姓名', trigger: ['input'] }"
> >
<n-input v-model:value="formValue.user.name" placeholder="输入姓名" /> <n-input v-model:value="formValue.user.name" placeholder="输入姓名" />
</n-form-item> </n-form-item>
@@ -68,45 +56,24 @@ const formRefNormal = useTemplateRef('formRefNormal');
<n-form-item <n-form-item
label="电话号码" label="电话号码"
path="phone" path="phone"
:rule="{ required: true, message: '请输入电话号码', trigger: ['input'] }" :rule="{ required: true, message: '请输入电话号码', trigger: ['blur'] }"
> >
<n-input v-model:value="formValue.phone" placeholder="电话号码" /> <n-input v-model:value="formValue.phone" placeholder="电话号码" />
</n-form-item> </n-form-item>
<SafeNFormItem #default="slotProps" path="phone"> <SafeNFormItem #default="slotProps" path="user.name">
<div class="border"> <div class="border">
<pre>{{ JSON.stringify({ slotProps }, null, 2) }}</pre> <pre>{{ JSON.stringify({ slotProps }, null, 2) }}</pre>
<div>v: {{ slotProps.value }}</div>
</div> </div>
<!-- <NInput :value="value" placeholder="{props.placeholder}" /> --> <NInput :value="slotProps.value" placeholder="SafeNFormItem" />
</SafeNFormItem> </SafeNFormItem>
<n-form-item> <n-form-item>
<n-button attr-type="button" @click="handleValidateClick_Safe_Form"> 验证 </n-button> <n-button attr-type="button" @click="handleValidateClick"> 验证 </n-button>
</n-form-item> </n-form-item>
</SafeNForm> </SafeNForm>
</n-card> </n-card>
<n-card title="普通 Form" mt-4>
<n-form ref="formRefNormal" inline :model="formValue">
<n-form-item
label="姓名"
path="user.name"
:rule="{ required: true, message: '请输入姓名', trigger: 'blur' }"
>
<n-input v-model:value="formValue.user.name" placeholder="输入姓名" />
</n-form-item>
<n-form-item
label="电话号码"
path="phone"
:rule="{ required: true, message: '请输入电话号码', trigger: ['input'] }"
>
<n-input v-model:value="formValue.phone" placeholder="电话号码" />
</n-form-item>
<n-form-item>
<n-button attr-type="button" @click="handleValidateClick_Normal"> 验证 </n-button>
</n-form-item>
</n-form>
</n-card>
</n-space> </n-space>
</div> </div>
</template> </template>

View File

@@ -16,6 +16,7 @@ export function useSafeNForm<T extends Record<string, any> = Record<string, unkn
const formRef = ref<FormInst | null>(null); const formRef = ref<FormInst | null>(null);
const formValue = ref<T>(structuredClone(toRaw(options.initialFormValue)) || ({} as T)); const formValue = ref<T>(structuredClone(toRaw(options.initialFormValue)) || ({} as T));
// 创建类型安全的 Form 组件
interface SafeNFormProps { interface SafeNFormProps {
disabled?: boolean; disabled?: boolean;
} }
@@ -23,7 +24,6 @@ export function useSafeNForm<T extends Record<string, any> = Record<string, unkn
default?: { count?: number }; default?: { count?: number };
}>; }>;
// 创建类型安全的 Form 组件
const SafeNForm = defineComponent<SafeNFormProps, /* Emits */ [], /* EE */ never, SafeNFormSlots>( const SafeNForm = defineComponent<SafeNFormProps, /* Emits */ [], /* EE */ never, SafeNFormSlots>(
(props, ctx) => { (props, ctx) => {
return () => ( return () => (
@@ -48,31 +48,45 @@ export function useSafeNForm<T extends Record<string, any> = Record<string, unkn
// <<<<< // <<<<<
// >>>>> 创建类型安全的 FormItem 组件 // >>>>> 创建类型安全的 FormItem 组件
interface SafeNFormItemProps {
type SafeNFormItemProps<P extends Paths<T> & string> = {
label?: string; label?: string;
path: Paths<T> /* & string */; path: P;
placeholder?: string; placeholder?: string;
}
type SafeNFormItemSlots = SlotsType<{
default: {
value: Get<T, string>;
}; };
type SafeNFormItemSlotScope<P extends Paths<T> & string> = {
value: Get<T, P>;
};
type SafeNFormItemSlotsDefinition<P extends Paths<T> & string> = SlotsType<{
default: SafeNFormItemSlotScope<P>;
}>; }>;
const SafeNFormItem = defineComponent< type SafeNFormItemSlotFns<P extends Paths<T> & string> = {
SafeNFormItemProps, default?: (scope: SafeNFormItemSlotScope<P>) => any;
};
type SafeNFormItemComponent = new <P extends Paths<T> & string>(
props: SafeNFormItemProps<P>,
) => {
$props: SafeNFormItemProps<P>;
$slots: SafeNFormItemSlotFns<P>;
};
const SafeNFormItemImpl = defineComponent<
SafeNFormItemProps<Paths<T> & string>,
/* Emits */ [], /* Emits */ [],
/* EE */ never, /* EE */ never,
SafeNFormItemSlots SafeNFormItemSlotsDefinition<Paths<T> & string>
>( >(
(props, ctx) => { (props, ctx) => {
return () => { return () => {
const value = get(formValue.value, props.path) as Get<T, string>; const value = get(formValue.value, props.path) as Get<T, typeof props.path>;
const slots = ctx.slots;
return ( return (
<NFormItem> <NFormItem path={props.path} label={props.label}>
{ctx.slots.default?.({ {slots.default?.({ value })}
value,
})}
</NFormItem> </NFormItem>
); );
}; };
@@ -83,6 +97,7 @@ export function useSafeNForm<T extends Record<string, any> = Record<string, unkn
props: ['label', 'path', 'placeholder'], props: ['label', 'path', 'placeholder'],
}, },
); );
const SafeNFormItem = SafeNFormItemImpl as unknown as SafeNFormItemComponent;
// <<<<< // <<<<<
return { return {