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: {
name: '',
age: 0,
},
phone: '',
},
@@ -19,7 +20,7 @@ function handleSetUserName() {
set(formValue.value, 'user.name', 'Alice');
}
function handleValidateClick_Safe_Form() {
function handleValidateClick() {
formRef.value?.validate((errors) => {
if (!errors) {
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>
<template>
@@ -60,7 +48,7 @@ const formRefNormal = useTemplateRef('formRefNormal');
<n-form-item
label="姓名"
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-form-item>
@@ -68,45 +56,24 @@ const formRefNormal = useTemplateRef('formRefNormal');
<n-form-item
label="电话号码"
path="phone"
:rule="{ required: true, message: '请输入电话号码', trigger: ['input'] }"
:rule="{ required: true, message: '请输入电话号码', trigger: ['blur'] }"
>
<n-input v-model:value="formValue.phone" placeholder="电话号码" />
</n-form-item>
<SafeNFormItem #default="slotProps" path="phone">
<SafeNFormItem #default="slotProps" path="user.name">
<div class="border">
<pre>{{ JSON.stringify({ slotProps }, null, 2) }}</pre>
<div>v: {{ slotProps.value }}</div>
</div>
<!-- <NInput :value="value" placeholder="{props.placeholder}" /> -->
<NInput :value="slotProps.value" placeholder="SafeNFormItem" />
</SafeNFormItem>
<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>
</SafeNForm>
</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>
</div>
</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 formValue = ref<T>(structuredClone(toRaw(options.initialFormValue)) || ({} as T));
// 创建类型安全的 Form 组件
interface SafeNFormProps {
disabled?: boolean;
}
@@ -23,7 +24,6 @@ export function useSafeNForm<T extends Record<string, any> = Record<string, unkn
default?: { count?: number };
}>;
// 创建类型安全的 Form 组件
const SafeNForm = defineComponent<SafeNFormProps, /* Emits */ [], /* EE */ never, SafeNFormSlots>(
(props, ctx) => {
return () => (
@@ -48,31 +48,45 @@ export function useSafeNForm<T extends Record<string, any> = Record<string, unkn
// <<<<<
// >>>>> 创建类型安全的 FormItem 组件
interface SafeNFormItemProps {
type SafeNFormItemProps<P extends Paths<T> & string> = {
label?: string;
path: Paths<T> /* & string */;
path: P;
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<
SafeNFormItemProps,
type SafeNFormItemSlotFns<P extends Paths<T> & string> = {
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 */ [],
/* EE */ never,
SafeNFormItemSlots
SafeNFormItemSlotsDefinition<Paths<T> & string>
>(
(props, ctx) => {
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 (
<NFormItem>
{ctx.slots.default?.({
value,
})}
<NFormItem path={props.path} label={props.label}>
{slots.default?.({ value })}
</NFormItem>
);
};
@@ -83,6 +97,7 @@ export function useSafeNForm<T extends Record<string, any> = Record<string, unkn
props: ['label', 'path', 'placeholder'],
},
);
const SafeNFormItem = SafeNFormItemImpl as unknown as SafeNFormItemComponent;
// <<<<<
return {