element-plus: 二次封装el-form方案?

网上看了几篇文章,都不是想要的答案。于是给下自己的实现思路,各位有好的建议也可以评论。

建议直接看源码: https://github.com/jyj1202/j-admin/blob/dev/src/views/j-form/components/JForm.vue

如果觉得有用,可以去github给我的项目点个star。

核心思路就是利用vue的动态组件渲染需要的组件,而不是v-if判断。这样大大简化了代码。

模板部分代码:

<el-form-item
          :label="col.label"
          :prop="col.prop"
          :rules="col.rules"
        >
          <slot :name="col.prop" v-bind="{col, size: getProp('size', col)}">
            <component
              :is="getComponentType(col, getProp('type', col))"
              v-model="formData[col.prop]"
              v-bind="getComponentProps(col)"
              :key="col.prop"
              @change="handleFormItemValueChange"
            >
              <template v-if="!col.component && getSlotComponent(getProp('type', col))">
                <!-- 目前只有两种情况
                1.有dicData的formItem
                2.upload组件里的button
                -->
                <template v-if="col.dicData">
                  <component
                    v-for="dicData in col.dicData"
                    :key="dicData.value"
                    :is="getSlotComponent(getProp('type', col))"
                    v-bind="dicData"
                  />
                </template>
                <el-button
                  v-if="getSlotComponent(getProp('type', col))=='el-button'"
                  type="primary"
                >Click to upload
                </el-button>
              </template>
            </component>
          </slot>
        </el-form-item>

需要注意的是,涉及到有插槽的组件,我目前的思路是:

1.条件判断,渲染对应的插槽内组件。

/**
 * @description 获取组件插槽内子组件
 * @param type 
 */
const getSlotComponent = (type: string): string|undefined => {
  /* 插槽子组件映射 */
  const slotCompMap: Record<string, string> = {
    'select': 'option',
    'radio-group': 'radio',
    'checkbox-group': 'checkbox',
    'upload': 'button'
  }
  return `el-${slotCompMap[type]}`
}

2.二次封装该组件,用props配置项取代插槽,然后使用二次封装后的组件。

3.使用jsx写法,灵活的渲染插槽,但是需要用户传入插槽对应的jsx,提高了用户使用复杂度。

用户传入的表单配置数据解构:

const formOption = reactive<JFormOptionType>({
  labelPosition: 'left',
  column: [{
    label: 'input',
    prop: 'input',
    type: 'input',
    span: 8,
    placeholder: 'try to input something',
    rules: {
      required: true,
      message: "Please input something",
      trigger: "blur"
    }
  }, {
    label: 'input-number',
    prop: 'input-number',
    type: 'input-number',
    min: 1,
    max: 2,
    span: 8,
  }, {
    label: 'password',
    prop: 'password',
    type: 'password',
    showPassword: true,
    span: 8
  }, {
    label: 'select',
    prop: 'select',
    type: 'select',
    multiple: true,
    placeholder: 'make a selection',
    rules: [{
      required: true,
      message: "Please select something",
      trigger: "blur"
    }],
    dicData: [{
      label: 'select1',
      value: 0
    }, {
      label: 'select2',
      value: 1
    }]
  }, {
    label: 'switch',
    prop: 'switch',
    type: 'switch',
    inlinePrompt: true,
    activeIcon: "Check",
    inactiveIcon: "Close",
  }, {
    label: 'radio-group',
    prop: 'radioGroup',
    type: 'radio-group',
    rules: {
      required: true,
      message: "Please select something",
      trigger: "blur"
    },
    dicData: [{
      label: 'radio1',
      value: 0
    }, {
      label: 'radio2',
      value: 1
    }]
  }, {
    label: 'checkbox-group',
    prop: 'checkbox-group',
    type: 'checkbox-group',
    dicData: [{
      label: 'checkbox1',
      value: 0
    }, {
      label: 'checkbox2',
      value: 1
    }]
  }, {
    label: 'date-picker',
    prop: 'date-picker',
    type: 'date',
    rules: {
      required: true,
      message: "Please select date",
      trigger: "blur"
    },
  }, {
    label: 'rate',
    prop: 'rate',
    type: 'rate',
  }, {
    label: 'slider',
    prop: 'slider',
    type: 'slider',
    span: 24,
    formatTooltip: (val: number) => {
      return val / 100
    },
  }, {
    label: 'transfer',// FIXME:bug
    prop: 'transfer',
    type: 'transfer',
    span: 24,
    data: ref<{
      key: number
      label: string
      disabled: boolean
    }[]>([{
      key: 1,
      label: 'option 1',
      disabled: false
    }, {
      key: 2,
      label: 'option 2',
      disabled: true
    }, {
      key: 3,
      label: 'option 3',
      disabled: false
    }, ])
  }]
})