?完整代碼
<template><div class="dynamic-form-container"><el-formref="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-itemv-if="field.type === 'input'":label="field.label":prop="field.name":rules="generateFieldRules(field)"><el-inputv-model="formData[field.name]":placeholder="field.placeholder || `請輸入${field.label}`":type="field.inputType || 'text'":clearable="field.clearable !== false"/></el-form-item><!-- 下拉選擇 --><el-form-itemv-else-if="field.type === 'select'":label="field.label":prop="field.name":rules="generateFieldRules(field)"><el-selectv-model="formData[field.name]":placeholder="field.placeholder || `請選擇${field.label}`":clearable="field.clearable !== false"style="width: 100%"><el-optionv-for="option in field.options":key="option.value":label="option.label":value="option.value"/></el-select></el-form-item><!-- 單選框 --><el-form-itemv-else-if="field.type === 'radio'":label="field.label":prop="field.name":rules="generateFieldRules(field)"><el-radio-group v-model="formData[field.name]"><el-radiov-for="option in field.options":key="option.value":label="option.value">{{ option.label }}</el-radio></el-radio-group></el-form-item><!-- 復選框 --><el-form-itemv-else-if="field.type === 'checkbox'":label="field.label":prop="field.name":rules="generateFieldRules(field)"><el-checkbox-group v-model="formData[field.name]"><el-checkboxv-for="option in field.options":key="option.value":label="option.value">{{ option.label }}</el-checkbox></el-checkbox-group></el-form-item><!-- 日期選擇器 --><el-form-itemv-else-if="field.type === 'date'":label="field.label":prop="field.name":rules="generateFieldRules(field)"><el-date-pickerv-model="formData[field.name]":type="field.dateType || 'date'":placeholder="field.placeholder || `請選擇${field.label}`"style="width: 100%"/></el-form-item><!-- 開關 --><el-form-itemv-else-if="field.type === 'switch'":label="field.label":prop="field.name"><el-switch v-model="formData[field.name]" /></el-form-item><!-- 自定義插槽 --><el-form-itemv-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 || []breakcase 'switch':data[field.name] = field.defaultValue || falsebreakdefault: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()// 這里替換為實際的提交APIconst 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格式:
{"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}]}
}