vue封裝form表單組件拒絕重復(fù)寫form表單
前言
在日常工作中,當(dāng)需要處理的form
表單很多時,我們沒有必要一遍又一遍地重復(fù)寫form
表單,直接封裝一個組件去處理就好。其實(shí)很早之前就有涉獵通過使用類似配置json
方法寫form
表單的文章,雖然當(dāng)時也沒怎么認(rèn)真看...我們前端組也是使用這種思想配置的。
然而,這個思想和方法很早就有出現(xiàn)過,并不怎么新穎,還望不喜勿噴...在此我封裝了一個最最最基礎(chǔ)的form
表單,離我們前端組封裝的組件差距還很大,有興趣朋友們的可以繼續(xù)往下完善。
核心思想:
- 通過配置
js
文件的變量,使用vue
的is
屬性動態(tài)切換組件,默認(rèn)顯示的組件為el-input
- 通過
element
的分欄和柵格屬性,對form
表單進(jìn)行響應(yīng)式布局 baseForm
在組件初始化時,需要動態(tài)添加校驗(yàn)規(guī)則
、請求接口
以及初始化form
的部分值- 正統(tǒng)思想是對
element
組件的各個組件進(jìn)行二次封裝,然后通過is
屬性切換二次封裝后的組件,在此不做過多描述,有興趣的朋友可以自行研究 - 更好的思想是將頁面請求、搜索項(xiàng)、表格、分頁封裝到一起,形成一個整體,這也是我們前端小組目前的處理思路
實(shí)現(xiàn)重點(diǎn):
- 任何標(biāo)簽或者組件都可以通過
vue的is
屬性來動態(tài)切換組件。本組件中使用div
,將它的寬度設(shè)置為100%
,使得element
組件能夠完全撐開。(使用vue內(nèi)置組件component會與el-radio-group
相沖突,因?yàn)樗讓泳褪怯?code>component實(shí)現(xiàn)的) - 表單上添加
validate-on-rule-change="false"
屬性,防止在表單初始化時就校驗(yàn)表單 - 當(dāng)為對象添加不存在的字段屬性時,需要使用
$set
實(shí)現(xiàn)數(shù)據(jù)的響應(yīng)式 - 如果
form
表單中只有一個輸入框,在輸入框中按下回車會提交表單,刷新頁面。為了阻止這一默認(rèn)行為,需要在el-form
標(biāo)簽上添加@submit.native.prevent
- 使用
lodash
中的get
方法獲取對象的屬性值,如果屬性值不存在,可以給一個默認(rèn)值 baseForm
子組件中可以傳一個form
對象給父組件,那么添加或者編輯form
對象,就都可以在父組件中進(jìn)行。
表單雙向綁定的方式有兩種:
1.使用v-model進(jìn)行雙向綁定
<div v-else clearable style="width: 100%" type="daterange" range-separator="至" start-placeholder="開始日期" end-placeholder="結(jié)束日期" v-model="form[column.prop]" :label-width="get(column, 'size', column || defaultFormSize)" :disabled="get(column, 'disabled', false)" :is="get(column, 'type', 'el-input')" >
2.使用v-model的語法糖
(`:value以及@input`)進(jìn)行雙向綁定
<div v-else clearable style="width: 100%" type="daterange" range-separator="至" start-placeholder="開始日期" end-placeholder="結(jié)束日期" :value="form[column.prop]" :label-width="get(column, 'size', column || defaultFormSize)" :disabled="get(column, 'disabled', false)" :is="get(column, 'type', 'el-input')" @input="input($event,column.prop)" > methods: { input(e,prop) { this.$set(this.form, prop, e) } }
配置項(xiàng)
(本組件寫得比較基礎(chǔ),目前僅支持element的五個常用組件):
整體字段:
formSize
(表單中各element組件的整體大小)
column數(shù)組中每一個對象對應(yīng)的字段(非請求接口):
label
(表單label的名稱)span
(這個表單項(xiàng)占據(jù)的份數(shù),一行為24
,默認(rèn)為12
)labelWidth
(這個表單項(xiàng)的label寬度
,默認(rèn)為90px
)labelHeight
(這個表單項(xiàng)占據(jù)的高度,默認(rèn)為50px
)slotName
(插槽名)prop
(這個表單項(xiàng)綁定的屬性名稱)size
(這個表單項(xiàng)組件的大小,默認(rèn)為small
)disabled
(是否禁用這個表單項(xiàng))type
(使用的element
組件,默認(rèn)為el-input
)dic
(非接口請求的靜態(tài)表單數(shù)據(jù),使用{label以及value字段}
表示的數(shù)組形式)
column數(shù)組中每一個對象對應(yīng)的字段(請求接口):
url
(接口的api
地址)requestParams
(非必填項(xiàng),需要額外傳入的傳參)requestLabel
(接口返回對應(yīng)的id
)requestValue
(接口返回對應(yīng)的value
)
效果瀏覽
源碼放送
1. baseForm組件
<template> <el-form ref="form" :model="form" :rules="formRules" :size="get(option, 'formSize', defaultFormSize)" :validate-on-rule-change="false" @submit.native.prevent > <el-row :gutter="20" :span="24"> <el-col v-for="column in formColumn" :key="column.label" :md="column.span || 12" :sm="12" :xs="24" > <el-form-item :label="`${column.label}:`" :prop="column.prop" :label-width="get(column, 'labelWidth', column.labelWidth || defaultLabelWidth)" :style="{ height: get(column, 'labelHeight', column.labelHeight || defaultLabelHeight) }" > <slot v-if="column.slotName" :name="column.slotName" :form="form" :prop="column.prop" :value="form[column.prop]" ></slot> <div v-else clearable style="width: 100%" type="daterange" range-separator="-" start-placeholder="開始日期" end-placeholder="結(jié)束日期" v-model="form[column.prop]" :placeholder="getPlaceholder(column.type, column.label)" :label-width="get(column, 'size', column || defaultFormSize)" :disabled="get(column, 'disabled', false)" :is="get(column, 'type', 'el-input')" > <template v-if="column.type == 'el-select'"> <el-option v-for="item in column.dic" :key="item.value" :label="item.label" :value="item.value" > </el-option> </template> <template v-if="column.type == 'el-radio-group'"> <el-radio v-for="item in column.dic" :key="item.value" :label="item.label" > {{ item.value }} </el-radio> </template> <template v-if="column.type == 'el-checkbox-group'"> <el-checkbox v-for="item in column.dic" :key="item.label" :label="item.value" > {{ item.label }} </el-checkbox> </template> </div> </el-form-item> </el-col> </el-row> </el-form> </template> <script> import get from 'lodash/get' import request from '@/service/request' export default { props: { option: { type: Object, default: () => {} }, form: { type: Object, default: () => {} } }, data() { return { formRules: {}, defaultFormSize: 'small', defaultLabelWidth: '90px', defaultLabelHeight: '50px' } }, computed: { formColumn() { return this.option.column } }, created() { this.initRules() this.initRequest() this.initValue() }, methods: { get, getPlaceholder(type, label) { return type == 'el-select' ? `請選擇${label}` : `請輸入${label}` }, initRequest() { if (!Array.isArray(this.formColumn)) return // 根據(jù)實(shí)際請求接口地址的前綴來判斷 const urls = this.formColumn?.filter((item) => item.url && item.url.indexOf('/emes') == 0) || [] urls.forEach(async (item) => { const data = { page: { pageIndex: 1, pageSize: 0 }, ...item.requestParams } const { detail } = await request({ url: item.url, method: 'post', data }) || [] const finalResult = detail.map((result) => ({ label: result[item.requestLabel], value: result[item.requestValue] })) this.$set(item, 'dic', finalResult) }) }, initRules() { if (!Array.isArray(this.formColumn)) return this.formColumn?.forEach((item) => { if (item.rules) { item.rules.map((rule, index) => { if (rule.required) { item.rules.splice(index, 1, { message: ['el-radio-group', 'el-checkbox-group'].includes(item.type) ? `${item.label}必選` : `${item.label}必填`, ...rule }) } }) this.$set(this.formRules, item.prop, item.rules) } }) }, initValue() { const selectList = this.formColumn.filter((item) => ['el-radio-group', 'el-checkbox-group'].includes(item.type)) selectList.forEach((item) => { this.$set(this.form, item.prop, item.type == 'el-radio-group' ? item.dic[0].label : [item.dic[0].value]) }) } } } </script>
2. 父組件
<template> <div class="app-container"> <myForm :option="option" :form="form"> <template #usageSlot="{form, prop}"> <el-input size="small" placeholder="請輸入插槽使用" v-model="form[prop]" clearable > </el-input> </template> </myForm> </div> </template> <script> import { option } from './const.js' export default { data() { return { option, form: {} } } } </script>
3. 配置項(xiàng)
export const option = { column: [ { label: '姓名', prop: 'name', span: 8, rules: [ { required: true } ] }, { label: '職業(yè)', prop: 'job', type: 'el-select', span: 8, dic: [ { label: '教師', value: 0 }, { label: '程序猿', value: 1 }, { label: '作家', value: 2 } ], rules: [ { required: true } ] }, { label: '性別', prop: 'sex', span: 8, type: 'el-radio-group', dic: [ { label: 0, value: '男' }, { label: 1, value: '女' } ], rules: [ { required: true } ] }, { label: '城市', prop: 'city', type: 'el-checkbox-group', span: 8, dic: [ { label: '仙桃', value: 0 }, { label: '泉州', value: 1 }, { label: '武漢', value: 2 } ], rules: [ { required: true } ] }, { label: '出生日期', prop: 'data', type: 'el-date-picker', span: 8, rules: [ { required: true } ] }, { label: '測試', prop: 'test', type: 'el-select', span: 8, url:'/emes/factoryOrderService/warehouse/list', requestLabel: 'warehouseName', requestValue: 'id', rules: [ { required: true } ] }, { label: '插槽使用', prop: 'usage', slotName: 'usageSlot', span: 8, rules: [ { required: true } ] } ] }
4. 添加或編輯
- 添加: 如果是添加狀態(tài),直接在父組件中引入就好。在點(diǎn)擊確定按鈕時,拿到子組件的
ref
并進(jìn)行表單校驗(yàn)。校驗(yàn)通過后,使用后端定義好的接口進(jìn)行傳參; - 編輯: 如果是編輯狀態(tài),則需要在父組件頁面初始化時,將后端返回的數(shù)據(jù)使用
$set
進(jìn)行初始賦值,其余操作同添加狀態(tài)。
以上就是vue封裝form表單組件拒絕重復(fù)寫form表單的詳細(xì)內(nèi)容,更多關(guān)于vue封裝form表單組件的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
詳解Vue.js 作用域、slot用法(單個slot、具名slot)
這篇文章主要介紹了Vue.js 作用域、slot用法(單個slot、具名slot),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2019-10-10vue項(xiàng)目實(shí)現(xiàn)多語言切換的思路
這篇文章主要介紹了vue項(xiàng)目實(shí)現(xiàn)多語言切換的思路,幫助大家完成多語言翻譯,感興趣的朋友可以了解下2020-09-09基于Vue3和element-plus實(shí)現(xiàn)登錄功能(最終完整版)
這篇文章主要介紹了基于Vue3和element-plus實(shí)現(xiàn)一個完整的登錄功能,本文結(jié)合示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-03-03