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.js實(shí)戰(zhàn)之組件的進(jìn)階
組件(Component)是 Vue.js 最強(qiáng)大的功能之一,之前的文章都只是用到了基本的封裝功能,這次將介紹一些更強(qiáng)大的擴(kuò)展。這篇文章主要介紹了Vue.js實(shí)戰(zhàn)之組件進(jìn)階的相關(guān)資料,需要的朋友們可以參考借鑒,下面來(lái)一起看看吧。2017-04-04
Vue程序化的事件監(jiān)聽(tīng)器(實(shí)例方案詳解)
本文通過(guò)兩種方案給大家介紹了Vue程序化的事件監(jiān)聽(tīng)器,每種方案通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2020-01-01
Vue.js實(shí)現(xiàn)大轉(zhuǎn)盤抽獎(jiǎng)總結(jié)及實(shí)現(xiàn)思路
這篇文章主要介紹了 Vue.js實(shí)現(xiàn)大轉(zhuǎn)盤抽獎(jiǎng)總結(jié)及實(shí)現(xiàn)思路,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-10-10
解決Vue中引入swiper,在數(shù)據(jù)渲染的時(shí)候,發(fā)生不滑動(dòng)的問(wèn)題
今天小編就為大家分享一篇解決Vue中引入swiper,在數(shù)據(jù)渲染的時(shí)候,發(fā)生不滑動(dòng)的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-09-09
詳解webpack編譯多頁(yè)面vue項(xiàng)目的配置問(wèn)題
本篇文章主要介紹了詳解webpack編譯多頁(yè)面vue項(xiàng)目的配置問(wèn)題,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-12-12

