Bläddra i källkod

refactor(form): code optimization and reconstruction

vben 4 år sedan
förälder
incheckning
84c9d78fa7

+ 13 - 0
CHANGELOG.zh_CN.md

@@ -1,5 +1,17 @@
 ## Wip
 
+### ✨ Features
+
+- 表单组件现在支持直接传入 model 直接进行 set 操作,参考**组件->弹窗扩展->打开弹窗并传递数据**
+
+- modal 的 useModalInner 现在支持传入回调函数,用于接收外部`transferModalData`传进来的值,
+  - 用于处理打开弹窗对表单等组件的设置值。参考**组件->弹窗扩展->打开弹窗并传递数据**
+  - `receiveModalDataRef`这个值暂时保留。尽量少用。后续可能会删除。
+
+### ✨ Refactor
+
+- 表单代码优化重构
+
 ### 🎫 Chores
 
 - 添加部分注释
@@ -10,6 +22,7 @@
 
 - 修复本地代理 post 接口到 https 地址超时错误
 - 修复 modal 在不显示 footer 的时候全屏高度计算问题
+- 修复表单重置未删除校验信息错误
 
 ## 2.0.0-rc.6 (2020-10-28)
 

+ 61 - 352
src/components/Form/src/BasicForm.vue

@@ -25,6 +25,8 @@
 <script lang="ts">
   import type { FormActionType, FormProps, FormSchema } from './types/form';
   import type { Form as FormType, ValidateFields } from 'ant-design-vue/types/form/form';
+  import type { AdvanceState } from './types/hooks';
+  import type { Ref } from 'vue';
 
   import {
     defineComponent,
@@ -32,27 +34,22 @@
     ref,
     computed,
     unref,
-    toRaw,
-    watch,
     toRef,
     onMounted,
+    watchEffect,
   } from 'vue';
   import { Form, Row } from 'ant-design-vue';
   import FormItem from './FormItem';
   import { basicProps } from './props';
-  import { deepMerge, unique } from '/@/utils';
+  import { deepMerge } from '/@/utils';
   import FormAction from './FormAction';
 
   import { dateItemType } from './helper';
   import moment from 'moment';
-  import { isArray, isBoolean, isFunction, isNumber, isObject, isString } from '/@/utils/is';
   import { cloneDeep } from 'lodash-es';
-  import { useBreakpoint } from '/@/hooks/event/useBreakpoint';
-  // import { useThrottle } from '/@/hooks/core/useThrottle';
   import { useFormValues } from './hooks/useFormValues';
-  import type { ColEx } from './types';
-  import { NamePath } from 'ant-design-vue/types/form/form-item';
-  const BASIC_COL_LEN = 24;
+  import useAdvanced from './hooks/useAdvanced';
+  import { useFormAction } from './hooks/useFormAction';
 
   export default defineComponent({
     name: 'BasicForm',
@@ -61,13 +58,20 @@
     props: basicProps,
     emits: ['advanced-change', 'reset', 'submit', 'register'],
     setup(props, { emit }) {
-      let formModel = reactive({});
-      const advanceState = reactive({
+      const formModel = reactive({});
+
+      const actionState = reactive({
+        resetAction: {},
+        submitAction: {},
+      });
+
+      const advanceState = reactive<AdvanceState>({
         isAdvanced: true,
         hideAdvanceBtn: false,
         isLoad: false,
         actionSpan: 6,
       });
+
       const defaultValueRef = ref<any>({});
       const propsRef = ref<Partial<FormProps>>({});
       const schemaRef = ref<FormSchema[] | null>(null);
@@ -78,50 +82,24 @@
           return deepMerge(cloneDeep(props), unref(propsRef));
         }
       );
+
       // 获取表单基本配置
       const getProps = computed(
         (): FormProps => {
-          const resetAction = {
-            onClick: resetFields,
-          };
-          const submitAction = {
-            onClick: handleSubmit,
-          };
           return {
             ...unref(getMergePropsRef),
             resetButtonOptions: deepMerge(
-              resetAction,
+              actionState.resetAction,
               unref(getMergePropsRef).resetButtonOptions || {}
-            ) as any,
+            ),
             submitButtonOptions: deepMerge(
-              submitAction,
+              actionState.submitAction,
               unref(getMergePropsRef).submitButtonOptions || {}
-            ) as any,
+            ),
           };
         }
       );
 
-      const getActionPropsRef = computed(() => {
-        const {
-          resetButtonOptions,
-          submitButtonOptions,
-          showActionButtonGroup,
-          showResetButton,
-          showSubmitButton,
-          showAdvancedButton,
-          actionColOptions,
-        } = unref(getProps);
-        return {
-          resetButtonOptions,
-          submitButtonOptions,
-          show: showActionButtonGroup,
-          showResetButton,
-          showSubmitButton,
-          showAdvancedButton,
-          actionColOptions,
-        };
-      });
-
       const getSchema = computed((): FormSchema[] => {
         const schemas: FormSchema[] = unref(schemaRef) || (unref(getProps).schemas as any);
         for (const schema of schemas) {
@@ -133,305 +111,51 @@
         return schemas as FormSchema[];
       });
 
-      const getEmptySpanRef = computed((): number => {
-        if (!advanceState.isAdvanced) {
-          return 0;
-        }
-        const emptySpan = unref(getMergePropsRef).emptySpan || 0;
-
-        if (isNumber(emptySpan)) {
-          return emptySpan;
-        }
-        if (isObject(emptySpan)) {
-          const { span = 0 } = emptySpan;
-          const screen = unref(screenRef) as string;
-
-          const screenSpan = (emptySpan as any)[screen.toLowerCase()];
-          return screenSpan || span || 0;
-        }
-        return 0;
+      const { getActionPropsRef, handleToggleAdvanced } = useAdvanced({
+        advanceState,
+        emit,
+        getMergePropsRef,
+        getProps,
+        getSchema,
+        formModel,
+        defaultValueRef,
       });
 
-      const { realWidthRef, screenEnum, screenRef } = useBreakpoint();
-      // const [throttleUpdateAdvanced] = useThrottle(updateAdvanced, 30, { immediate: true });
-      watch(
-        [() => unref(getSchema), () => advanceState.isAdvanced, () => unref(realWidthRef)],
-        () => {
-          const { showAdvancedButton } = unref(getProps);
-          if (showAdvancedButton) {
-            updateAdvanced();
-          }
-        },
-        { immediate: true }
-      );
-
-      function initDefault() {
-        const schemas = unref(getSchema);
-        const obj: any = {};
-        schemas.forEach((item) => {
-          if (item.defaultValue) {
-            obj[item.field] = item.defaultValue;
-            (formModel as any)[item.field] = item.defaultValue;
-          }
-        });
-        defaultValueRef.value = obj;
-      }
-
-      function updateAdvanced() {
-        let itemColSum = 0;
-        let realItemColSum = 0;
-        for (const schema of unref(getSchema)) {
-          const { show, colProps } = schema;
-          let isShow = true;
-
-          if (isBoolean(show)) {
-            isShow = show;
-          }
-
-          if (isFunction(show)) {
-            isShow = show({
-              schema: schema,
-              model: formModel,
-              field: schema.field,
-              values: {
-                ...unref(defaultValueRef),
-                ...formModel,
-              },
-            });
-          }
-          if (isShow && colProps) {
-            const { itemColSum: sum, isAdvanced } = getAdvanced(colProps, itemColSum);
-
-            itemColSum = sum || 0;
-            if (isAdvanced) {
-              realItemColSum = itemColSum;
-            }
-            schema.isAdvanced = isAdvanced;
-          }
-        }
-        advanceState.actionSpan = (realItemColSum % BASIC_COL_LEN) + unref(getEmptySpanRef);
-        getAdvanced(
-          unref(getActionPropsRef).actionColOptions || { span: BASIC_COL_LEN },
-          itemColSum,
-          true
-        );
-        emit('advanced-change');
-      }
-      function getAdvanced(itemCol: Partial<ColEx>, itemColSum = 0, isLastAction = false) {
-        const width = unref(realWidthRef);
-
-        const mdWidth =
-          parseInt(itemCol.md as string) ||
-          parseInt(itemCol.xs as string) ||
-          parseInt(itemCol.sm as string) ||
-          (itemCol.span as number) ||
-          BASIC_COL_LEN;
-        const lgWidth = parseInt(itemCol.lg as string) || mdWidth;
-        const xlWidth = parseInt(itemCol.xl as string) || lgWidth;
-        const xxlWidth = parseInt(itemCol.xxl as string) || xlWidth;
-        if (width <= screenEnum.LG) {
-          itemColSum += mdWidth;
-        } else if (width < screenEnum.XL) {
-          itemColSum += lgWidth;
-        } else if (width < screenEnum.XXL) {
-          itemColSum += xlWidth;
-        } else {
-          itemColSum += xxlWidth;
-        }
-        if (isLastAction) {
-          advanceState.hideAdvanceBtn = false;
-          if (itemColSum <= BASIC_COL_LEN * 2) {
-            // 小于等于2行时,不显示收起展开按钮
-            advanceState.hideAdvanceBtn = true;
-            advanceState.isAdvanced = true;
-          } else if (
-            itemColSum > BASIC_COL_LEN * 2 &&
-            itemColSum <= BASIC_COL_LEN * (props.autoAdvancedLine || 3)
-          ) {
-            advanceState.hideAdvanceBtn = false;
-
-            // 大于3行默认收起
-          } else if (!advanceState.isLoad) {
-            advanceState.isLoad = true;
-            advanceState.isAdvanced = !advanceState.isAdvanced;
-          }
-          return { isAdvanced: advanceState.isAdvanced, itemColSum };
-        }
-        if (itemColSum > BASIC_COL_LEN) {
-          return { isAdvanced: advanceState.isAdvanced, itemColSum };
-        } else {
-          // 第一行始终显示
-          return { isAdvanced: true, itemColSum };
-        }
-      }
-
-      async function resetFields(): Promise<any> {
-        const { resetFunc, submitOnReset } = unref(getProps);
-        resetFunc && isFunction(resetFunc) && (await resetFunc());
-        const formEl = unref(formElRef);
-        if (!formEl) return;
-        Object.keys(formModel).forEach((key) => {
-          (formModel as any)[key] = defaultValueRef.value[key];
-        });
-        // const values = formEl.resetFields();
-        emit('reset', toRaw(formModel));
-        // return values;
-        submitOnReset && handleSubmit();
-      }
-
-      /**
-       * @description: 设置表单值
-       */
-      async function setFieldsValue(values: any): Promise<void> {
-        const fields = unref(getSchema)
-          .map((item) => item.field)
-          .filter(Boolean);
-        const formEl = unref(formElRef);
-        Object.keys(values).forEach((key) => {
-          const element = values[key];
-          if (fields.includes(key) && element !== undefined && element !== null) {
-            // 时间
-            if (itemIsDateType(key)) {
-              if (Array.isArray(element)) {
-                const arr: any[] = [];
-                for (const ele of element) {
-                  arr.push(moment(ele));
-                }
-                (formModel as any)[key] = arr;
-              } else {
-                (formModel as any)[key] = moment(element);
-              }
-            } else {
-              (formModel as any)[key] = element;
-            }
-            if (formEl) {
-              formEl.validateFields([key]);
-            }
-          }
-        });
-      }
-
-      /**
-       * @description: 表单提交
-       */
-      async function handleSubmit(e?: Event): Promise<void> {
-        e && e.preventDefault();
-        const { submitFunc } = unref(getProps);
-        if (submitFunc && isFunction(submitFunc)) {
-          await submitFunc();
-          return;
-        }
-        const formEl = unref(formElRef);
-        if (!formEl) return;
-        try {
-          const values = await formEl.validate();
-          const res = handleFormValues(values);
-          emit('submit', res);
-        } catch (error) {}
-      }
-
-      /**
-       * @description: 根据字段名删除
-       */
-      function removeSchemaByFiled(fields: string | string[]): void {
-        const schemaList: FormSchema[] = cloneDeep(unref(getSchema));
-        if (!fields) {
-          return;
-        }
-        let fieldList: string[] = fields as string[];
-        if (isString(fields)) {
-          fieldList = [fields];
-        }
-        for (const field of fieldList) {
-          _removeSchemaByFiled(field, schemaList);
-        }
-        schemaRef.value = schemaList as any;
-      }
-
-      /**
-       * @description: 根据字段名删除
-       */
-      function _removeSchemaByFiled(field: string, schemaList: FormSchema[]): void {
-        if (isString(field)) {
-          const index = schemaList.findIndex((schema) => schema.field === field);
-          if (index !== -1) {
-            schemaList.splice(index, 1);
-          }
-        }
-      }
-
-      /**
-       * @description: 往某个字段后面插入,如果没有插入最后一个
-       */
-      function appendSchemaByField(schema: FormSchema, prefixField?: string) {
-        const schemaList: FormSchema[] = cloneDeep(unref(getSchema));
-
-        const index = schemaList.findIndex((schema) => schema.field === prefixField);
-        const hasInList = schemaList.find((item) => item.field === schema.field);
-
-        if (hasInList) {
-          return;
-        }
-        if (!prefixField || index === -1) {
-          schemaList.push(schema);
-          schemaRef.value = schemaList as any;
-          return;
-        }
-        if (index !== -1) {
-          schemaList.splice(index + 1, 0, schema);
-        }
-        schemaRef.value = schemaList as any;
-      }
-
-      function updateSchema(data: Partial<FormSchema> | Partial<FormSchema>[]) {
-        let updateData: Partial<FormSchema>[] = [];
-        if (isObject(data)) {
-          updateData.push(data as FormSchema);
-        }
-        if (isArray(data)) {
-          updateData = [...data];
-        }
-        const hasField = updateData.every((item) => Reflect.has(item, 'field') && item.field);
-        if (!hasField) {
-          throw new Error('Must pass in the `field` field!');
-        }
-        const schema: FormSchema[] = [];
-        updateData.forEach((item) => {
-          unref(getSchema).forEach((val) => {
-            if (val.field === item.field) {
-              const newScheam = deepMerge(val, item);
-              schema.push(newScheam as FormSchema);
-            } else {
-              schema.push(val);
-            }
-          });
-        });
-        schemaRef.value = unique(schema, 'field') as any;
-      }
-
-      function handleToggleAdvanced() {
-        advanceState.isAdvanced = !advanceState.isAdvanced;
-      }
-
-      const handleFormValues = useFormValues(
-        toRef(props, 'transformDateFunc'),
-        toRef(props, 'fieldMapToTime')
-      );
+      const { handleFormValues, initDefault } = useFormValues({
+        transformDateFuncRef: toRef(props, 'transformDateFunc') as Ref<Fn<any>>,
+        fieldMapToTimeRef: toRef(props, 'fieldMapToTime'),
+        defaultValueRef,
+        getSchema,
+        formModel,
+      });
 
-      function getFieldsValue(): any {
-        const formEl = unref(formElRef);
-        if (!formEl) return;
-        return handleFormValues(toRaw(unref(formModel)));
-      }
+      const {
+        // handleSubmit,
+        setFieldsValue,
+        clearValidate,
+        validate,
+        validateFields,
+        getFieldsValue,
+        updateSchema,
+        appendSchemaByField,
+        removeSchemaByFiled,
+        resetFields,
+      } = useFormAction({
+        emit,
+        getProps,
+        formModel,
+        getSchema,
+        defaultValueRef,
+        formElRef: formElRef as any,
+        schemaRef: schemaRef as any,
+        handleFormValues,
+        actionState,
+      });
 
-      /**
-       * @description: 是否是时间
-       */
-      function itemIsDateType(key: string) {
-        return unref(getSchema).some((item) => {
-          return item.field === key ? dateItemType.includes(item.component!) : false;
-        });
-      }
+      watchEffect(() => {
+        if (!unref(getMergePropsRef).model) return;
+        setFieldsValue(unref(getMergePropsRef).model);
+      });
 
       /**
        * @description:设置表单
@@ -441,21 +165,6 @@
         propsRef.value = mergeProps;
       }
 
-      function validateFields(nameList?: NamePath[] | undefined) {
-        if (!formElRef.value) return;
-        return formElRef.value.validateFields(nameList);
-      }
-
-      function validate(nameList?: NamePath[] | undefined) {
-        if (!formElRef.value) return;
-        return formElRef.value.validate(nameList);
-      }
-
-      function clearValidate(name: string | string[]) {
-        if (!formElRef.value) return;
-        formElRef.value.clearValidate(name);
-      }
-
       const methods: Partial<FormActionType> = {
         getFieldsValue,
         setFieldsValue,

+ 42 - 40
src/components/Form/src/FormAction.tsx

@@ -57,6 +57,7 @@ export default defineComponent({
         ...props.resetButtonOptions,
       };
     });
+
     const getSubmitBtnOptionsRef = computed(() => {
       return {
         text: '查询',
@@ -80,10 +81,12 @@ export default defineComponent({
     function toggleAdvanced() {
       emit('toggle-advanced');
     }
+
     return () => {
       if (!props.show) {
         return;
       }
+
       const {
         showAdvancedButton,
         hideAdvanceBtn,
@@ -91,50 +94,49 @@ export default defineComponent({
         showResetButton,
         showSubmitButton,
       } = props;
+
       return (
-        <>
-          <Col {...unref(actionColOpt)} style={{ textAlign: 'right' }}>
-            {() => (
-              <Form.Item>
-                {() => (
-                  <>
-                    {getSlot(slots, 'advanceBefore')}
-                    {showAdvancedButton && !hideAdvanceBtn && (
-                      <Button type="default" class="mr-2" onClick={toggleAdvanced}>
-                        {() => (
-                          <>
-                            {isAdvanced ? '收起' : '展开'}
-                            {isAdvanced ? (
-                              <UpOutlined class="advanced-icon" />
-                            ) : (
-                              <DownOutlined class="advanced-icon" />
-                            )}
-                          </>
-                        )}
-                      </Button>
-                    )}
+        <Col {...unref(actionColOpt)} style={{ textAlign: 'right' }}>
+          {() => (
+            <Form.Item>
+              {() => (
+                <>
+                  {getSlot(slots, 'advanceBefore')}
+                  {showAdvancedButton && !hideAdvanceBtn && (
+                    <Button type="default" class="mr-2" onClick={toggleAdvanced}>
+                      {() => (
+                        <>
+                          {isAdvanced ? '收起' : '展开'}
+                          {isAdvanced ? (
+                            <UpOutlined class="advanced-icon" />
+                          ) : (
+                            <DownOutlined class="advanced-icon" />
+                          )}
+                        </>
+                      )}
+                    </Button>
+                  )}
 
-                    {getSlot(slots, 'resetBefore')}
-                    {showResetButton && (
-                      <Button type="default" class="mr-2" {...unref(getResetBtnOptionsRef)}>
-                        {() => unref(getResetBtnOptionsRef).text}
-                      </Button>
-                    )}
+                  {getSlot(slots, 'resetBefore')}
+                  {showResetButton && (
+                    <Button type="default" class="mr-2" {...unref(getResetBtnOptionsRef)}>
+                      {() => unref(getResetBtnOptionsRef).text}
+                    </Button>
+                  )}
 
-                    {getSlot(slots, 'submitBefore')}
-                    {showSubmitButton && (
-                      <Button type="primary" {...unref(getSubmitBtnOptionsRef)}>
-                        {() => unref(getSubmitBtnOptionsRef).text}
-                      </Button>
-                    )}
+                  {getSlot(slots, 'submitBefore')}
+                  {showSubmitButton && (
+                    <Button type="primary" {...unref(getSubmitBtnOptionsRef)}>
+                      {() => unref(getSubmitBtnOptionsRef).text}
+                    </Button>
+                  )}
 
-                    {getSlot(slots, 'submitAfter')}
-                  </>
-                )}
-              </Form.Item>
-            )}
-          </Col>
-        </>
+                  {getSlot(slots, 'submitAfter')}
+                </>
+              )}
+            </Form.Item>
+          )}
+        </Col>
       );
     };
   },

+ 11 - 9
src/components/Form/src/FormItem.tsx

@@ -1,17 +1,20 @@
+import type { ValidationRule } from 'ant-design-vue/types/form/form';
+import type { PropType } from 'vue';
+import type { FormProps } from './types/form';
+import type { FormSchema } from './types/form';
+
 import { defineComponent, computed, unref, toRef } from 'vue';
 import { Form, Col } from 'ant-design-vue';
 import { componentMap } from './componentMap';
+import { BasicHelp } from '/@/components/Basic';
 
-import type { PropType } from 'vue';
-import type { FormProps } from './types/form';
-import type { FormSchema } from './types/form';
 import { isBoolean, isFunction } from '/@/utils/is';
-import { useItemLabelWidth } from './hooks/useLabelWidth';
 import { getSlot } from '/@/utils/helper/tsxHelper';
-import { BasicHelp } from '/@/components/Basic';
 import { createPlaceholderMessage } from './helper';
 import { upperFirst, cloneDeep } from 'lodash-es';
-import { ValidationRule } from 'ant-design-vue/types/form/form';
+
+import { useItemLabelWidth } from './hooks/useLabelWidth';
+
 export default defineComponent({
   name: 'BasicFormItem',
   inheritAttrs: false,
@@ -50,6 +53,7 @@ export default defineComponent({
         schema: schema,
       };
     });
+
     const getShowRef = computed(() => {
       const { show, ifShow, isAdvanced } = props.schema;
       const { showAdvancedButton } = props.formProps;
@@ -226,6 +230,7 @@ export default defineComponent({
         </span>
       );
     }
+
     function renderItem() {
       const { itemProps, slot, render, field } = props.schema;
       const { labelCol, wrapperCol } = unref(itemLabelWidthRef);
@@ -255,11 +260,8 @@ export default defineComponent({
       const { colProps = {}, colSlot, renderColContent, component } = props.schema;
       if (!componentMap.has(component)) return null;
       const { baseColProps = {} } = props.formProps;
-
       const realColProps = { ...baseColProps, ...colProps };
-
       const { isIfShow, isShow } = unref(getShowRef);
-
       const getContent = () => {
         return colSlot
           ? getSlot(slots, colSlot)

+ 4 - 1
src/components/Form/src/helper.ts

@@ -1,4 +1,5 @@
-import { ComponentType } from './types/index';
+import type { ComponentType } from './types/index';
+
 /**
  * @description: 生成placeholder
  */
@@ -21,9 +22,11 @@ export function createPlaceholderMessage(component: ComponentType) {
   }
   return '';
 }
+
 function genType() {
   return ['DatePicker', 'MonthPicker', 'RangePicker', 'WeekPicker', 'TimePicker'];
 }
+
 /**
  * 时间字段
  */

+ 179 - 0
src/components/Form/src/hooks/useAdvanced.ts

@@ -0,0 +1,179 @@
+import type { ColEx } from '../types';
+import type { AdvanceState } from '../types/hooks';
+import type { ComputedRef, Ref } from 'vue';
+import type { FormProps, FormSchema } from '../types/form';
+
+import { computed, unref, watch } from 'vue';
+import { isBoolean, isFunction, isNumber, isObject } from '/@/utils/is';
+
+import { useBreakpoint } from '/@/hooks/event/useBreakpoint';
+
+const BASIC_COL_LEN = 24;
+
+interface UseAdvancedContext {
+  advanceState: AdvanceState;
+  emit: EmitType;
+  getMergePropsRef: ComputedRef<FormProps>;
+  getProps: ComputedRef<FormProps>;
+  getSchema: ComputedRef<FormSchema[]>;
+  formModel: any;
+  defaultValueRef: Ref<any>;
+}
+
+export default function ({
+  advanceState,
+  emit,
+  getMergePropsRef,
+  getProps,
+  getSchema,
+  formModel,
+  defaultValueRef,
+}: UseAdvancedContext) {
+  const { realWidthRef, screenEnum, screenRef } = useBreakpoint();
+  const getEmptySpanRef = computed((): number => {
+    if (!advanceState.isAdvanced) {
+      return 0;
+    }
+    const emptySpan = unref(getMergePropsRef).emptySpan || 0;
+
+    if (isNumber(emptySpan)) {
+      return emptySpan;
+    }
+    if (isObject(emptySpan)) {
+      const { span = 0 } = emptySpan;
+      const screen = unref(screenRef) as string;
+
+      const screenSpan = (emptySpan as any)[screen.toLowerCase()];
+      return screenSpan || span || 0;
+    }
+    return 0;
+  });
+
+  const getActionPropsRef = computed(() => {
+    const {
+      resetButtonOptions,
+      submitButtonOptions,
+      showActionButtonGroup,
+      showResetButton,
+      showSubmitButton,
+      showAdvancedButton,
+      actionColOptions,
+    } = unref(getProps);
+    return {
+      resetButtonOptions,
+      submitButtonOptions,
+      show: showActionButtonGroup,
+      showResetButton,
+      showSubmitButton,
+      showAdvancedButton,
+      actionColOptions,
+    };
+  });
+  watch(
+    [() => unref(getSchema), () => advanceState.isAdvanced, () => unref(realWidthRef)],
+    () => {
+      const { showAdvancedButton } = unref(getProps);
+      if (showAdvancedButton) {
+        updateAdvanced();
+      }
+    },
+    { immediate: true }
+  );
+
+  function getAdvanced(itemCol: Partial<ColEx>, itemColSum = 0, isLastAction = false) {
+    const width = unref(realWidthRef);
+
+    const mdWidth =
+      parseInt(itemCol.md as string) ||
+      parseInt(itemCol.xs as string) ||
+      parseInt(itemCol.sm as string) ||
+      (itemCol.span as number) ||
+      BASIC_COL_LEN;
+    const lgWidth = parseInt(itemCol.lg as string) || mdWidth;
+    const xlWidth = parseInt(itemCol.xl as string) || lgWidth;
+    const xxlWidth = parseInt(itemCol.xxl as string) || xlWidth;
+    if (width <= screenEnum.LG) {
+      itemColSum += mdWidth;
+    } else if (width < screenEnum.XL) {
+      itemColSum += lgWidth;
+    } else if (width < screenEnum.XXL) {
+      itemColSum += xlWidth;
+    } else {
+      itemColSum += xxlWidth;
+    }
+    if (isLastAction) {
+      advanceState.hideAdvanceBtn = false;
+      if (itemColSum <= BASIC_COL_LEN * 2) {
+        // 小于等于2行时,不显示收起展开按钮
+        advanceState.hideAdvanceBtn = true;
+        advanceState.isAdvanced = true;
+      } else if (
+        itemColSum > BASIC_COL_LEN * 2 &&
+        itemColSum <= BASIC_COL_LEN * (unref(getMergePropsRef).autoAdvancedLine || 3)
+      ) {
+        advanceState.hideAdvanceBtn = false;
+
+        // 大于3行默认收起
+      } else if (!advanceState.isLoad) {
+        advanceState.isLoad = true;
+        advanceState.isAdvanced = !advanceState.isAdvanced;
+      }
+      return { isAdvanced: advanceState.isAdvanced, itemColSum };
+    }
+    if (itemColSum > BASIC_COL_LEN) {
+      return { isAdvanced: advanceState.isAdvanced, itemColSum };
+    } else {
+      // 第一行始终显示
+      return { isAdvanced: true, itemColSum };
+    }
+  }
+
+  function updateAdvanced() {
+    let itemColSum = 0;
+    let realItemColSum = 0;
+    for (const schema of unref(getSchema)) {
+      const { show, colProps } = schema;
+      let isShow = true;
+
+      if (isBoolean(show)) {
+        isShow = show;
+      }
+
+      if (isFunction(show)) {
+        isShow = show({
+          schema: schema,
+          model: formModel,
+          field: schema.field,
+          values: {
+            ...unref(defaultValueRef),
+            ...formModel,
+          },
+        });
+      }
+
+      if (isShow && colProps) {
+        const { itemColSum: sum, isAdvanced } = getAdvanced(colProps, itemColSum);
+
+        itemColSum = sum || 0;
+        if (isAdvanced) {
+          realItemColSum = itemColSum;
+        }
+        schema.isAdvanced = isAdvanced;
+      }
+    }
+
+    advanceState.actionSpan = (realItemColSum % BASIC_COL_LEN) + unref(getEmptySpanRef);
+
+    getAdvanced(
+      unref(getActionPropsRef).actionColOptions || { span: BASIC_COL_LEN },
+      itemColSum,
+      true
+    );
+    emit('advanced-change');
+  }
+
+  function handleToggleAdvanced() {
+    advanceState.isAdvanced = !advanceState.isAdvanced;
+  }
+  return { getActionPropsRef, handleToggleAdvanced };
+}

+ 1 - 2
src/components/Form/src/hooks/useComponentRegister.ts

@@ -1,7 +1,6 @@
+import type { ComponentType } from '../types/index';
 import { tryOnUnmounted } from '/@/utils/helper/vueHelper';
 import { add, del } from '../componentMap';
-
-import { ComponentType } from '../types/index';
 export function useComponentRegister(compName: ComponentType, comp: any) {
   add(compName, comp);
   tryOnUnmounted(() => {

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

@@ -1,9 +1,9 @@
 import { ref, onUnmounted, unref } from 'vue';
 
 import { isInSetup } from '/@/utils/helper/vueHelper';
+import { isProdMode } from '/@/utils/env';
 
 import type { FormProps, FormActionType, UseFormReturnType, FormSchema } from '../types/form';
-import { isProdMode } from '/@/utils/env';
 import type { NamePath } from 'ant-design-vue/types/form/form-item';
 import type { ValidateFields } from 'ant-design-vue/types/form/form';
 
@@ -11,6 +11,7 @@ export function useForm(props?: Partial<FormProps>): UseFormReturnType {
   isInSetup();
   const formRef = ref<FormActionType | null>(null);
   const loadedRef = ref<boolean | null>(false);
+
   function getForm() {
     const form = unref(formRef);
     if (!form) {

+ 233 - 0
src/components/Form/src/hooks/useFormAction.ts

@@ -0,0 +1,233 @@
+import type { ComputedRef, Ref } from 'vue';
+import type { FormProps, FormSchema } from '../types/form';
+import type { Form as FormType } from 'ant-design-vue/types/form/form';
+import type { NamePath } from 'ant-design-vue/types/form/form-item';
+
+import { unref, toRaw } from 'vue';
+
+import { isArray, isFunction, isObject, isString } from '/@/utils/is';
+import { deepMerge, unique } from '/@/utils';
+import { dateItemType } from '../helper';
+import moment from 'moment';
+import { cloneDeep } from 'lodash-es';
+
+interface UseFormActionContext {
+  emit: EmitType;
+  getProps: ComputedRef<FormProps>;
+  getSchema: ComputedRef<FormSchema[]>;
+  formModel: any;
+  defaultValueRef: Ref<any>;
+  formElRef: Ref<FormType>;
+  schemaRef: Ref<FormSchema[]>;
+  handleFormValues: Fn;
+  actionState: {
+    resetAction: any;
+    submitAction: any;
+  };
+}
+export function useFormAction({
+  emit,
+  getProps,
+  formModel,
+  getSchema,
+  defaultValueRef,
+  formElRef,
+  schemaRef,
+  handleFormValues,
+  actionState,
+}: UseFormActionContext) {
+  async function resetFields(): Promise<any> {
+    const { resetFunc, submitOnReset } = unref(getProps);
+    resetFunc && isFunction(resetFunc) && (await resetFunc());
+    const formEl = unref(formElRef);
+    if (!formEl) return;
+    Object.keys(formModel).forEach((key) => {
+      (formModel as any)[key] = defaultValueRef.value[key];
+    });
+    // @ts-ignore
+    // TODO 官方组件库类型定义错误,可以不传参数
+    formEl.clearValidate();
+    emit('reset', toRaw(formModel));
+    // return values;
+    submitOnReset && handleSubmit();
+  }
+
+  /**
+   * @description: 设置表单值
+   */
+  async function setFieldsValue(values: any): Promise<void> {
+    const fields = unref(getSchema)
+      .map((item) => item.field)
+      .filter(Boolean);
+    const formEl = unref(formElRef);
+    Object.keys(values).forEach((key) => {
+      const element = values[key];
+      if (fields.includes(key) && element !== undefined && element !== null) {
+        // 时间
+        if (itemIsDateType(key)) {
+          if (Array.isArray(element)) {
+            const arr: any[] = [];
+            for (const ele of element) {
+              arr.push(moment(ele));
+            }
+            (formModel as any)[key] = arr;
+          } else {
+            (formModel as any)[key] = moment(element);
+          }
+        } else {
+          (formModel as any)[key] = element;
+        }
+        if (formEl) {
+          formEl.validateFields([key]);
+        }
+      }
+    });
+  }
+  /**
+   * @description: 根据字段名删除
+   */
+  function removeSchemaByFiled(fields: string | string[]): void {
+    const schemaList: FormSchema[] = cloneDeep(unref(getSchema));
+    if (!fields) {
+      return;
+    }
+    let fieldList: string[] = fields as string[];
+    if (isString(fields)) {
+      fieldList = [fields];
+    }
+    for (const field of fieldList) {
+      _removeSchemaByFiled(field, schemaList);
+    }
+    schemaRef.value = schemaList as any;
+  }
+
+  /**
+   * @description: 根据字段名删除
+   */
+  function _removeSchemaByFiled(field: string, schemaList: FormSchema[]): void {
+    if (isString(field)) {
+      const index = schemaList.findIndex((schema) => schema.field === field);
+      if (index !== -1) {
+        schemaList.splice(index, 1);
+      }
+    }
+  }
+
+  /**
+   * @description: 往某个字段后面插入,如果没有插入最后一个
+   */
+  function appendSchemaByField(schema: FormSchema, prefixField?: string) {
+    const schemaList: FormSchema[] = cloneDeep(unref(getSchema));
+
+    const index = schemaList.findIndex((schema) => schema.field === prefixField);
+    const hasInList = schemaList.find((item) => item.field === schema.field);
+
+    if (hasInList) {
+      return;
+    }
+    if (!prefixField || index === -1) {
+      schemaList.push(schema);
+      schemaRef.value = schemaList as any;
+      return;
+    }
+    if (index !== -1) {
+      schemaList.splice(index + 1, 0, schema);
+    }
+    schemaRef.value = schemaList as any;
+  }
+
+  function updateSchema(data: Partial<FormSchema> | Partial<FormSchema>[]) {
+    let updateData: Partial<FormSchema>[] = [];
+    if (isObject(data)) {
+      updateData.push(data as FormSchema);
+    }
+    if (isArray(data)) {
+      updateData = [...data];
+    }
+    const hasField = updateData.every((item) => Reflect.has(item, 'field') && item.field);
+    if (!hasField) {
+      throw new Error('Must pass in the `field` field!');
+    }
+    const schema: FormSchema[] = [];
+    updateData.forEach((item) => {
+      unref(getSchema).forEach((val) => {
+        if (val.field === item.field) {
+          const newScheam = deepMerge(val, item);
+          schema.push(newScheam as FormSchema);
+        } else {
+          schema.push(val);
+        }
+      });
+    });
+    schemaRef.value = unique(schema, 'field') as any;
+  }
+
+  function getFieldsValue(): any {
+    const formEl = unref(formElRef);
+    if (!formEl) return;
+    return handleFormValues(toRaw(unref(formModel)));
+  }
+
+  /**
+   * @description: 是否是时间
+   */
+  function itemIsDateType(key: string) {
+    return unref(getSchema).some((item) => {
+      return item.field === key ? dateItemType.includes(item.component!) : false;
+    });
+  }
+
+  function validateFields(nameList?: NamePath[] | undefined) {
+    if (!formElRef.value) return;
+    return formElRef.value.validateFields(nameList);
+  }
+
+  function validate(nameList?: NamePath[] | undefined) {
+    if (!formElRef.value) return;
+    return formElRef.value.validate(nameList);
+  }
+
+  function clearValidate(name: string | string[]) {
+    if (!formElRef.value) return;
+    formElRef.value.clearValidate(name);
+  }
+
+  /**
+   * @description: 表单提交
+   */
+  async function handleSubmit(e?: Event): Promise<void> {
+    e && e.preventDefault();
+    const { submitFunc } = unref(getProps);
+    if (submitFunc && isFunction(submitFunc)) {
+      await submitFunc();
+      return;
+    }
+    const formEl = unref(formElRef);
+    if (!formEl) return;
+    try {
+      const values = await formEl.validate();
+      const res = handleFormValues(values);
+      emit('submit', res);
+    } catch (error) {}
+  }
+  actionState.resetAction = {
+    onClick: resetFields,
+  };
+
+  actionState.submitAction = {
+    onClick: handleSubmit,
+  };
+
+  return {
+    handleSubmit,
+    clearValidate,
+    validate,
+    validateFields,
+    getFieldsValue,
+    updateSchema,
+    appendSchemaByField,
+    removeSchemaByFiled,
+    resetFields,
+    setFieldsValue,
+  };
+}

+ 31 - 7
src/components/Form/src/hooks/useFormValues.ts

@@ -1,13 +1,23 @@
 import { isArray, isFunction, isObject, isString } from '/@/utils/is';
 import moment from 'moment';
 import { unref } from 'vue';
-import type { Ref } from 'vue';
-import type { FieldMapToTime } from '../types/form';
+import type { Ref, ComputedRef } from 'vue';
+import type { FieldMapToTime, FormSchema } from '../types/form';
 
-export function useFormValues(
-  transformDateFuncRef: Ref<Fn>,
-  fieldMapToTimeRef: Ref<FieldMapToTime>
-) {
+interface UseFormValuesContext {
+  transformDateFuncRef: Ref<Fn>;
+  fieldMapToTimeRef: Ref<FieldMapToTime>;
+  defaultValueRef: Ref<any>;
+  getSchema: ComputedRef<FormSchema[]>;
+  formModel: any;
+}
+export function useFormValues({
+  transformDateFuncRef,
+  fieldMapToTimeRef,
+  defaultValueRef,
+  getSchema,
+  formModel,
+}: UseFormValuesContext) {
   // 处理表单值
   function handleFormValues(values: any) {
     if (!isObject(values)) {
@@ -35,6 +45,7 @@ export function useFormValues(
     }
     return handleRangeTimeValue(resMap);
   }
+
   /**
    * @description: 处理时间区间参数
    */
@@ -58,5 +69,18 @@ export function useFormValues(
 
     return values;
   }
-  return handleFormValues;
+
+  function initDefault() {
+    const schemas = unref(getSchema);
+    const obj: any = {};
+    schemas.forEach((item) => {
+      if (item.defaultValue) {
+        obj[item.field] = item.defaultValue;
+        (formModel as any)[item.field] = item.defaultValue;
+      }
+    });
+    defaultValueRef.value = obj;
+  }
+
+  return { handleFormValues, initDefault };
 }

+ 4 - 0
src/components/Form/src/props.ts

@@ -3,6 +3,10 @@ import type { PropType } from 'vue';
 import type { ColEx } from './types';
 
 export const basicProps = {
+  model: {
+    type: Object as PropType<any>,
+    default: {},
+  },
   // 标签宽度  固定宽度
   labelWidth: {
     type: [Number, String] as PropType<number | string>,

+ 2 - 0
src/components/Form/src/types/form.ts

@@ -35,6 +35,8 @@ export type RegisterFn = (formInstance: FormActionType) => void;
 export type UseFormReturnType = [RegisterFn, FormActionType];
 
 export interface FormProps {
+  // 表单值
+  model?: any;
   // 整个表单所有项宽度
   labelWidth?: number | string;
   // 重置时提交

+ 6 - 0
src/components/Form/src/types/hooks.ts

@@ -0,0 +1,6 @@
+export interface AdvanceState {
+  isAdvanced: boolean;
+  hideAdvanceBtn: boolean;
+  isLoad: boolean;
+  actionSpan: number;
+}

+ 15 - 15
src/components/Modal/src/ModalWrapper.tsx

@@ -15,13 +15,12 @@ import {
 import { Spin } from 'ant-design-vue';
 
 import { useWindowSizeFn } from '/@/hooks/event/useWindowSize';
-// import { useTimeout } from '/@/hooks/core/useTimeout';
+import { useTimeout } from '/@/hooks/core/useTimeout';
 
 import { getSlot } from '/@/utils/helper/tsxHelper';
 import { useElResize } from '/@/hooks/event/useElResize';
 export default defineComponent({
   name: 'ModalWrapper',
-  emits: ['heightChange', 'getExtHeight'],
   props: {
     loading: {
       type: Boolean as PropType<boolean>,
@@ -52,6 +51,7 @@ export default defineComponent({
       default: false,
     },
   },
+  emits: ['heightChange', 'getExtHeight'],
   setup(props: ModalWrapperProps, { slots, emit }) {
     const wrapperRef = ref<HTMLElement | null>(null);
     const spinRef = ref<any>(null);
@@ -66,7 +66,7 @@ export default defineComponent({
     });
 
     // 重试次数
-    // let tryCount = 0;
+    let tryCount = 0;
     let stopElResizeFn: Fn = () => {};
 
     watchEffect(() => {
@@ -123,17 +123,17 @@ export default defineComponent({
         }
         await nextTick();
         const spinEl = unref(spinRef);
-        // if (!spinEl) {
-        //   useTimeout(() => {
-        //     // retry
-        //     if (tryCount < 3) {
-        //       setModalHeight();
-        //     }
-        //     tryCount++;
-        //   }, 10);
-        //   return;
-        // }
-        // tryCount = 0;
+        if (!spinEl) {
+          useTimeout(() => {
+            // retry
+            if (tryCount < 3) {
+              setModalHeight();
+            }
+            tryCount++;
+          }, 10);
+          return;
+        }
+        tryCount = 0;
 
         const spinContainerEl = spinEl.$el.querySelector('.ant-spin-container') as HTMLElement;
         if (!spinContainerEl) return;
@@ -142,7 +142,7 @@ export default defineComponent({
 
         if (props.fullScreen) {
           realHeightRef.value =
-            window.innerHeight - props.modalFooterHeight - props.modalHeaderHeight - 26;
+            window.innerHeight - props.modalFooterHeight - props.modalHeaderHeight - 6;
         } else {
           realHeightRef.value = realHeight > maxHeight ? maxHeight : realHeight + 16 + 30;
         }

+ 10 - 2
src/components/Modal/src/useModal.ts

@@ -5,8 +5,9 @@ import type {
   ReturnMethods,
   UseModalInnerReturnType,
 } from './types';
-import { ref, onUnmounted, unref, getCurrentInstance, reactive, computed } from 'vue';
+import { ref, onUnmounted, unref, getCurrentInstance, reactive, computed, watchEffect } from 'vue';
 import { isProdMode } from '/@/utils/env';
+import { isFunction } from '/@/utils/is';
 const dataTransferRef = reactive<any>({});
 
 /**
@@ -58,7 +59,7 @@ export function useModal(): UseModalReturnType {
   return [register, methods];
 }
 
-export const useModalInner = (): UseModalInnerReturnType => {
+export const useModalInner = (callbackFn?: Fn): UseModalInnerReturnType => {
   const modalInstanceRef = ref<ModalMethods | null>(null);
   const currentInstall = getCurrentInstance();
   const uidRef = ref<string>('');
@@ -81,6 +82,13 @@ export const useModalInner = (): UseModalInnerReturnType => {
     currentInstall.emit('register', modalInstance);
   };
 
+  watchEffect(() => {
+    const data = dataTransferRef[unref(uidRef)];
+    if (!data) return;
+    if (!callbackFn || !isFunction(callbackFn)) return;
+    callbackFn(data);
+  });
+
   return [
     register,
     {

+ 56 - 4
src/views/demo/comp/modal/Modal4.vue

@@ -1,16 +1,68 @@
 <template>
   <BasicModal v-bind="$attrs" @register="register" title="Modal Title">
     <p class="h-20">外部传递数据: {{ receiveModalDataRef }}</p>
+    <BasicForm @register="registerForm" :model="model" />
   </BasicModal>
 </template>
 <script lang="ts">
-  import { defineComponent } from 'vue';
+  import { defineComponent, nextTick, ref } from 'vue';
   import { BasicModal, useModalInner } from '/@/components/Modal';
+  import { BasicForm, FormSchema, useForm } from '/@/components/Form/index';
+  const schemas: FormSchema[] = [
+    {
+      field: 'field1',
+      component: 'Input',
+      label: '字段1',
+      colProps: {
+        span: 12,
+      },
+      defaultValue: '111',
+    },
+    {
+      field: 'field2',
+      component: 'Input',
+      label: '字段2',
+      colProps: {
+        span: 12,
+      },
+    },
+  ];
   export default defineComponent({
-    components: { BasicModal },
+    components: { BasicModal, BasicForm },
     setup() {
-      const [register, { receiveModalDataRef }] = useModalInner();
-      return { register, receiveModalDataRef };
+      const modelRef = ref({});
+      const [
+        registerForm,
+        {
+          // setFieldsValue,
+          // setProps
+        },
+      ] = useForm({
+        labelWidth: 120,
+        schemas,
+        showActionButtonGroup: false,
+        actionColOptions: {
+          span: 24,
+        },
+      });
+
+      const [register, { receiveModalDataRef }] = useModalInner((data) => {
+        nextTick(() => {
+          // 方式1
+          // setFieldsValue({
+          //   field2: data.data,
+          //   field1: data.info,
+          // });
+
+          // 方式2
+          modelRef.value = { field2: data.data, field1: data.info };
+
+          // setProps({
+          //   model:{ field2: data.data, field1: data.info }
+          // })
+        });
+      });
+      return { register, receiveModalDataRef, schemas, registerForm, model: modelRef };
     },
   });
 </script>

+ 6 - 0
src/views/demo/comp/modal/index.vue

@@ -48,6 +48,12 @@
           data: 'content',
           info: 'Info',
         });
+        // setTimeout(() => {
+        //   transferModalData({
+        //     data: 'content1',
+        //     info: 'Info1',
+        //   });
+        // }, 3000);
         openModal4(true);
       }
       function openModalLoading() {