一個成熟的表單
表單表單,你已經(jīng)長大了力惯,你要學(xué)會:
- 動態(tài)渲染
- 支持單列碗誉、雙列、多列
- 支持調(diào)整布局
- 支持表單驗證
- 支持調(diào)整排列(顯示)順序
- 依據(jù)組件值顯示需要的組件
- 支持 item 擴展組件
- 可以自動創(chuàng)建 model
這個表單控件是基于 element-plus 的 el-form 做的二次封裝父晶,所以首先感謝 element-plus 提供了這么強大的UI庫哮缺,以前用jQuery做過類似的,但是非常麻煩甲喝,既不好看尝苇,可維護性、擴展性也差埠胖,好多想法都實現(xiàn)不了(技術(shù)有限)糠溜。
現(xiàn)在好了,站在巨人的肩膀上直撤,以前的想法都可以實現(xiàn)了非竿。
大圖預(yù)警:這里有好幾張大 gif 圖片,流量不夠的話慎開谋竖。汽馋。。
調(diào)整排列(顯示)順序以及調(diào)整布局
既然是動態(tài)渲染圈盔,那么組件的顯示順序也應(yīng)該可以靈活調(diào)整豹芯。
所以設(shè)計了一個顯示用的數(shù)組,把組件ID存放進去驱敲,v-for的時候铁蹈,遍歷這個數(shù)組就可以實現(xiàn)調(diào)整顯示順序的需求了。
單列的調(diào)整順序众眨,以及調(diào)整布局
導(dǎo)出MP4才400多k握牧,導(dǎo)出gif居然2M多容诬。真的好無語。
可以插入沿腰,可以交換览徒,可以把幾個短的擠到一行。
多列的調(diào)整順序颂龙,以及調(diào)整布局
掐了一半沒播习蓬,因為gif太大了。
還是插入措嵌、交換躲叼,可以讓一個組件獨占一行。
多列也是一樣的操作企巢。
依據(jù)組件值顯示需要的組件
有的時候表單里的組件并不是都需要用戶填寫的枫慷,比如注冊域名的時候,公司注冊需要填寫公司名稱等信息浪规,而個人注冊顯然沒有公司信息可以填或听,那么這時候表單就需要根據(jù)用戶的選擇(公司、個人)來顯示不同的字段來讓用戶填寫笋婿。
這時候就需要一個運行時調(diào)整的功能誉裆。
其實現(xiàn)原理,還是通過調(diào)整那個排序用的數(shù)組來實現(xiàn)的萌抵。
先看一下效果:
還是這么大。元镀。绍填。
下拉列表里面選擇不同的選項,會顯示對應(yīng)的組件栖疑。這種對應(yīng)并不是寫死的表單控件里面讨永,而是可以在外部配置出來。
監(jiān)聽組件的值遇革,然后修改排序用的數(shù)組
- json文件:
"formColShow": {
"103": {
"101" : [101, 102, 103, 104],
"102" : [101, 102, 103, 106, 107, 104, 105],
"103" : [101, 102, 103, 104, 105]
}
},
依據(jù)組件的編號卿闹,指定每個組件值對應(yīng)需要顯示的組件ID。
似乎有點太抽象了萝快,其實就是應(yīng)為抽象出來共同的特點锻霎,才可以實現(xiàn)這樣的功能呀。
// 設(shè)置組件的顯示順序
const setFormColSort = (array = formMeta.colOrder) => {
formColSort.length = 0
formColSort.push(...array)
}
// 監(jiān)聽組件值的變化揪漩,調(diào)整組件的顯示以及顯示順序
if (typeof formMeta.formColShow !== 'undefined') {
for (const key in formMeta.formColShow) {
const ctl = formMeta.formColShow[key]
const colName = formItemMeta[key].colName
watch(() => formModel[colName], (v1, v2) => {
if (typeof ctl[v1] === 'undefined') {
// 沒有設(shè)定旋恼,顯示默認組件
setFormColSort()
} else {
// 按照設(shè)定顯示組件
setFormColSort(ctl[v1])
}
})
}
}
遍歷 formColShow ,監(jiān)聽里面的組件的值奄容,有變化了就按照值去加載對應(yīng)的組件ID冰更,如果沒有就加載默認組件产徊。
自動創(chuàng)建 model
我比較懶,不想寫model的定義代碼蜀细,太麻煩舟铜。
尤其當(dāng)需求變更的時候,又要改代碼奠衔。
那么能不能自動創(chuàng)建呢谆刨?于是寫了個函數(shù)。
完整的model
表單里面有多少組件涣觉,就創(chuàng)建多少個屬性痴荐。
// 根據(jù)表單元素meta,創(chuàng)建 v-model
const createModel = () => {
// 依據(jù)meta官册,創(chuàng)建module
for (const key in formItemMeta) {
const m = formItemMeta[key]
// 根據(jù)控件類型設(shè)置屬性值
switch (m.controlType) {
case 100: // 文本類
case 101:
case 102:
case 103:
case 104:
case 105:
case 106:
case 107:
case 130:
case 131:
formModel[m.colName] = ''
break
case 110: // 日期
case 111: // 日期時間
case 112: // 年月
case 114: // 年
case 113: // 年周
formModel[m.colName] = null
break
case 115: // 任意時間
formModel[m.colName] = '00:00:00'
break
case 116: // 選擇時間
formModel[m.colName] = '00:00'
break
case 120: // 數(shù)字
case 121:
formModel[m.colName] = 0
break
case 150: // 勾選
case 151: // 開關(guān)
formModel[m.colName] = false
break
case 153: // 單選組
case 160: // 下拉單選
case 162: // 下拉聯(lián)動
formModel[m.colName] = null
break
case 152: // 多選組
case 161: // 下拉多選
formModel[m.colName] = []
break
}
// 看看有沒有設(shè)置默認值
if (typeof m.defaultValue !== 'undefined') {
switch (m.defaultValue) {
case '':
break
case '{}':
formModel[m.colName] = {}
break
case '[]':
formModel[m.colName] = []
break
case 'date':
formModel[m.colName] = new Date()
break
default:
formModel[m.colName] = m.defaultValue
break
}
}
}
// 同步父組件的v-model
context.emit('update:modelValue', formModel)
return formModel
}
雖然有點長生兆,但都是簡單的case判斷。這樣就省去了手擼 model 的麻煩膝宁。
依據(jù)選項創(chuàng)建 model
因為支持依據(jù)用戶的選擇而現(xiàn)實不同的組件鸦难,那么創(chuàng)建的model是否也需要調(diào)整呢?所以又寫了個函數(shù)员淫。兩種 model 同時支持合蔽,這樣需要哪個就可以用哪個。
// 依據(jù)用戶選項介返,創(chuàng)建對應(yīng)的 model
const createPartModel = (array) => {
// 先刪除屬性
for (const key in formPartModel) {
delete formPartModel[key]
}
// 建立新屬性
for (let i = 0; i < array.length; i++) {
const colName = formItemMeta[array[i]].colName
formPartModel[colName] = formModel[colName]
}
}
先把原有的屬性刪掉拴事,然后再加上新的屬性。
支持 擴展組件
自帶的組件肯定是不夠的圣蝎,因為用戶的需求總是千變?nèi)f化的刃宵,那么新組件如何加入到表單控件里面呢?可以按照接口定義封裝成符合要求的組件徘公,然后做一個map字典牲证,就可以設(shè)置進去了。
因為接口統(tǒng)一关面,所以可以適應(yīng)表單控件的調(diào)用坦袍。
簡單的方法是,直接修改兩個js文件等太。
如果不方便修改的話捂齐,也可以通過屬性傳遞進來。目前暫時還沒有想好細節(jié)缩抡,不過似乎不是太難辛燥。
動態(tài)渲染
引入json,然后作為屬性傳入表單控件,然后就可以了挎塌。
這樣我們再實現(xiàn)添加徘六、修改的功能的時候,就不是寫代碼榴都,而是寫json了待锈,另外劇透一下,這個json當(dāng)然是不需要手擼的嘴高,我這么懶竿音。
單列、雙列拴驮、多列以及調(diào)整布局
這個其實就是個 v-for 的功能春瞬,使用el-cow、el-col 設(shè)置好span就可以實現(xiàn)套啤。
// 根據(jù)配置里面的colCount宽气,設(shè)置 formColSpan
const setFormColSpan = () => {
const formColCount = formMeta.formColCount // 列數(shù)
const moreColSpan = 24 / formColCount // 一個格子占多少份
if (formColCount === 1) {
// 一列的情況
for (const key in formItemMeta) {
const m = formItemMeta[key]
if (typeof m.colCount === 'undefined') {
formColSpan[m.controlId] = moreColSpan
} else {
if (m.colCount >= 1) {
// 單列,多占的也只有24格
formColSpan[m.controlId] = moreColSpan
} else if (m.colCount < 0) {
// 擠一擠的情況潜沦, 24 除以 占的份數(shù)
formColSpan[m.controlId] = moreColSpan / (0 - m.colCount)
}
}
}
} else {
// 多列的情況
for (const key in formItemMeta) {
const m = formItemMeta[key]
if (typeof m.colCount === 'undefined') {
formColSpan[m.controlId] = moreColSpan
} else {
if (m.colCount < 0 || m.colCount === 1) {
// 多列萄涯,擠一擠的占一份
formColSpan[m.controlId] = moreColSpan
} else if (m.colCount > 1) {
// 多列,占的格子數(shù) * 份數(shù)
formColSpan[m.controlId] = moreColSpan * m.colCount
}
}
}
}
}
// 先運行一次
setFormColSpan()
// 設(shè)置組件的顯示順序
const setFormColSort = (array = formMeta.colOrder) => {
formColSort.length = 0
formColSort.push(...array)
}
// 先運行一下
setFormColSort()
表單驗證
這個使用el-form提供的驗證功能唆鸡。
目前暫時還沒有歸納好el-form的驗證涝影,因為需要把這個驗證用的數(shù)據(jù)寫入到j(luò)son里面,然后讀取出來設(shè)置好即可争占。
所以肯定沒難度燃逻,只是需要點時間。