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> <!-- 開關(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: '興趣愛好', 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)資料,需要的朋友們可以參考借鑒,下面來一起看看吧。2017-04-04Vue程序化的事件監(jiān)聽器(實(shí)例方案詳解)
本文通過兩種方案給大家介紹了Vue程序化的事件監(jiān)聽器,每種方案通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2020-01-01Vue.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)思路,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-10-10解決Vue中引入swiper,在數(shù)據(jù)渲染的時(shí)候,發(fā)生不滑動(dòng)的問題
今天小編就為大家分享一篇解決Vue中引入swiper,在數(shù)據(jù)渲染的時(shí)候,發(fā)生不滑動(dòng)的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-09-09詳解webpack編譯多頁面vue項(xiàng)目的配置問題
本篇文章主要介紹了詳解webpack編譯多頁面vue項(xiàng)目的配置問題,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-12-12