element-plus-dynamic-form
GitHub
GitHub
  • 首页
  • 快速开始
  • FormRender
  • BaseTable

基础用法

formItems 建议使用 computed 属性,当表单项属性发生变化时,便于及时更新表单配置项。

示例
代码
<template>
  <form-render :form-items="formItems" :form-data="formData" ref="formRenderRef" />
  <div>
    <el-button type="primary" @click="handleSubmit">提交</el-button>
    <el-button type="primary" @click="handleReset">重置</el-button>
  </div>
</template>

<script setup lang="tsx">
import { computed, ref } from 'vue'
import { ElForm, ElMessage } from 'element-plus'

const ageLabel = ref('年龄')
const ageRequired = ref(true)
const formRenderRef = ref<InstanceType<typeof ElForm>>()
const formItems = computed(() => [
  {
    renderType: 'el-input',
    itemProps: {
      prop: 'name',
      label: '姓名',
      rules: [
        {
          required: true,
          message: '请输入姓名',
          trigger: ['blur']
        }
      ]
    },
    comProps: {
      placeholder: '请输入姓名'
    },
    comEvents: {
      onBlur: () => {
        if (!formData.value.name) {
          ElMessage.error('请输入姓名')
        }
      }
    }
  },
  {
    renderType: 'el-input-number',
    itemProps: {
      prop: 'age',
      label: ageLabel.value,
      rules: [
        {
          required: ageRequired.value,
          message: '请输入年龄',
          trigger: ['blur']
        }
      ]
    },
    comProps: {
      placeholder: '请输入年龄',
      style: {
        width: '100%'
      }
    }
  }
])

const formData = ref({
  name: '',
  age: 0
})

const handleSubmit = () => {
  formRenderRef.value?.validate((valid) => {
    if (valid) {
      ElMessage.success('提交成功')
    } else {
      ElMessage.error('提交失败')
    }
  })
}

const handleReset = () => {
  formRenderRef.value?.resetFields()
}
</script>

自定义组件

renderType 为自定义组件时,需要提前全局或局部注册该自定义组件。

示例
代码
<template>
  <form-render :form-items="formItems" :form-data="formData" ref="formRenderRef" />
  <div>
    <el-button type="primary" @click="handleSubmit">提交</el-button>
    <el-button type="primary" @click="handleReset">重置</el-button>
  </div>
</template>

<script setup lang="tsx">
import { computed, ref } from 'vue'
import { ElForm, ElMessage } from 'element-plus'

const formRenderRef = ref<InstanceType<typeof ElForm>>()
const formItems = computed(() => [
  {
    renderType: 'el-input',
    itemProps: {
      prop: 'name',
      label: '姓名',
      rules: [
        {
          required: true,
          message: '请输入姓名',
          trigger: ['blur']
        }
      ]
    },
    comProps: {
      placeholder: '请输入姓名'
    }
  },
  {
    renderType: 'custom-select',
    itemProps: {
      prop: 'sex',
      label: '性别',
      rules: [
        {
          required: true,
          message: '请选择性别',
          trigger: ['change']
        }
      ]
    },
    comProps: {
      placeholder: '请选择性别',
      options: [
        {
          value: 'male',
          label: '男'
        },
        {
          value: 'female',
          label: '女'
        }
      ]
    }
  }
])

const formData = ref({
  name: '',
  sex: ''
})

const handleSubmit = () => {
  formRenderRef.value?.validate((valid) => {
    if (valid) {
      ElMessage.success('提交成功')
    } else {
      ElMessage.error('提交失败')
    }
  })
}

const handleReset = () => {
  formRenderRef.value?.resetFields()
}
</script>

CustomSelect.vue

代码
<template>
  <el-select v-model="selectedValue" placeholder="请选择">
    <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </el-option>
  </el-select>
</template>

<script setup>
import { ref, defineProps } from 'vue'

const props = defineProps({
  options: {
    type: Array,
    default: () => [],
    validator: (value) => {
      return value.every((item) => item.hasOwnProperty('value') && item.hasOwnProperty('label'))
    }
  }
})

const selectedValue = ref('')
</script>

事件

Form 事件

给 form-render 组件增加 ref 属性,用于获取表单实例,调用表单方法。具体方法名参考 Form Exposes。

示例
代码
<template>
  <form-render :form-items="formItems" :form-data="formData" ref="formRenderRef" />
  <div>
    <el-button type="primary" @click="handleSubmit">提交</el-button>
    <el-button type="primary" @click="handleReset">重置</el-button>
  </div>
</template>

<script setup lang="tsx">
import { computed, ref } from 'vue'
import { ElForm, ElMessage } from 'element-plus'

const ageLabel = ref('年龄')
const ageRequired = ref(true)
const formRenderRef = ref<InstanceType<typeof ElForm>>()
const formItems = computed(() => [
  {
    renderType: 'el-input',
    itemProps: {
      prop: 'name',
      label: '姓名',
      rules: [
        {
          required: true,
          message: '请输入姓名',
          trigger: ['blur']
        }
      ]
    },
    comProps: {
      placeholder: '请输入姓名'
    },
    comEvents: {
      onBlur: () => {
        if (!formData.value.name) {
          ElMessage.error('请输入姓名')
        }
      }
    }
  },
  {
    renderType: 'el-input-number',
    itemProps: {
      prop: 'age',
      label: ageLabel.value,
      rules: [
        {
          required: ageRequired.value,
          message: '请输入年龄',
          trigger: ['blur']
        }
      ]
    },
    comProps: {
      placeholder: '请输入年龄',
      style: {
        width: '100%'
      }
    }
  }
])

const formData = ref({
  name: '',
  age: 0
})

const handleSubmit = () => {
  formRenderRef.value?.validate((valid) => {
    if (valid) {
      ElMessage.success('提交成功')
    } else {
      ElMessage.error('提交失败')
    }
  })
}

const handleReset = () => {
  formRenderRef.value?.resetFields()
}
</script>

FormItem 事件

获取表单实例后,调用 getField 方法获取表单项实例,调用表单项方法。具体方法名参考 FormItem Exposes。

示例
代码
<template>
  <form-render :form-items="formItems" :form-data="formData" ref="formRenderRef" />
  <div>
    <el-button type="primary" @click="handleSubmit">提交</el-button>
    <el-button type="primary" @click="handleClearAgeValidate">清空年龄字段校验</el-button>
  </div>
</template>

<script setup lang="tsx">
import { computed, ref } from 'vue'
import { ElForm, ElMessage } from 'element-plus'

const ageLabel = ref('年龄')
const ageRequired = ref(true)
const formRenderRef = ref<InstanceType<typeof ElForm>>()
const formItems = computed(() => [
  {
    renderType: 'el-input',
    itemProps: {
      prop: 'name',
      label: '姓名',
      rules: [
        {
          required: true,
          message: '请输入姓名',
          trigger: ['blur']
        }
      ]
    },
    comProps: {
      placeholder: '请输入姓名'
    },
    comEvents: {
      onBlur: () => {
        if (!formData.value.name) {
          ElMessage.error('请输入姓名')
        }
      }
    }
  },
  {
    renderType: 'el-input-number',
    itemProps: {
      prop: 'age',
      label: ageLabel.value,
      rules: [
        {
          required: ageRequired.value,
          message: '请输入年龄',
          trigger: ['blur', 'change']
        }
      ]
    },
    comProps: {
      placeholder: '请输入年龄',
      style: {
        width: '100%'
      }
    }
  }
])

const formData = ref({
  name: '',
  age: 0
})

const handleSubmit = () => {
  formRenderRef.value?.validate((valid) => {
    if (valid) {
      ElMessage.success('提交成功')
    } else {
      ElMessage.error('提交失败')
    }
  })
}

const handleClearAgeValidate = () => {
  const ageFieldRef = formRenderRef.value?.getField('age')
  ageFieldRef?.clearValidate()
}
</script>

插槽

template 插槽

renderType 设置为 slot ,在 template 标签里编写插槽内容

示例
代码
<template>
  <form-render :form-items="formItems" :form-data="formData" ref="formRenderRef">
    <template #image>
      <el-upload
        v-model:file-list="formData.image"
        action="https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15"
        multiple
        :on-preview="handlePreview"
        :on-remove="handleRemove"
        :before-remove="beforeRemove"
        :limit="3"
        :on-exceed="handleExceed"
      >
        <el-button type="primary">上传图片</el-button>
        <template #tip>
          <div class="el-upload__tip">jpg/png files with a size less than 500KB.</div>
        </template>
      </el-upload>
    </template>
  </form-render>
  <div>
    <el-button type="primary" @click="handleSubmit">提交</el-button>
    <el-button type="primary" @click="handleReset">重置</el-button>
  </div>
</template>

<script setup lang="tsx">
import { computed, ref } from 'vue'
import { ElForm, ElMessage, ElMessageBox } from 'element-plus'

const formRenderRef = ref<InstanceType<typeof ElForm>>()
const formItems = computed(() => [
  {
    renderType: 'el-input',
    itemProps: {
      prop: 'name',
      label: '姓名',
      rules: [
        {
          required: true,
          message: '请输入姓名',
          trigger: ['blur']
        }
      ]
    },
    comProps: {
      placeholder: '请输入姓名'
    }
  },
  {
    renderType: 'custom-select',
    itemProps: {
      prop: 'sex',
      label: '性别',
      rules: [
        {
          required: true,
          message: '请选择性别',
          trigger: ['change']
        }
      ]
    },
    comProps: {
      placeholder: '请选择性别',
      options: [
        {
          value: 'male',
          label: '男'
        },
        {
          value: 'female',
          label: '女'
        }
      ]
    }
  },
  {
    renderType: 'slot',
    itemProps: {
      prop: 'image',
      label: '头像',
      rules: [
        {
          required: true,
          message: '请上传头像',
          trigger: ['change']
        }
      ]
    }
  }
])

const formData = ref({
  name: '',
  sex: '',
  image: [
    {
      name: 'element-plus-logo.svg',
      url: 'https://element-plus.org/images/element-plus-logo.svg'
    },
    {
      name: 'element-plus-logo2.svg',
      url: 'https://element-plus.org/images/element-plus-logo.svg'
    }
  ]
})

const handleRemove: UploadProps['onRemove'] = (file, uploadFiles) => {
  console.log(file, uploadFiles)
}

const handlePreview: UploadProps['onPreview'] = (uploadFile) => {
  console.log(uploadFile)
}

const handleExceed: UploadProps['onExceed'] = (files, uploadFiles) => {
  ElMessage.warning(
    `The limit is 3, you selected ${files.length} files this time, add up to ${
      files.length + uploadFiles.length
    } totally`
  )
}

const beforeRemove: UploadProps['beforeRemove'] = (uploadFile, uploadFiles) => {
  return ElMessageBox.confirm(`Cancel the transfer of ${uploadFile.name} ?`).then(
    () => true,
    () => false
  )
}

const handleSubmit = () => {
  formRenderRef.value?.validate((valid) => {
    if (valid) {
      ElMessage.success('提交成功')
    } else {
      ElMessage.error('提交失败')
    }
  })
}

const handleReset = () => {
  formRenderRef.value?.resetFields()
}
</script>

jsx 插槽

renderType 正常设置,comProps 中设置 slots 属性,值为插槽名称。

示例
代码
<template>
  <form-render :form-items="formItems" :form-data="formData" ref="formRenderRef" />
  <div>
    <el-button type="primary" @click="handleSubmit">提交</el-button>
    <el-button type="primary" @click="handleReset">重置</el-button>
  </div>
</template>

<script setup lang="tsx">
import { computed, ref } from 'vue'
import { ElForm, ElMessage, ElMessageBox } from 'element-plus'

const formRenderRef = ref<InstanceType<typeof ElForm>>()
const formItems = computed(() => [
  {
    renderType: 'el-input',
    itemProps: {
      prop: 'name',
      label: '姓名',
      rules: [
        {
          required: true,
          message: '请输入姓名',
          trigger: ['blur']
        }
      ]
    },
    comProps: {
      placeholder: '请输入姓名'
    }
  },
  {
    renderType: 'custom-select',
    itemProps: {
      prop: 'sex',
      label: '性别',
      rules: [
        {
          required: true,
          message: '请选择性别',
          trigger: ['change']
        }
      ]
    },
    comProps: {
      placeholder: '请选择性别',
      options: [
        {
          value: 'male',
          label: '男'
        },
        {
          value: 'female',
          label: '女'
        }
      ]
    }
  },
  {
    renderType: 'el-upload',
    itemProps: {
      prop: 'image',
      label: '头像',
      rules: [
        {
          required: true,
          message: '请上传头像',
          trigger: ['change']
        }
      ]
    },
    comProps: {
      action: 'https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15',
      multiple: true,
      modelName: 'fileList',
      fileList: formData.value.image,
      onPreview: handlePreview,
      onRemove: handleRemove,
      beforeRemove: beforeRemove,
      onExceed: handleExceed,
      limit: 3,
      slots: {
        default: () => (
          <>
            <el-button type="primary">{imageText.value}</el-button>
          </>
        )
      }
    }
  }
])

const formData = ref({
  name: '',
  sex: '',
  image: [
    {
      name: 'element-plus-logo.svg',
      url: 'https://element-plus.org/images/element-plus-logo.svg'
    },
    {
      name: 'element-plus-logo2.svg',
      url: 'https://element-plus.org/images/element-plus-logo.svg'
    }
  ]
})

const imageText = ref('上传头像')

const handleRemove: UploadProps['onRemove'] = (file, uploadFiles) => {
  console.log(file, uploadFiles)
}

const handlePreview: UploadProps['onPreview'] = (uploadFile) => {
  console.log(uploadFile)
}

const handleExceed: UploadProps['onExceed'] = (files, uploadFiles) => {
  ElMessage.warning(
    `The limit is 3, you selected ${files.length} files this time, add up to ${
      files.length + uploadFiles.length
    } totally`
  )
}

const beforeRemove: UploadProps['beforeRemove'] = (uploadFile, uploadFiles) => {
  return ElMessageBox.confirm(`Cancel the transfer of ${uploadFile.name} ?`).then(
    () => true,
    () => false
  )
}

const handleSubmit = () => {
  formRenderRef.value?.validate((valid) => {
    if (valid) {
      ElMessage.success('提交成功')
    } else {
      ElMessage.error('提交失败')
    }
  })
}

const handleReset = () => {
  formRenderRef.value?.resetFields()
}
</script>

使用 jsx 插槽时,需安装 @vitejs/plugin-vue-jsx 插件,script 标签需设置 lang=tsx

API

Form 属性

属性释义说明
formItems表单配置项默认 []
formData表单数据默认 {}
baseColSpan基础列宽度默认 24

其他属性参考 el-form

FormItem 属性

属性释义说明
renderType表单项组件类型可以为 element-plus 组件,如(el-input,el-select 等),也可以为自定义组件
visible表单项是否显示默认 true
colPropsel-col 属性参考 el-col
itemPropsel-form-item 属性参考 el-form-item
comProps组件属性参考 el-input、el-select 或自定义组件等自身属性
comEvents组件事件参考 el-input、el-select 或自定义组件等自身事件,如 onBlur,onChange 等
Last Updated:: 10/21/25, 8:10 AM
Contributors: CJ
Prev
快速开始
Next
BaseTable