/** * https://www.naiveui.com/zh-CN/os-theme/components/form * * FIXME: `NForm` 和 `NFormItem` 的 slots 还没有实现。`NFormItemGi`组件。 */ import { get, set } from 'lodash-es'; import type { FormInst, FormItemProps, FormProps } from 'naive-ui'; import { NForm, NFormItem, formItemProps, NInput, formProps } from 'naive-ui'; import type { Get, Paths } from 'type-fest'; import type { SlotsType } from 'vue'; import { Comment } from 'vue'; type UseSafeNFormOptions = { initialFormValue?: FormValue; }; export function useSafeNForm = Record>( options: UseSafeNFormOptions = {}, ) { const formInst = ref(null); const formValue = ref(structuredClone(toRaw(options.initialFormValue)) || ({} as T)); // 创建类型安全的 Form 组件 type SafeNFormProps = FormProps; type SafeNFormSlots = SlotsType<{ default?: { count?: number }; }>; const SafeNForm = defineComponent( (props, ctx) => { return () => ( { formInst.value = inst as unknown as FormInst; }} > {ctx.slots.default?.({})} ); }, { name: 'SafeNForm', inheritAttrs: true, props: formProps, }, ); // <<<<< // >>>>> 创建类型安全的 FormItem 组件 type SafeNFormItemProps

& string> = FormItemProps & { path: P; }; type SafeNFormItemDefaultSlot

& string> = { value: Get; setValue: (val: Get) => void; }; const SafeNFormItemImpl = defineComponent< SafeNFormItemProps & string>, /* Emits */ [], /* EE */ never, SlotsType<{ default: SafeNFormItemDefaultSlot & string> }> >( (props, ctx) => { return () => { const value = get(formValue.value, props.path); function setValue(val: typeof value) { set(formValue.value, props.path, val); } const defaultSlotContent = ctx.slots.default?.({ value, setValue, }); // 如果没有提供默认 slot 内容,则渲染一个 NInput 作为默认输入组件 const renderDefaultNInput = defaultSlotContent?.some((v) => v.type !== Comment) ? null : ( ); return ( {defaultSlotContent} {renderDefaultNInput} ); }; }, { name: 'SafeNFormItem', inheritAttrs: false, props: Object.keys(formItemProps) as unknown as [keyof FormItemProps], }, ); // Expose a generic constructor so template literals narrow `path`. type SafeNFormItemComponent = { new

& string>( props: SafeNFormItemProps

, ): { $props: SafeNFormItemProps

; $slots: { default?: (scope: SafeNFormItemDefaultSlot

) => VNode[]; }; }; }; const SafeNFormItem = SafeNFormItemImpl as SafeNFormItemComponent; // <<<<< return { formValue, SafeNForm, SafeNFormItem, formInst, }; }