vue3使用h函数封装form表单

151 阅读2分钟

该组件目前只封装了input 和select,其他类型依照element ui中的form表单格式封装

组件封装


<!--动态form组件 DynamicForm.vue-->
<template>

</template>

<script lang="ts">
import {defineComponent, PropType, ref, h} from 'vue'
import type {FormConfigFace, FormButtonFace, SelectOptionFace} from "@/types/form.d.ts";
import {ElButton, ElForm, ElFormItem, ElInput, ElOption, ElSelect, FormInstance} from "element-plus";
export default defineComponent({
  name: 'DynamicForm',
  props: {
    // el-form的属性配置,如label-width等
    attrsConfig: {
      type: Object,
      default: () => ({})
    },
    // el-form-item的属性配置,具体的参数为FormConfigFace
    config: {
      type: Array as PropType<FormConfigFace[]>, // 使用 PropType 来指定 config 为 FormConfigFace 类型的数组
      require: true
    },
    // 表单的验证按钮,参数为FormButtonFace
    btnConfig: {
     type: Array as PropType<FormButtonFace[]>,
      default: () => ([])
    },
    // el-form的model数,需要双向绑定
    modelValue: {
      type: Object,
      default: () => ({})
    }
  },
  emits:['update:modelValue', 'submit', 'close'],
  setup(props, { emit }) {
    const formData = ref({ ...props.modelValue })

    // 动态生成表单
    const renderFormItems = () => {
      return [
          h('div', {class: 'dynamic-form'}, props.config.map((item: FormConfigFace) => {
            // 通用属性的配置
            const commonProps = {
              ...item.attrs,
              modelValue: formData.value[item.prop],
              'onUpdate:modelValue': (val: any) => {
                formData.value[item.prop] = val
                emit('update:modelValue', formData.value)
              },
            }
            // 根据不同类型渲染不同组件
            let formComponent: any;
            switch (item.type) {
              case "input":
                formComponent = h(ElInput, commonProps);
                break;
              case "select":
                formComponent = h(ElSelect, commonProps, () => {
                  item.options?.map((opt:SelectOptionFace) => h(ElOption, {
                    label: opt.label,
                    value: opt.value
                  }))
                });
                break;
                // 这里设置其他类型
            }

            return h(ElFormItem, {
              prop: item.prop,
              label: item.label,
              rules: item.rules
            }, () => formComponent)
          })),

          props.btnConfig.map((item: FormButtonFace) => {
            const btnComponent = h(ElButton, {...item.attrs, onClick: () => {
              if(item.type === 1) {
                emit('submit', {formData: formData.value})
              }
              if(item.type === 2) {
                emit('close')
              }
              }}, item.name)
            return h(ElFormItem, {class: 'dynamic-form-btn'}, () => btnComponent)
          })
      ]
    }
    return () => h(ElForm, { ...props.attrsConfig, model: formData.value }, renderFormItems)
  }
})

</script>

<style scoped lang="scss">

</style>

类型声明

// src/types/form.d.ts
// 下拉选择的选项类型
export declare interface SelectOptionFace {
  label: string,
  value: any
}
// 表单组件类型
export declare interface FormConfigFace {
  // 类型--暂定input|select|button
  type: 'input' | 'select',
  // 属性值
  prop: string,
  // 标签文本
  label: string,
  // 下拉选择的属性值
  options?: SelectOptionFace[];
  // 规则验证
  rules?: any[],
  // 原生属性(如placeholder)
  attrs?: Record<string, any>;
}

export declare interface FormButtonFace {
  // 类型1--提交| 取消
  type: 1 | 2,
  // 名字
  name: string,
  // 原生属性(如placeholder)
  attrs?: Record<string, any>;
}

组件的使用

<template>
  <DynamicForm
    :attrsConfig="attrsConfig"
    :config="formConfig"
    :btn-config="btnConfig"
    :model-value="formData"
    @submit="submitHandle"
  ></DynamicForm>
</template>

<script lang="ts" setup>
import DynamicForm from '@/components/common/DynamicForm.vue';
import { ref } from 'vue';
import type { FormConfigFace, FormButtonFace } from '@/types/form.d.ts';

const formData = ref({ 
  username: '',
  password: '',
});

const formConfig = ref<FormConfigFace[]>([
  {
    type: 'input',
    prop: 'username',
    label: '用户名',
    rules: [{ required: true, message: '请输入用户名' }],
  },
  {
    type: 'input',
    prop: 'password',
    label: '密码',
    rules: [{ required: true, message: '请输入密码' }],
    attrs: { type: 'password', showPassword: true },
  },
]);

const btnConfig = ref<FormButtonFace[]>([
  {
    type: 1,
    name: '登录',
    attrs: { type: 'primary', style: { width: '60%' } },
  },
]);

const attrsConfig = ref({
  labelWidth: 'auto',
  labelPosition: 'left',
});

const submitHandle = (data: any) => {
  console.log('data', data);
};
</script>

<style scoped lang="scss"></style>