完整代码
javascript
<template>
<div class="dynamic-form-container">
<el-form
ref="dynamicFormRef"
:model="formData"
:rules="formRules"
label-width="auto"
label-position="top"
v-loading="loading"
>
<!-- 动态渲染表单字段 -->
<template v-for="field in formConfig" :key="field.name">
<!-- 输入框 -->
<el-form-item
v-if="field.type === 'input'"
:label="field.label"
:prop="field.name"
:rules="generateFieldRules(field)"
>
<el-input
v-model="formData[field.name]"
:placeholder="field.placeholder || `请输入${field.label}`"
:type="field.inputType || 'text'"
:clearable="field.clearable !== false"
/>
</el-form-item>
<!-- 下拉选择 -->
<el-form-item
v-else-if="field.type === 'select'"
:label="field.label"
:prop="field.name"
:rules="generateFieldRules(field)"
>
<el-select
v-model="formData[field.name]"
:placeholder="field.placeholder || `请选择${field.label}`"
:clearable="field.clearable !== false"
style="width: 100%"
>
<el-option
v-for="option in field.options"
:key="option.value"
:label="option.label"
:value="option.value"
/>
</el-select>
</el-form-item>
<!-- 单选框 -->
<el-form-item
v-else-if="field.type === 'radio'"
:label="field.label"
:prop="field.name"
:rules="generateFieldRules(field)"
>
<el-radio-group v-model="formData[field.name]">
<el-radio
v-for="option in field.options"
:key="option.value"
:label="option.value"
>
{{ option.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<!-- 复选框 -->
<el-form-item
v-else-if="field.type === 'checkbox'"
:label="field.label"
:prop="field.name"
:rules="generateFieldRules(field)"
>
<el-checkbox-group v-model="formData[field.name]">
<el-checkbox
v-for="option in field.options"
:key="option.value"
:label="option.value"
>
{{ option.label }}
</el-checkbox>
</el-checkbox-group>
</el-form-item>
<!-- 日期选择器 -->
<el-form-item
v-else-if="field.type === 'date'"
:label="field.label"
:prop="field.name"
:rules="generateFieldRules(field)"
>
<el-date-picker
v-model="formData[field.name]"
:type="field.dateType || 'date'"
:placeholder="field.placeholder || `请选择${field.label}`"
style="width: 100%"
/>
</el-form-item>
<!-- 开关 -->
<el-form-item
v-else-if="field.type === 'switch'"
:label="field.label"
:prop="field.name"
>
<el-switch v-model="formData[field.name]" />
</el-form-item>
<!-- 自定义插槽 -->
<el-form-item
v-else-if="field.type === 'slot'"
:label="field.label"
:prop="field.name"
:rules="generateFieldRules(field)"
>
<slot :name="field.slotName" :field="field" :model="formData" />
</el-form-item>
</template>
<el-form-item>
<el-button type="primary" @click="submitForm">提交</el-button>
<el-button @click="resetForm">重置</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
// 表单引用
const dynamicFormRef = ref()
// 加载状态
const loading = ref(false)
// 表单数据
const formData = ref({})
// 表单验证规则
const formRules = ref({})
// 表单配置(从后端获取)
const formConfig = ref([
// 默认配置,实际会被后端数据覆盖
{
name: 'username',
label: '用户名',
type: 'input',
required: true,
placeholder: '请输入用户名'
}
])
// 模拟从后端获取表单配置
const fetchFormConfig = async () => {
try {
loading.value = true
// 这里替换为实际的API调用
const response = await mockApiGetFormConfig()
formConfig.value = response.data.fields
// 初始化表单数据
initFormData()
// 生成验证规则
generateFormRules()
} catch (error) {
ElMessage.error('获取表单配置失败: ' + error.message)
} finally {
loading.value = false
}
}
// 初始化表单数据
const initFormData = () => {
const data = {}
formConfig.value.forEach(field => {
// 根据字段类型设置默认值
switch (field.type) {
case 'checkbox':
data[field.name] = field.defaultValue || []
break
case 'switch':
data[field.name] = field.defaultValue || false
break
default:
data[field.name] = field.defaultValue || ''
}
})
formData.value = data
}
// 生成表单验证规则
const generateFormRules = () => {
const rules = {}
formConfig.value.forEach(field => {
if (field.required || field.rules) {
rules[field.name] = generateFieldRules(field)
}
})
formRules.value = rules
}
// 生成单个字段的验证规则
const generateFieldRules = (field) => {
const rules = []
// 必填规则
if (field.required) {
rules.push({
required: true,
message: field.message || `${field.label}不能为空`,
trigger: field.trigger || 'blur'
})
}
// 自定义规则
if (field.rules && Array.isArray(field.rules)) {
rules.push(...field.rules)
}
// 类型校验
if (field.type === 'input' && field.inputType === 'email') {
rules.push({
type: 'email',
message: '请输入正确的邮箱格式',
trigger: ['blur', 'change']
})
}
return rules
}
// 提交表单
const submitForm = async () => {
try {
// 表单验证
await dynamicFormRef.value.validate()
// 这里替换为实际的提交API
const response = await mockApiSubmitForm(formData.value)
ElMessage.success('提交成功')
console.log('表单数据:', formData.value)
console.log('服务器响应:', response)
// 可以在这里处理提交成功后的逻辑
} catch (error) {
if (error instanceof Error) {
ElMessage.error('表单验证失败: ' + error.message)
}
}
}
// 重置表单
const resetForm = () => {
dynamicFormRef.value.resetFields()
}
// 模拟API获取表单配置
const mockApiGetFormConfig = () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve({
data: {
fields: [
{
name: 'username',
label: '用户名',
type: 'input',
required: true,
placeholder: '请输入用户名',
maxlength: 20
},
{
name: 'password',
label: '密码',
type: 'input',
inputType: 'password',
required: true,
placeholder: '请输入密码',
rules: [
{ min: 6, max: 18, message: '密码长度在6到18个字符', trigger: 'blur' }
]
},
{
name: 'gender',
label: '性别',
type: 'select',
required: true,
options: [
{ label: '男', value: 'male' },
{ label: '女', value: 'female' },
{ label: '其他', value: 'other' }
]
},
{
name: 'hobbies',
label: '兴趣爱好',
type: 'checkbox',
options: [
{ label: '游泳', value: 'swimming' },
{ label: '跑步', value: 'running' },
{ label: '阅读', value: 'reading' }
]
},
{
name: 'subscribe',
label: '订阅通知',
type: 'switch',
defaultValue: true
},
{
name: 'birthday',
label: '出生日期',
type: 'date',
dateType: 'date',
required: true
}
]
}
})
}, 800)
})
}
// 模拟API提交表单
const mockApiSubmitForm = (data) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve({ code: 200, message: 'success', data })
}, 500)
})
}
// 组件挂载时获取表单配置
onMounted(() => {
fetchFormConfig()
})
</script>
<style scoped>
.dynamic-form-container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
</style>
后端API数据结构建议
后端API返回的表单配置建议采用如下JSON格式:
javascript
{
"code": 200,
"message": "success",
"data": {
"fields": [
{
"name": "username",
"label": "用户名",
"type": "input",
"required": true,
"placeholder": "请输入用户名",
"inputType": "text",
"maxlength": 20,
"rules": [
{
"pattern": "^[a-zA-Z0-9_]+$",
"message": "只能包含字母、数字和下划线"
}
]
},
{
"name": "gender",
"label": "性别",
"type": "select",
"required": true,
"options": [
{
"label": "男",
"value": "male"
},
{
"label": "女",
"value": "female"
}
]
},
{
"name": "subscribe",
"label": "订阅通知",
"type": "switch",
"defaultValue": true
}
]
}
}