feat: 更新 useSafeNForm 组件,优化类型定义 [skip ci]
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
@@ -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: {
|
type SafeNFormItemSlotScope<P extends Paths<T> & string> = {
|
||||||
value: Get<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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user