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

wip(VForm): VForm component development

陈凯龙 3 жил өмнө
parent
commit
d71bc5d6f5

+ 17 - 4
pnpm-lock.yaml

@@ -24,6 +24,7 @@ specifiers:
   less: ^4.1.2
   lint-staged: ^12.1.2
   lodash-es: ^4.17.21
+  normalize.css: ^8.0.1
   pinia: ^2.0.6
   postcss: ^8.4.4
   postcss-html: ^1.3.0
@@ -54,6 +55,7 @@ dependencies:
   '@vueuse/core': registry.npmmirror.com/@vueuse/core/7.1.2_vue@3.2.24
   element-plus: registry.npmmirror.com/element-plus/1.2.0-beta.6_vue@3.2.24
   lodash-es: registry.nlark.com/lodash-es/4.17.21
+  normalize.css: registry.nlark.com/normalize.css/8.0.1
   pinia: registry.npmmirror.com/pinia/2.0.6_typescript@4.5.2+vue@3.2.24
   vue: registry.npmmirror.com/vue/3.2.24
   vue-i18n: registry.npmmirror.com/vue-i18n/9.1.9_vue@3.2.24
@@ -3347,6 +3349,17 @@ packages:
     version: 0.2.0
     dev: true
 
+  registry.nlark.com/normalize.css/8.0.1:
+    resolution:
+      {
+        integrity: sha1-m5iiCHOLnMJjTKrLxC0THJdIe/M=,
+        registry: https://registry.npm.taobao.org/,
+        tarball: https://registry.nlark.com/normalize.css/download/normalize.css-8.0.1.tgz
+      }
+    name: normalize.css
+    version: 8.0.1
+    dev: false
+
   registry.nlark.com/nth-check/2.0.1:
     resolution:
       {
@@ -6961,7 +6974,7 @@ packages:
       {
         integrity: sha1-/q7SVZc9LndVW4PbwIhRpsY1IPo=,
         registry: https://registry.npm.taobao.org/,
-        tarball: https://registry.npmmirror.com/acorn/download/acorn-7.4.1.tgz?cache=0&sync_timestamp=1637226362293&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Facorn%2Fdownload%2Facorn-7.4.1.tgz
+        tarball: https://registry.npmmirror.com/acorn/download/acorn-7.4.1.tgz?cache=0&sync_timestamp=1637225943828&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Facorn%2Fdownload%2Facorn-7.4.1.tgz
       }
     name: acorn
     version: 7.4.1
@@ -6974,7 +6987,7 @@ packages:
       {
         integrity: sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw==,
         registry: https://registry.npm.taobao.org/,
-        tarball: https://registry.npmmirror.com/acorn/download/acorn-8.6.0.tgz?cache=0&sync_timestamp=1637226362293&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Facorn%2Fdownload%2Facorn-8.6.0.tgz
+        tarball: https://registry.npmmirror.com/acorn/download/acorn-8.6.0.tgz?cache=0&sync_timestamp=1637225943828&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Facorn%2Fdownload%2Facorn-8.6.0.tgz
       }
     name: acorn
     version: 8.6.0
@@ -7912,7 +7925,7 @@ packages:
       {
         integrity: sha1-MOvR73wv3/AcOk8VEESvJfqwUj4=,
         registry: https://registry.npm.taobao.org/,
-        tarball: https://registry.npmmirror.com/eslint-visitor-keys/download/eslint-visitor-keys-1.3.0.tgz?cache=0&sync_timestamp=1636378498011&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Feslint-visitor-keys%2Fdownload%2Feslint-visitor-keys-1.3.0.tgz
+        tarball: https://registry.npmmirror.com/eslint-visitor-keys/download/eslint-visitor-keys-1.3.0.tgz?cache=0&sync_timestamp=1636378941796&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Feslint-visitor-keys%2Fdownload%2Feslint-visitor-keys-1.3.0.tgz
       }
     name: eslint-visitor-keys
     version: 1.3.0
@@ -7924,7 +7937,7 @@ packages:
       {
         integrity: sha1-9lMoJZMFknOSyTjtROsKXJsr0wM=,
         registry: https://registry.npm.taobao.org/,
-        tarball: https://registry.npmmirror.com/eslint-visitor-keys/download/eslint-visitor-keys-2.1.0.tgz?cache=0&sync_timestamp=1636378498011&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Feslint-visitor-keys%2Fdownload%2Feslint-visitor-keys-2.1.0.tgz
+        tarball: https://registry.npmmirror.com/eslint-visitor-keys/download/eslint-visitor-keys-2.1.0.tgz?cache=0&sync_timestamp=1636378941796&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Feslint-visitor-keys%2Fdownload%2Feslint-visitor-keys-2.1.0.tgz
       }
     name: eslint-visitor-keys
     version: 2.1.0

+ 64 - 20
src/App.vue

@@ -1,42 +1,86 @@
 <script setup lang="ts">
-import { ref, onMounted, unref, reactive } from 'vue'
-import { ElConfigProvider } from 'element-plus'
+import { reactive } from 'vue'
+import { ElConfigProvider, ElIcon } from 'element-plus'
 import zhCn from 'element-plus/lib/locale/lang/zh-cn'
 // import en from 'element-plus/lib/locale/lang/en'
-import { VFrom, VFormExpose } from '@/components/Form'
-const formRef = ref<ComponentRef<typeof VFrom> & VFormExpose>()
-import { useI18n } from '@/hooks/web/useI18n'
-const { t } = useI18n()
+import { VFrom } from '@/components/Form'
+import Calendar from '~icons/ep/calendar'
 
-onMounted(() => {
-  const form = unref(formRef.value)
-  console.log(form)
-})
 const schema = reactive<VFormSchema[]>([
   {
     field: 'field1',
     component: 'Divider',
     componentProps: {
-      text: 'input示例'
+      text: 'Input'
     }
   },
   {
     field: 'field2',
-    label: '字段1',
+    label: 'default',
     component: 'Input'
+  },
+  {
+    field: 'field3',
+    label: 'input-icon1',
+    component: 'Input',
+    componentProps: {
+      suffixIcon: Calendar,
+      prefixIcon: Calendar
+    }
+  },
+  {
+    field: 'field4',
+    label: 'input-icon2',
+    component: 'Input',
+    componentProps: {
+      slots: {
+        suffix: true,
+        prefix: true
+      }
+    }
+  },
+  {
+    field: 'field5',
+    label: 'input-mixed',
+    component: 'Input',
+    componentProps: {
+      slots: {
+        prepend: true,
+        append: true
+      }
+    }
+  },
+  {
+    field: 'field6',
+    label: 'textarea',
+    component: 'Input',
+    componentProps: {
+      type: 'textarea',
+      rows: 1
+    }
+  },
+  {
+    field: 'field7',
+    component: 'Divider',
+    componentProps: {
+      text: 'Autocomplete'
+    }
   }
 ])
 </script>
 
 <template>
   <ElConfigProvider :locale="zhCn">
-    <!-- <VFrom ref="formRef" is-custom>
-      <template #default> hahahah </template>
-    </VFrom> -->
-    <VFrom :schema="schema" />
-    <!-- <VFrom :is-col="false" :schema="schema" /> -->
-    <!-- <Component :is="VFrom" /> -->
-    <!-- <RouterView class="app" /> -->
-    <div>{{ t('common.inputText') }}</div>
+    <VFrom :schema="schema">
+      <template #field4-prefix>
+        <ElIcon class="el-input__icon"><Calendar /></ElIcon>
+      </template>
+      <template #field4-suffix>
+        <ElIcon class="el-input__icon"><Calendar /></ElIcon>
+      </template>
+
+      <template #field5-prepend> Http:// </template>
+      <template #field5-append> .com </template>
+    </VFrom>
   </ElConfigProvider>
 </template>

+ 14 - 5
src/components/Form/src/VForm.vue

@@ -4,7 +4,7 @@ import { ElForm, ElFormItem, ElRow, ElCol } from 'element-plus'
 import { COMPONENT_MAP } from './componentMap'
 import { propTypes } from '@/utils/propTypes'
 import { getSlot } from '@/utils/tsxHelper'
-import { setTextPlaceholder, setGridProp } from './helper'
+import { setTextPlaceholder, setGridProp, setComponentProps, setItemComponentSlots } from './helper'
 
 export default defineComponent({
   name: 'VForm',
@@ -12,7 +12,7 @@ export default defineComponent({
     // 生成Form的布局结构数组
     schema: {
       type: Array as PropType<VFormSchema[]>,
-      require: true,
+      required: true,
       default: () => []
     },
     // 是否需要栅格布局
@@ -25,7 +25,9 @@ export default defineComponent({
     // 是否自动设置placeholder
     autoSetPlaceholder: propTypes.bool.def(true),
     // 是否自定义内容
-    isCustom: propTypes.bool.def(false)
+    isCustom: propTypes.bool.def(false),
+    // 表单label宽度
+    labelWidth: propTypes.oneOfType([String, Number]).def(130)
   },
   setup(props, { slots }) {
     const formRef = ref<ComponentRef<typeof ElForm>>()
@@ -81,8 +83,15 @@ export default defineComponent({
               typeof defineComponent
             >
             return (
-              <Com vModel={test.value} {...(autoSetPlaceholder && setTextPlaceholder(item))}>
-                {item.options ? renderOptions() : null}
+              <Com
+                vModel={test.value}
+                {...(autoSetPlaceholder && setTextPlaceholder(item))}
+                {...setComponentProps(item.componentProps)}
+              >
+                {{
+                  default: () => (item.options ? renderOptions() : null),
+                  ...setItemComponentSlots(slots, item?.componentProps?.slots, item.field)
+                }}
               </Com>
             )
           }}

+ 61 - 7
src/components/Form/src/helper.ts

@@ -1,5 +1,9 @@
 import { useI18n } from '@/hooks/web/useI18n'
 const { t } = useI18n()
+import { shallowRef } from 'vue'
+import { isFunction } from '@/utils/is'
+import { Slots } from 'vue'
+import { getSlot } from '@/utils/tsxHelper'
 
 interface PlaceholderMoel {
   placeholder?: string
@@ -52,14 +56,64 @@ export function setTextPlaceholder(schema: VFormSchema): PlaceholderMoel {
  */
 export function setGridProp(col: ColProps = {}): ColProps {
   const colProps: ColProps = {
-    ...{
-      xs: 24,
-      sm: 12,
-      md: 12,
-      lg: 12,
-      xl: 8
-    },
+    // 如果有span,代表用户优先级更高,所以不需要默认栅格
+    ...(col.span
+      ? {}
+      : {
+          xs: 24,
+          sm: 12,
+          md: 12,
+          lg: 12,
+          xl: 8
+        }),
     ...col
   }
   return colProps
 }
+
+type ComponentPropsModel = {
+  clearable: boolean
+} & Recordable
+
+/**
+ *
+ * @param props 传入的组件属性
+ * @returns 默认添加 clearable 属性
+ */
+export function setComponentProps(props: Recordable = {}): ComponentPropsModel {
+  for (const key in props) {
+    // 如果传入的是组件,需要让其失去响应式,避免不必要的性能开销
+    // 这样判断好像还不太合理。后续看看没有更合理的判断方法
+    if (props[key]?.render && isFunction(props[key]?.render)) {
+      props[key] = shallowRef(props[key]?.render())
+    }
+  }
+  const componentProps: ComponentPropsModel = {
+    clearable: true,
+    ...props
+  }
+  return componentProps
+}
+
+/**
+ *
+ * @param slots 插槽
+ * @param slotsProps 插槽属性
+ * @param field 字段名
+ */
+export function setItemComponentSlots(
+  slots: Slots,
+  slotsProps: Recordable = {},
+  field: string
+): Recordable {
+  const slotObj: Recordable = {}
+  for (const key in slotsProps) {
+    if (slotsProps[key]) {
+      // 由于组件有可能重复,需要有一个唯一的前缀
+      slotObj[key] = () => {
+        return getSlot(slots, `${field}-${key}`)
+      }
+    }
+  }
+  return slotObj
+}