فهرست منبع

fix(BasicForm): useForm 中 scheme 选项 slot 与 component冲突 (#3133)

* fix(BasicForm): useForm 中 scheme 选项 slot 与 component冲突

* chore: add type-predicate utils

* chore: add FormSchemaInner type
Li Kui 1 سال پیش
والد
کامیت
0bb76a86d2

+ 7 - 2
src/components/Form/src/BasicForm.vue

@@ -38,7 +38,7 @@
   </Form>
 </template>
 <script lang="ts">
-  import type { FormActionType, FormProps, FormSchema } from './types/form';
+  import type { FormActionType, FormProps, FormSchemaInner as FormSchema } from './types/form';
   import type { AdvanceState } from './types/hooks';
   import type { Ref } from 'vue';
 
@@ -125,7 +125,12 @@
             isHandleDateDefaultValue = true,
           } = schema;
           // handle date type
-          if (isHandleDateDefaultValue && defaultValue && dateItemType.includes(component)) {
+          if (
+            isHandleDateDefaultValue &&
+            defaultValue &&
+            component &&
+            dateItemType.includes(component)
+          ) {
             const valueFormat = componentProps ? componentProps['valueFormat'] : null;
             if (!Array.isArray(defaultValue)) {
               schema.defaultValue = valueFormat

+ 11 - 3
src/components/Form/src/components/FormItem.vue

@@ -2,7 +2,12 @@
   import { type Recordable, type Nullable } from '@vben/types';
   import type { PropType, Ref } from 'vue';
   import { computed, defineComponent, toRefs, unref } from 'vue';
-  import type { FormActionType, FormProps, FormSchema } from '../types/form';
+  import {
+    isComponentFormSchema,
+    type FormActionType,
+    type FormProps,
+    type FormSchemaInner as FormSchema,
+  } from '../types/form';
   import type { Rule as ValidationRule } from 'ant-design-vue/lib/form/interface';
   import type { TableActionType } from '/@/components/Table';
   import { Col, Divider, Form } from 'ant-design-vue';
@@ -241,6 +246,9 @@
       }
 
       function renderComponent() {
+        if (!isComponentFormSchema(props.schema)) {
+          return null;
+        }
         const {
           renderComponentContent,
           component,
@@ -352,7 +360,7 @@
           const getSuffix = isFunction(suffix) ? suffix(unref(getValues)) : suffix;
 
           // TODO 自定义组件验证会出现问题,因此这里框架默认将自定义组件设置手动触发验证,如果其他组件还有此问题请手动设置autoLink=false
-          if (NO_AUTO_LINK_COMPONENTS.includes(component)) {
+          if (component && NO_AUTO_LINK_COMPONENTS.includes(component)) {
             props.schema &&
               (props.schema.itemProps! = {
                 autoLink: false,
@@ -382,7 +390,7 @@
 
       return () => {
         const { colProps = {}, colSlot, renderColContent, component, slot } = props.schema;
-        if (!componentMap.has(component) && !slot) {
+        if (!component || (!componentMap.has(component) && !slot)) {
           return null;
         }
 

+ 1 - 1
src/components/Form/src/hooks/useAdvanced.ts

@@ -1,7 +1,7 @@
 import type { ColEx } from '../types';
 import type { AdvanceState } from '../types/hooks';
 import { ComputedRef, getCurrentInstance, Ref, shallowReactive, computed, unref, watch } from 'vue';
-import type { FormProps, FormSchema } from '../types/form';
+import type { FormProps, FormSchemaInner as FormSchema } from '../types/form';
 import { isBoolean, isFunction, isNumber, isObject } from '/@/utils/is';
 import { useBreakpoint } from '/@/hooks/event/useBreakpoint';
 import { useDebounceFn } from '@vueuse/core';

+ 6 - 2
src/components/Form/src/hooks/useAutoFocus.ts

@@ -1,5 +1,9 @@
 import type { ComputedRef, Ref } from 'vue';
-import type { FormSchema, FormActionType, FormProps } from '../types/form';
+import {
+  type FormSchemaInner as FormSchema,
+  type FormActionType,
+  type FormProps,
+} from '../types/form';
 
 import { unref, nextTick, watchEffect } from 'vue';
 
@@ -29,7 +33,7 @@ export async function useAutoFocus({
 
     const firstItem = schemas[0];
     // Only open when the first form item is input type
-    if (!firstItem.component.includes('Input')) {
+    if (!firstItem.component || !firstItem.component.includes('Input')) {
       return;
     }
 

+ 6 - 1
src/components/Form/src/hooks/useForm.ts

@@ -1,4 +1,9 @@
-import type { FormProps, FormActionType, UseFormReturnType, FormSchema } from '../types/form';
+import type {
+  FormProps,
+  FormActionType,
+  UseFormReturnType,
+  FormSchemaInner as FormSchema,
+} from '../types/form';
 import type { NamePath } from 'ant-design-vue/lib/form/interface';
 import type { DynamicProps } from '/#/utils';
 import { ref, onUnmounted, unref, nextTick, watch } from 'vue';

+ 2 - 2
src/components/Form/src/hooks/useFormEvents.ts

@@ -1,5 +1,5 @@
 import type { ComputedRef, Ref } from 'vue';
-import type { FormProps, FormSchema, FormActionType } from '../types/form';
+import type { FormProps, FormSchemaInner as FormSchema, FormActionType } from '../types/form';
 import type { NamePath } from 'ant-design-vue/lib/form/interface';
 import { unref, toRaw, nextTick } from 'vue';
 import {
@@ -335,7 +335,7 @@ export function useFormEvents({
    */
   function itemIsDateType(key: string) {
     return unref(getSchema).some((item) => {
-      return item.field === key ? dateItemType.includes(item.component) : false;
+      return item.field === key && item.component ? dateItemType.includes(item.component) : false;
     });
   }
 

+ 1 - 1
src/components/Form/src/hooks/useFormValues.ts

@@ -2,7 +2,7 @@ import { isArray, isFunction, isNotEmpty, isObject, isString, isNullOrUnDef } fr
 import { dateUtil } from '/@/utils/dateUtil';
 import { unref } from 'vue';
 import type { Ref, ComputedRef } from 'vue';
-import type { FormProps, FormSchema } from '../types/form';
+import type { FormProps, FormSchemaInner as FormSchema } from '../types/form';
 import { cloneDeep, get, set, unset } from 'lodash-es';
 
 interface UseFormValuesContext {

+ 1 - 1
src/components/Form/src/hooks/useLabelWidth.ts

@@ -1,6 +1,6 @@
 import type { Ref } from 'vue';
 import { computed, unref } from 'vue';
-import type { FormProps, FormSchema } from '../types/form';
+import type { FormProps, FormSchemaInner as FormSchema } from '../types/form';
 import { isNumber } from '/@/utils/is';
 
 export function useItemLabelWidth(schemaItemRef: Ref<FormSchema>, propsRef: Ref<FormProps>) {

+ 29 - 10
src/components/Form/src/types/form.ts

@@ -13,7 +13,7 @@ export type Rule = RuleObject & {
 };
 
 export interface RenderCallbackParams {
-  schema: FormSchema;
+  schema: FormSchemaInner;
   values: Recordable;
   model: Recordable;
   field: string;
@@ -29,12 +29,12 @@ export interface FormActionType {
   resetFields: () => Promise<void>;
   getFieldsValue: () => Recordable;
   clearValidate: (name?: string | string[]) => Promise<void>;
-  updateSchema: (data: Partial<FormSchema> | Partial<FormSchema>[]) => Promise<void>;
-  resetSchema: (data: Partial<FormSchema> | Partial<FormSchema>[]) => Promise<void>;
+  updateSchema: (data: Partial<FormSchemaInner> | Partial<FormSchemaInner>[]) => Promise<void>;
+  resetSchema: (data: Partial<FormSchemaInner> | Partial<FormSchemaInner>[]) => Promise<void>;
   setProps: (formProps: Partial<FormProps>) => Promise<void>;
   removeSchemaByField: (field: string | string[]) => Promise<void>;
   appendSchemaByField: (
-    schema: FormSchema | FormSchema[],
+    schema: FormSchemaInner | FormSchemaInner[],
     prefixField: string | undefined,
     first?: boolean | undefined,
   ) => Promise<void>;
@@ -127,7 +127,8 @@ export type RenderOpts = {
   disabled: boolean;
   [key: string]: any;
 };
-export interface FormSchema {
+
+interface BaseFormSchema {
   // Field name
   field: string;
   // Extra Fields name[]
@@ -151,8 +152,6 @@ export interface FormSchema {
   labelWidth?: string | number;
   // Disable the adjustment of labelWidth with global settings of formModel, and manually set labelCol and wrapperCol by yourself
   disabledLabelWidth?: boolean;
-  // render component
-  component: ComponentType;
   // Component parameters
   componentProps?:
     | ((opt: {
@@ -214,9 +213,6 @@ export interface FormSchema {
     | VNode[]
     | string;
 
-  // Custom slot, in from-item
-  slot?: string;
-
   // Custom slot, similar to renderColContent
   colSlot?: string;
 
@@ -224,6 +220,29 @@ export interface FormSchema {
 
   dynamicRules?: (renderCallbackParams: RenderCallbackParams) => Rule[];
 }
+export interface ComponentFormSchema extends BaseFormSchema {
+  // render component
+  component: ComponentType;
+}
+
+export interface SlotFormSchema extends BaseFormSchema {
+  // Custom slot, in from-item
+  slot: string;
+}
+
+export type FormSchema = ComponentFormSchema | SlotFormSchema;
+
+export type FormSchemaInner = Partial<ComponentFormSchema> &
+  Partial<SlotFormSchema> &
+  BaseFormSchema;
+
+export function isSlotFormSchema(schema: FormSchemaInner): schema is SlotFormSchema {
+  return 'slot' in schema;
+}
+
+export function isComponentFormSchema(schema: FormSchemaInner): schema is ComponentFormSchema {
+  return !isSlotFormSchema(schema);
+}
 export interface HelpComponentProps {
   maxWidth: string;
   // Whether to display the serial number