Эх сурвалжийг харах

feat: Form useForm 完成

kailong321200875 1 жил өмнө
parent
commit
3e4e27c21f

+ 1 - 1
src/components/Editor/src/Editor.vue

@@ -115,7 +115,7 @@ defineExpose({
 </script>
 
 <template>
-  <div class="border-1 border-solid border-[var(--tags-view-border-color)] z-99">
+  <div class="border-1 border-solid border-[var(--tags-view-border-color)] z-10">
     <!-- 工具栏 -->
     <Toolbar
       :editor="editorRef"

+ 2 - 0
src/components/Form/index.ts

@@ -41,6 +41,8 @@ export interface FormExpose {
   setSchema: (schemaProps: FormSetProps[]) => void
   formModel: Recordable
   getElFormRef: () => ComponentRef<typeof ElForm>
+  getComponentExpose: (field: string) => any
+  getFormItemExpose: (field: string) => any
 }
 
 export { Form }

+ 49 - 6
src/components/Form/src/Form.vue

@@ -73,6 +73,12 @@ export default defineComponent({
       return propsObj
     })
 
+    // 存储表单实例
+    const formComponents = ref({})
+
+    // 存储form-item实例
+    const formItemComponents = ref({})
+
     // 表单数据
     const formModel = ref<Recordable>({})
 
@@ -125,7 +131,37 @@ export default defineComponent({
 
     const getOptions = async (fn: Function, field: string) => {
       const options = await fn()
-      console.log(field, options)
+      setSchema([
+        {
+          field,
+          path: 'componentProps.options',
+          value: options
+        }
+      ])
+    }
+
+    /**
+     * @description: 获取表单组件实例
+     * @param filed 表单字段
+     */
+    const getComponentExpose = (filed: string) => {
+      return unref(formComponents)[filed]
+    }
+
+    /**
+     * @description: 获取formItem实例
+     * @param filed 表单字段
+     */
+    const getFormItemExpose = (filed: string) => {
+      return unref(formItemComponents)[filed]
+    }
+
+    const setComponentRefMap = (ref: any, filed: string) => {
+      formComponents.value[filed] = ref
+    }
+
+    const setFormItemRefMap = (ref: any, filed: string) => {
+      formItemComponents.value[filed] = ref
     }
 
     expose({
@@ -135,14 +171,15 @@ export default defineComponent({
       delSchema,
       addSchema,
       setSchema,
-      getElFormRef
+      getElFormRef,
+      getComponentExpose,
+      getFormItemExpose
     })
 
     // 监听表单结构化数组,重新生成formModel
     watch(
       () => unref(getProps).schema,
       (schema = []) => {
-        console.log('@@####')
         formModel.value = initModel(schema, unref(formModel))
       },
       {
@@ -193,8 +230,8 @@ export default defineComponent({
       }
       const formItemSlots: Recordable = {
         default: () => {
-          if (slots[item.field]) {
-            return getSlot(slots, item.field, formModel.value)
+          if (item?.formItemProps?.slots?.default) {
+            return item?.formItemProps?.slots?.default(formModel.value)
           } else {
             const Com = componentMap[item.component as string] as ReturnType<typeof defineComponent>
 
@@ -254,6 +291,7 @@ export default defineComponent({
               return (
                 <Com
                   vModel={formModel.value[item.field]}
+                  ref={(el: any) => setComponentRefMap(el, item.field)}
                   {...(autoSetPlaceholder && setTextPlaceholder(item))}
                   {...setComponentProps(item)}
                   style={item.componentProps?.style || {}}
@@ -278,7 +316,12 @@ export default defineComponent({
         }
       }
       return (
-        <ElFormItem {...(item.formItemProps || {})} prop={item.field} label={item.label || ''}>
+        <ElFormItem
+          ref={(el: any) => setFormItemRefMap(el, item.field)}
+          {...(item.formItemProps || {})}
+          prop={item.field}
+          label={item.label || ''}
+        >
           {formItemSlots}
         </ElFormItem>
       )

+ 1 - 0
src/components/Form/src/types/index.ts

@@ -756,6 +756,7 @@ export interface FormSetProps {
 }
 
 export interface FormItemProps {
+  ref?: any
   labelWidth?: string | number
   required?: boolean
   rules?: FormItemRule | FormItemRule[]

+ 16 - 2
src/hooks/web/useForm.ts

@@ -1,5 +1,5 @@
 import type { Form, FormExpose } from '@/components/Form'
-import type { ElForm } from 'element-plus'
+import type { ElForm, ElFormItem } from 'element-plus'
 import { ref, unref, nextTick } from 'vue'
 import { FormSchema, FormSetProps, FormProps } from '@/components/Form'
 
@@ -74,12 +74,26 @@ export const useForm = () => {
     getFormData: async <T = Recordable>(): Promise<T> => {
       const form = await getForm()
       return form?.formModel as T
+    },
+
+    getComponentExpose: async (field: string) => {
+      const form = await getForm()
+      return form?.getComponentExpose(field)
+    },
+
+    getFormItemExpose: async (field: string) => {
+      const form = await getForm()
+      return form?.getFormItemExpose(field) as ComponentRef<typeof ElFormItem>
+    },
+
+    getElFormExpose: async () => {
+      await getForm()
+      return unref(elFormRef)
     }
   }
 
   return {
     register,
-    formRef: elFormRef,
     methods
   }
 }

+ 3 - 1
src/locales/en.ts

@@ -285,7 +285,9 @@ export default {
     richText: 'Rich text',
     form: 'Form',
     // 远程加载
-    remoteLoading: 'Remote loading'
+    remoteLoading: 'Remote loading',
+    // 聚焦
+    focus: 'Focus'
   },
   guideDemo: {
     guide: 'Guide',

+ 3 - 1
src/locales/zh-CN.ts

@@ -284,7 +284,9 @@ export default {
     richText: '富文本编辑器',
     form: '表单',
     // 远程加载
-    remoteLoading: '远程加载'
+    remoteLoading: '远程加载',
+    // 聚焦
+    focus: '聚焦'
   },
   guideDemo: {
     guide: '引导页',

+ 53 - 1
src/views/Components/Form/DefaultForm.vue

@@ -14,7 +14,8 @@ import {
   ElRadio,
   ElRadioButton,
   ElCheckbox,
-  ElCheckboxButton
+  ElCheckboxButton,
+  ElInput
 } from 'element-plus'
 import { getDictOneApi } from '@/api/common'
 
@@ -1384,6 +1385,18 @@ const schema = reactive<FormSchema[]>([
       }
     }
   },
+  {
+    field: 'field69-1',
+    component: 'Input',
+    label: `custom formItem`,
+    formItemProps: {
+      slots: {
+        default: (formModel: any) => {
+          return <ElInput v-model={formModel['field69-1']} />
+        }
+      }
+    }
+  },
   {
     field: 'field70',
     component: 'Divider',
@@ -1401,6 +1414,45 @@ const schema = reactive<FormSchema[]>([
       const res = await getDictOneApi()
       return res.data
     }
+  },
+  {
+    field: 'field72',
+    label: `${t('formDemo.selectV2')}`,
+    component: 'SelectV2',
+    componentProps: {
+      options: []
+    },
+    // 远程加载option
+    optionApi: async () => {
+      const res = await getDictOneApi()
+      return res.data
+    }
+  },
+  {
+    field: 'field73',
+    label: `${t('formDemo.checkboxGroup')}`,
+    component: 'CheckboxGroup',
+    componentProps: {
+      options: []
+    },
+    // 远程加载option
+    optionApi: async () => {
+      const res = await getDictOneApi()
+      return res.data
+    }
+  },
+  {
+    field: 'field74',
+    label: `${t('formDemo.radioGroup')}`,
+    component: 'RadioGroup',
+    componentProps: {
+      options: []
+    },
+    // 远程加载option
+    optionApi: async () => {
+      const res = await getDictOneApi()
+      return res.data
+    }
   }
 ])
 

+ 45 - 14
src/views/Components/Form/UseFormDemo.vue

@@ -4,7 +4,7 @@ import { ContentWrap } from '@/components/ContentWrap'
 import { useI18n } from '@/hooks/web/useI18n'
 import { useForm } from '@/hooks/web/useForm'
 import { reactive, unref, ref } from 'vue'
-import { ElButton } from 'element-plus'
+import { ElButton, ElInput } from 'element-plus'
 import { useValidator } from '@/hooks/web/useValidator'
 import { getDictOneApi } from '@/api/common'
 
@@ -36,6 +36,9 @@ const schema = reactive<FormSchema[]>([
           value: '2'
         }
       ]
+    },
+    formItemProps: {
+      rules: [required()]
     }
   },
   {
@@ -92,31 +95,37 @@ const schema = reactive<FormSchema[]>([
   }
 ])
 
-const { register, methods, elFormRef } = useForm()
+const { register, methods } = useForm()
+const {
+  setProps,
+  delSchema,
+  addSchema,
+  setValues,
+  setSchema,
+  getComponentExpose,
+  getFormItemExpose,
+  getElFormExpose
+} = methods
 
 const changeLabelWidth = (width: number | string) => {
-  const { setProps } = methods
   setProps({
     labelWidth: width
   })
 }
 
 const changeSize = (size: string) => {
-  const { setProps } = methods
   setProps({
     size
   })
 }
 
 const changeDisabled = (bool: boolean) => {
-  const { setProps } = methods
   setProps({
     disabled: bool
   })
 }
 
 const changeSchema = (del: boolean) => {
-  const { delSchema, addSchema } = methods
   if (del) {
     delSchema('field2')
   } else if (!del && schema[1].field !== 'field2') {
@@ -143,10 +152,10 @@ const changeSchema = (del: boolean) => {
   }
 }
 
-const setValue = (reset: boolean) => {
-  const { setValues } = methods
+const setValue = async (reset: boolean) => {
+  const elFormExpose = await getElFormExpose()
   if (reset) {
-    unref(elFormRef)?.resetFields()
+    elFormExpose?.resetFields()
   } else {
     setValues({
       field1: 'field1',
@@ -162,7 +171,6 @@ const setValue = (reset: boolean) => {
 const index = ref(7)
 
 const setLabel = () => {
-  const { setSchema } = methods
   setSchema([
     {
       field: 'field2',
@@ -212,14 +220,16 @@ const addItem = () => {
   index.value++
 }
 
-const formValidation = () => {
-  unref(elFormRef)!.validate((isValid) => {
+const formValidation = async () => {
+  const elFormExpose = await getElFormExpose()
+  elFormExpose?.validate((isValid) => {
     console.log(isValid)
   })
 }
 
-const verifyReset = () => {
-  unref(elFormRef)?.resetFields()
+const verifyReset = async () => {
+  const elFormExpose = await getElFormExpose()
+  elFormExpose?.resetFields()
 }
 
 const getDictOne = async () => {
@@ -235,6 +245,20 @@ const getDictOne = async () => {
     ])
   }
 }
+
+const inoutFocus = async () => {
+  const inputEl: ComponentRef<typeof ElInput> = await getComponentExpose('field1')
+  inputEl?.focus()
+}
+
+const inoutValidation = async () => {
+  const formItem = await getFormItemExpose('field1')
+  const inputEl: ComponentRef<typeof ElInput> = await getComponentExpose('field1')
+  inputEl?.focus()
+  formItem?.validate('focus', (val: boolean) => {
+    console.log(val)
+  })
+}
 </script>
 
 <template>
@@ -270,6 +294,13 @@ const getDictOne = async () => {
     <ElButton @click="getDictOne">
       {{ t('searchDemo.dynamicOptions') }}
     </ElButton>
+
+    <ElButton @click="inoutFocus">
+      {{ `${t('formDemo.input')} ${t('formDemo.focus')}` }}
+    </ElButton>
+    <ElButton @click="inoutValidation">
+      {{ `${t('formDemo.input')} ${t('formDemo.formValidation')}` }}
+    </ElButton>
   </ContentWrap>
   <ContentWrap :title="`UseForm ${t('formDemo.example')}`">
     <Form :schema="schema" @register="register" />