From a1d7e81859f554f3a53680cc35f0f49bf1f77098 Mon Sep 17 00:00:00 2001
From: wwf <1971391498@qq.com>
Date: 星期四, 14 五月 2026 14:37:02 +0800
Subject: [PATCH] 导入项目

---
 src/components/Form/src/Form.vue |  307 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 307 insertions(+), 0 deletions(-)

diff --git a/src/components/Form/src/Form.vue b/src/components/Form/src/Form.vue
new file mode 100644
index 0000000..3acc10a
--- /dev/null
+++ b/src/components/Form/src/Form.vue
@@ -0,0 +1,307 @@
+<script lang="tsx">
+import { computed, defineComponent, onMounted, PropType, ref, unref, watch } from 'vue'
+import { ElCol, ElForm, ElFormItem, ElRow, ElTooltip } from 'element-plus'
+import { componentMap } from './componentMap'
+import { propTypes } from '@/utils/propTypes'
+import { getSlot } from '@/utils/tsxHelper'
+import {
+  initModel,
+  setComponentProps,
+  setFormItemSlots,
+  setGridProp,
+  setItemComponentSlots,
+  setTextPlaceholder
+} from './helper'
+import { useRenderSelect } from './components/useRenderSelect'
+import { useRenderRadio } from './components/useRenderRadio'
+import { useRenderCheckbox } from './components/useRenderCheckbox'
+import { useDesign } from '@/hooks/web/useDesign'
+import { findIndex } from '@/utils'
+import { set } from 'lodash-es'
+import { FormProps } from './types'
+import { Icon } from '@/components/Icon'
+import { FormSchema, FormSetPropsType } from '@/types/form'
+
+const { getPrefixCls } = useDesign()
+
+const prefixCls = getPrefixCls('form')
+
+export default defineComponent({
+  // eslint-disable-next-line vue/no-reserved-component-names
+  name: 'Form',
+  props: {
+    // 鐢熸垚Form鐨勫竷灞�缁撴瀯鏁扮粍
+    schema: {
+      type: Array as PropType<FormSchema[]>,
+      default: () => []
+    },
+    // 鏄惁闇�瑕佹爡鏍煎竷灞�
+    // update by 鑺嬭壙锛氬皢 true 鏀规垚 false锛屽洜涓洪」鐩洿甯哥敤杩欑鏂瑰紡
+    isCol: propTypes.bool.def(false),
+    // 琛ㄥ崟鏁版嵁瀵硅薄
+    model: {
+      type: Object as PropType<Recordable>,
+      default: () => ({})
+    },
+    // 鏄惁鑷姩璁剧疆placeholder
+    autoSetPlaceholder: propTypes.bool.def(true),
+    // 鏄惁鑷畾涔夊唴瀹�
+    isCustom: propTypes.bool.def(false),
+    // 琛ㄥ崟label瀹藉害
+    labelWidth: propTypes.oneOfType([String, Number]).def('auto'),
+    // 鏄惁 loading 鏁版嵁涓� add by 鑺嬭壙
+    vLoading: propTypes.bool.def(false)
+  },
+  emits: ['register'],
+  setup(props, { slots, expose, emit }) {
+    // element form 瀹炰緥
+    const elFormRef = ref<ComponentRef<typeof ElForm>>()
+
+    // useForm浼犲叆鐨刾rops
+    const outsideProps = ref<FormProps>({})
+
+    const mergeProps = ref<FormProps>({})
+
+    const getProps = computed(() => {
+      const propsObj = { ...props }
+      Object.assign(propsObj, unref(mergeProps))
+      return propsObj
+    })
+
+    // 琛ㄥ崟鏁版嵁
+    const formModel = ref<Recordable>({})
+
+    onMounted(() => {
+      emit('register', unref(elFormRef)?.$parent, unref(elFormRef))
+    })
+
+    // 瀵硅〃鍗曡祴鍊�
+    const setValues = (data: Recordable = {}) => {
+      formModel.value = Object.assign(unref(formModel), data)
+    }
+
+    const setProps = (props: FormProps = {}) => {
+      mergeProps.value = Object.assign(unref(mergeProps), props)
+      outsideProps.value = props
+    }
+
+    const delSchema = (field: string) => {
+      const { schema } = unref(getProps)
+
+      const index = findIndex(schema, (v: FormSchema) => v.field === field)
+      if (index > -1) {
+        schema.splice(index, 1)
+      }
+    }
+
+    const addSchema = (formSchema: FormSchema, index?: number) => {
+      const { schema } = unref(getProps)
+      if (index !== void 0) {
+        schema.splice(index, 0, formSchema)
+        return
+      }
+      schema.push(formSchema)
+    }
+
+    const setSchema = (schemaProps: FormSetPropsType[]) => {
+      const { schema } = unref(getProps)
+      for (const v of schema) {
+        for (const item of schemaProps) {
+          if (v.field === item.field) {
+            set(v, item.path, item.value)
+          }
+        }
+      }
+    }
+
+    const getElFormRef = (): ComponentRef<typeof ElForm> => {
+      return unref(elFormRef) as ComponentRef<typeof ElForm>
+    }
+
+    expose({
+      setValues,
+      formModel,
+      setProps,
+      delSchema,
+      addSchema,
+      setSchema,
+      getElFormRef
+    })
+
+    // 鐩戝惉琛ㄥ崟缁撴瀯鍖栨暟缁勶紝閲嶆柊鐢熸垚formModel
+    watch(
+      () => unref(getProps).schema,
+      (schema = []) => {
+        formModel.value = initModel(schema, unref(formModel))
+      },
+      {
+        immediate: true,
+        deep: true
+      }
+    )
+
+    // 娓叉煋鍖呰9鏍囩锛屾槸鍚︿娇鐢ㄦ爡鏍煎竷灞�
+    const renderWrap = () => {
+      const { isCol } = unref(getProps)
+      const content = isCol ? (
+        <ElRow gutter={20}>{renderFormItemWrap()}</ElRow>
+      ) : (
+        renderFormItemWrap()
+      )
+      return content
+    }
+
+    // 鏄惁瑕佹覆鏌揺l-col
+    const renderFormItemWrap = () => {
+      // hidden灞炴�ц〃绀洪殣钘忥紝涓嶅仛娓叉煋
+      const { schema = [], isCol } = unref(getProps)
+
+      return schema
+        .filter((v) => !v.hidden)
+        .map((item) => {
+          // 濡傛灉鏄� Divider 缁勪欢锛岄渶瑕佽嚜宸卞崰鐢ㄤ竴琛�
+          const isDivider = item.component === 'Divider'
+          const Com = componentMap['Divider'] as ReturnType<typeof defineComponent>
+          return isDivider ? (
+            <Com {...{ contentPosition: 'left', ...item.componentProps }}>{item?.label}</Com>
+          ) : isCol ? (
+            // 濡傛灉闇�瑕佹爡鏍硷紝闇�瑕佸寘瑁� ElCol
+            <ElCol {...setGridProp(item.colProps)}>{renderFormItem(item)}</ElCol>
+          ) : (
+            renderFormItem(item)
+          )
+        })
+    }
+
+    // 娓叉煋formItem
+    const renderFormItem = (item: FormSchema) => {
+      // 鍗曠嫭缁欏彧鏈塷ptions灞炴�х殑缁勪欢鍋氬垽鏂�
+      const notRenderOptions = ['SelectV2', 'Cascader', 'Transfer']
+      const slotsMap: Recordable = {
+        ...setItemComponentSlots(slots, item?.componentProps?.slots, item.field)
+      }
+      if (
+        item?.component !== 'SelectV2' &&
+        item?.component !== 'Cascader' &&
+        item?.componentProps?.options
+      ) {
+        slotsMap.default = () => renderOptions(item)
+      }
+
+      const formItemSlots: Recordable = setFormItemSlots(slots, item.field)
+      // 濡傛灉鏈� labelMessage锛岃嚜鍔ㄤ娇鐢ㄦ彃妲芥覆鏌�
+      if (item?.labelMessage) {
+        formItemSlots.label = () => {
+          return (
+            <>
+              <span>{item.label}</span>
+              <ElTooltip placement="right" raw-content>
+                {{
+                  content: () => <span v-dompurify-html={item.labelMessage}></span>,
+                  default: () => (
+                    <Icon
+                      icon="ep:warning"
+                      size={16}
+                      color="var(--el-color-primary)"
+                      class="relative top-1px ml-2px"
+                    ></Icon>
+                  )
+                }}
+              </ElTooltip>
+            </>
+          )
+        }
+      }
+      return (
+        <ElFormItem {...(item.formItemProps || {})} prop={item.field} label={item.label || ''}>
+          {{
+            ...formItemSlots,
+            default: () => {
+              const Com = componentMap[item.component as string] as ReturnType<
+                typeof defineComponent
+              >
+
+              const { autoSetPlaceholder } = unref(getProps)
+
+              return slots[item.field] ? (
+                getSlot(slots, item.field, formModel.value)
+              ) : (
+                <Com
+                  vModel={formModel.value[item.field]}
+                  {...(autoSetPlaceholder && setTextPlaceholder(item))}
+                  {...setComponentProps(item)}
+                  style={item.componentProps?.style}
+                  {...(notRenderOptions.includes(item?.component as string) &&
+                  item?.componentProps?.options
+                    ? { options: item?.componentProps?.options || [] }
+                    : {})}
+                >
+                  {{ ...slotsMap }}
+                </Com>
+              )
+            }
+          }}
+        </ElFormItem>
+      )
+    }
+
+    // 娓叉煋options
+    const renderOptions = (item: FormSchema) => {
+      switch (item.component) {
+        case 'Select':
+        case 'SelectV2':
+          const { renderSelectOptions } = useRenderSelect(slots)
+          return renderSelectOptions(item)
+        case 'Radio':
+        case 'RadioButton':
+          const { renderRadioOptions } = useRenderRadio()
+          return renderRadioOptions(item)
+        case 'Checkbox':
+        case 'CheckboxButton':
+          const { renderCheckboxOptions } = useRenderCheckbox()
+          return renderCheckboxOptions(item)
+        default:
+          break
+      }
+    }
+
+    // 杩囨护浼犲叆Form缁勪欢鐨勫睘鎬�
+    const getFormBindValue = () => {
+      // 閬垮厤鍦ㄦ爣绛句笂鍑虹幇澶氫綑鐨勫睘鎬�
+      const delKeys = ['schema', 'isCol', 'autoSetPlaceholder', 'isCustom', 'model']
+      const props = { ...unref(getProps) }
+      for (const key in props) {
+        if (delKeys.indexOf(key) !== -1) {
+          delete props[key]
+        }
+      }
+      return props
+    }
+
+    return () => (
+      <ElForm
+        ref={elFormRef}
+        {...getFormBindValue()}
+        model={props.isCustom ? props.model : formModel}
+        class={prefixCls}
+        v-loading={props.vLoading}
+      >
+        {{
+          // 濡傛灉闇�瑕佽嚜瀹氫箟锛屽氨浠�涔堥兘涓嶆覆鏌擄紝鑰屾槸鎻愪緵榛樿鎻掓Ы
+          default: () => {
+            const { isCustom } = unref(getProps)
+            return isCustom ? getSlot(slots, 'default') : renderWrap()
+          }
+        }}
+      </ElForm>
+    )
+  }
+})
+</script>
+
+<style lang="scss" scoped>
+.#{$elNamespace}-form.#{$namespace}-form .#{$elNamespace}-row {
+  margin-right: 0 !important;
+  margin-left: 0 !important;
+}
+</style>

--
Gitblit v1.8.0