refactor: 优化 SafeNFormItem 组件的类型定义和插槽处理
This commit is contained in:
@@ -63,10 +63,10 @@ function handleValidateClick() {
|
|||||||
|
|
||||||
<SafeNFormItem #default="slotProps" path="user.name">
|
<SafeNFormItem #default="slotProps" path="user.name">
|
||||||
<div class="border">
|
<div class="border">
|
||||||
<pre>{{ JSON.stringify({ slotProps }, null, 2) }}</pre>
|
<!-- <pre>{{ JSON.stringify({ slotProps: toRaw(slotProps) }, null, 2) }}</pre> -->
|
||||||
<div>v: {{ slotProps.value }}</div>
|
<div>v: {{ slotProps.value }}</div>
|
||||||
</div>
|
</div>
|
||||||
<NInput :value="slotProps.value" placeholder="SafeNFormItem" />
|
<NInput v-model:value="slotProps.value" placeholder="SafeNFormItem" />
|
||||||
</SafeNFormItem>
|
</SafeNFormItem>
|
||||||
|
|
||||||
<n-form-item>
|
<n-form-item>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// https://www.naiveui.com/zh-CN/os-theme/components/form
|
// https://www.naiveui.com/zh-CN/os-theme/components/form
|
||||||
// initialFormValue
|
// initialFormValue
|
||||||
|
|
||||||
import { get } from 'lodash-es';
|
import { get, set } from 'lodash-es';
|
||||||
import type { FormInst } from 'naive-ui';
|
import type { FormInst } from 'naive-ui';
|
||||||
import type { Get, Paths } from 'type-fest';
|
import type { Get, Paths } from 'type-fest';
|
||||||
import type { SlotsType } from 'vue';
|
import type { SlotsType } from 'vue';
|
||||||
@@ -48,45 +48,36 @@ export function useSafeNForm<T extends Record<string, any> = Record<string, unkn
|
|||||||
// <<<<<
|
// <<<<<
|
||||||
|
|
||||||
// >>>>> 创建类型安全的 FormItem 组件
|
// >>>>> 创建类型安全的 FormItem 组件
|
||||||
|
|
||||||
type SafeNFormItemProps<P extends Paths<T> & string> = {
|
type SafeNFormItemProps<P extends Paths<T> & string> = {
|
||||||
label?: string;
|
label?: string;
|
||||||
path: P;
|
path: P;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type SafeNFormItemSlotScope<P extends Paths<T> & string> = {
|
type SafeNFormItemDefaultSlot<P extends Paths<T> & string> = {
|
||||||
value: Get<T, P>;
|
value: Get<T, P>;
|
||||||
};
|
};
|
||||||
|
|
||||||
type SafeNFormItemSlotsDefinition<P extends Paths<T> & string> = SlotsType<{
|
|
||||||
default: SafeNFormItemSlotScope<P>;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
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<
|
const SafeNFormItemImpl = defineComponent<
|
||||||
SafeNFormItemProps<Paths<T> & string>,
|
SafeNFormItemProps<Paths<T> & string>,
|
||||||
/* Emits */ [],
|
/* Emits */ [],
|
||||||
/* EE */ never,
|
/* EE */ never,
|
||||||
SafeNFormItemSlotsDefinition<Paths<T> & string>
|
SlotsType<{ default: SafeNFormItemDefaultSlot<Paths<T> & string> }>
|
||||||
>(
|
>(
|
||||||
(props, ctx) => {
|
(props, ctx) => {
|
||||||
return () => {
|
return () => {
|
||||||
const value = get(formValue.value, props.path) as Get<T, typeof props.path>;
|
// const value = get(formValue.value, props.path) as Get<T, typeof props.path>;
|
||||||
const slots = ctx.slots;
|
const value = computed({
|
||||||
|
get() {
|
||||||
|
return get(formValue.value, props.path) as Get<T, typeof props.path>;
|
||||||
|
},
|
||||||
|
set(v) {
|
||||||
|
set(formValue.value, props.path, v);
|
||||||
|
},
|
||||||
|
});
|
||||||
return (
|
return (
|
||||||
<NFormItem path={props.path} label={props.label}>
|
<NFormItem path={props.path} label={props.label}>
|
||||||
{slots.default?.({ value })}
|
{ctx.slots.default?.({ value: value.value })}
|
||||||
</NFormItem>
|
</NFormItem>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -97,7 +88,19 @@ 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;
|
|
||||||
|
// Expose a generic constructor so template literals narrow `path`.
|
||||||
|
type SafeNFormItemComponent = {
|
||||||
|
new <P extends Paths<T> & string>(
|
||||||
|
props: SafeNFormItemProps<P>,
|
||||||
|
): {
|
||||||
|
$props: SafeNFormItemProps<P>;
|
||||||
|
$slots: {
|
||||||
|
default?: (scope: SafeNFormItemDefaultSlot<P>) => VNode[];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const SafeNFormItem = SafeNFormItemImpl as SafeNFormItemComponent;
|
||||||
// <<<<<
|
// <<<<<
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
Reference in New Issue
Block a user