Vue3+Element?Plus動(dòng)態(tài)表單實(shí)現(xiàn)
完整代碼
<template>
<div class="dynamic-form-container">
<el-form
ref="dynamicFormRef"
:model="formData"
:rules="formRules"
label-width="auto"
label-position="top"
v-loading="loading"
>
<!-- 動(dòng)態(tài)渲染表單字段 -->
<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 || `請(qǐng)輸入${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 || `請(qǐng)選擇${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>
<!-- 復(fù)選框 -->
<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 || `請(qǐng)選擇${field.label}`"
style="width: 100%"
/>
</el-form-item>
<!-- 開(kāi)關(guān) -->
<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()
// 加載狀態(tài)
const loading = ref(false)
// 表單數(shù)據(jù)
const formData = ref({})
// 表單驗(yàn)證規(guī)則
const formRules = ref({})
// 表單配置(從后端獲取)
const formConfig = ref([
// 默認(rèn)配置,實(shí)際會(huì)被后端數(shù)據(jù)覆蓋
{
name: 'username',
label: '用戶名',
type: 'input',
required: true,
placeholder: '請(qǐng)輸入用戶名'
}
])
// 模擬從后端獲取表單配置
const fetchFormConfig = async () => {
try {
loading.value = true
// 這里替換為實(shí)際的API調(diào)用
const response = await mockApiGetFormConfig()
formConfig.value = response.data.fields
// 初始化表單數(shù)據(jù)
initFormData()
// 生成驗(yàn)證規(guī)則
generateFormRules()
} catch (error) {
ElMessage.error('獲取表單配置失敗: ' + error.message)
} finally {
loading.value = false
}
}
// 初始化表單數(shù)據(jù)
const initFormData = () => {
const data = {}
formConfig.value.forEach(field => {
// 根據(jù)字段類型設(shè)置默認(rèn)值
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
}
// 生成表單驗(yàn)證規(guī)則
const generateFormRules = () => {
const rules = {}
formConfig.value.forEach(field => {
if (field.required || field.rules) {
rules[field.name] = generateFieldRules(field)
}
})
formRules.value = rules
}
// 生成單個(gè)字段的驗(yàn)證規(guī)則
const generateFieldRules = (field) => {
const rules = []
// 必填規(guī)則
if (field.required) {
rules.push({
required: true,
message: field.message || `${field.label}不能為空`,
trigger: field.trigger || 'blur'
})
}
// 自定義規(guī)則
if (field.rules && Array.isArray(field.rules)) {
rules.push(...field.rules)
}
// 類型校驗(yàn)
if (field.type === 'input' && field.inputType === 'email') {
rules.push({
type: 'email',
message: '請(qǐng)輸入正確的郵箱格式',
trigger: ['blur', 'change']
})
}
return rules
}
// 提交表單
const submitForm = async () => {
try {
// 表單驗(yàn)證
await dynamicFormRef.value.validate()
// 這里替換為實(shí)際的提交API
const response = await mockApiSubmitForm(formData.value)
ElMessage.success('提交成功')
console.log('表單數(shù)據(jù):', formData.value)
console.log('服務(wù)器響應(yīng):', response)
// 可以在這里處理提交成功后的邏輯
} catch (error) {
if (error instanceof Error) {
ElMessage.error('表單驗(yàn)證失敗: ' + 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: '請(qǐng)輸入用戶名',
maxlength: 20
},
{
name: 'password',
label: '密碼',
type: 'input',
inputType: 'password',
required: true,
placeholder: '請(qǐng)輸入密碼',
rules: [
{ min: 6, max: 18, message: '密碼長(zhǎng)度在6到18個(gè)字符', trigger: 'blur' }
]
},
{
name: 'gender',
label: '性別',
type: 'select',
required: true,
options: [
{ label: '男', value: 'male' },
{ label: '女', value: 'female' },
{ label: '其他', value: 'other' }
]
},
{
name: 'hobbies',
label: '興趣愛(ài)好',
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)
})
}
// 組件掛載時(shí)獲取表單配置
onMounted(() => {
fetchFormConfig()
})
</script>
<style scoped>
.dynamic-form-container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
</style>后端API數(shù)據(jù)結(jié)構(gòu)建議
后端API返回的表單配置建議采用如下JSON格式:
{
"code": 200,
"message": "success",
"data": {
"fields": [
{
"name": "username",
"label": "用戶名",
"type": "input",
"required": true,
"placeholder": "請(qǐng)輸入用戶名",
"inputType": "text",
"maxlength": 20,
"rules": [
{
"pattern": "^[a-zA-Z0-9_]+$",
"message": "只能包含字母、數(shù)字和下劃線"
}
]
},
{
"name": "gender",
"label": "性別",
"type": "select",
"required": true,
"options": [
{
"label": "男",
"value": "male"
},
{
"label": "女",
"value": "female"
}
]
},
{
"name": "subscribe",
"label": "訂閱通知",
"type": "switch",
"defaultValue": true
}
]
}
}到此這篇關(guān)于Vue3+Element Plus動(dòng)態(tài)表單實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Vue3 Element Plus動(dòng)態(tài)表單內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解決vue無(wú)法加載文件D:\Program Files\nodejs\node_global\vue.ps1,
這篇文章主要給大家介紹了關(guān)于解決vue無(wú)法加載文件D:\Program Files\nodejs\node_global\vue.ps1,因?yàn)樵诖讼到y(tǒng)上禁止運(yùn)行腳本的相關(guān)資料,這個(gè)報(bào)錯(cuò)是由于在系統(tǒng)上禁止運(yùn)行腳本導(dǎo)致的,文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下2024-01-01
Vue?elementUI表單嵌套表格并對(duì)每行進(jìn)行校驗(yàn)詳解
element-ui中有詳細(xì)的各種表格及表格方法,下面這篇文章主要給大家介紹了關(guān)于Vue?elementUI表單嵌套表格并對(duì)每行進(jìn)行校驗(yàn)的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-01-01
如何在 Vue3 中使用 OpenLayers 實(shí)現(xiàn)事件 loadst
在這篇文章中,我將詳細(xì)介紹如何在 Vue3 + OpenLayers 中監(jiān)聽(tīng) loadstart 和 loadend 事件,并通過(guò) Vue3 Composition API 進(jìn)行代碼優(yōu)化,使其更加高效、健壯,感興趣的朋友一起看看吧2025-04-04
Vue專屬狀態(tài)管理庫(kù)Pinia的使用與實(shí)踐分享
在 Vue 的開(kāi)發(fā)中,狀態(tài)管理是一個(gè)不可或缺的部分,尤其是在復(fù)雜的應(yīng)用中,組件之間的狀態(tài)共享和管理變得至關(guān)重要,Pinia 作為 Vue 的專屬狀態(tài)管理庫(kù),本文將深入介紹 Pinia 的基礎(chǔ)知識(shí)、核心功能以及實(shí)際使用場(chǎng)景,需要的朋友可以參考下2024-11-11
使用vue-i18n?入口文件配置控制臺(tái)報(bào)警問(wèn)題解決
這篇文章主要介紹了使用vue-i18n?入口文件配置控制臺(tái)報(bào)警問(wèn)題解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06
Vue中使用element-ui給按鈕綁定一個(gè)單擊事件實(shí)現(xiàn)點(diǎn)擊按鈕就彈出dialog對(duì)話框
最近遇到了個(gè)需求是使用element-ui插件編寫頁(yè)面,點(diǎn)擊按鈕,彈出對(duì)話框,這篇文章主要給大家介紹了關(guān)于Vue中使用element-ui給按鈕綁定一個(gè)單擊事件實(shí)現(xiàn)點(diǎn)擊按鈕就彈出dialog對(duì)話框的相關(guān)資料,需要的朋友可以參考下2022-11-11
vue-manage-system升級(jí)到vue3的開(kāi)發(fā)總結(jié)分析
這篇文章主要為大家介紹了vue-manage-system升級(jí)到vue3的開(kāi)發(fā)總結(jié)分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09

