vue動(dòng)態(tài)渲染表單配置項(xiàng)
在項(xiàng)目中,我們經(jīng)常會(huì)用到各種表單,但是數(shù)量一般都不多持际,那當(dāng)我們編寫大量表單時(shí)怎么辦呢,難道我們還要一個(gè)標(biāo)簽一個(gè)標(biāo)簽的寫嗎哗咆?如果各個(gè)表單之間又出現(xiàn)了各種級(jí)聯(lián)關(guān)系各種判斷呢蜘欲?所以這時(shí)候就出現(xiàn)了另外一種方式,我們可以通過(guò)對(duì)象的方式編寫表單項(xiàng)來(lái)替換element本來(lái)的標(biāo)簽寫法晌柬,以使我們能夠方便清晰的最大化的控制每個(gè)表單姥份。
當(dāng)我們編寫一個(gè)普通的input表單時(shí)郭脂,我們一般會(huì)直接在template里這樣寫:
<el-input v-model="inputValue" placeholder="請(qǐng)輸入"></el-input>
用了表單配置項(xiàng)后,我們可以這樣在data里寫:
{
type: 'input',
key: 'inputValue',
label: 'input框:',
placeholder: '請(qǐng)輸入'
}
我們先不討論怎么就可以這樣寫了澈歉,我們先來(lái)明白為什么要這樣用
- 可以通過(guò)this.XXX來(lái)改變某個(gè)表單項(xiàng)
- 不用再在標(biāo)簽里加各種判斷
- 標(biāo)簽寫法太死板
- render寫法展鸡,更貼近vue底層
如何配置
input舉栗,最后會(huì)附上其他的表單項(xiàng)
// 準(zhǔn)備一個(gè)文件---itemRenders.js
export default {
name: 'ItemRender',
props: {
// 類型埃难,input or select or ...
type: String,
// 主要配置項(xiàng)
config: Object
},
// 渲染函數(shù)
render: function (h) {
switch (this.type) {
case 'input':
return inputItem(h, this.config);
default:
return '';
}
}
};
function inputItem(h, config) {
let {
key, // 綁定的值
props, // 屬性, 如disable等
listeners, // 事件莹弊,如onblur等
placeholder,
areaProps // 額外屬性如type等
} = config;
let on = listeners || {};
let pps = props || {};
let area = areaProps || {};
return h('el-input', {
props: {
...pps,
value: key,
disabled: pps.disabled
},
attrs: {
placeholder: placeholder || pps.placeholder || '請(qǐng)輸入',
...area
},
on: {
...on,
// 實(shí)現(xiàn)雙向綁定
input: (val) => {
key = val;
}
}
});
}
如何使用
在.vue文件中使用itemRenders.js
<template>
<div>
<item-render
:type="item.type"
:config="{
placeholder: item.placeholder || '',
...item.config,
key: item.key
}"
>
</item-render>
</div>
</template>
<script>
import ItemRender from './itemRenders';
export default {
components: { 'item-render': ItemRender },
data() {
return {
item: {
type: 'input',
key: 'inputValue',
label: 'input框:',
placeholder: '請(qǐng)輸入',
config: {
props: {
disabled: true
},
listeners: {
blur: _this.blur1
}
}
}
}
}
}
</script>
這只是渲染一個(gè)input的寫法,但既然會(huì)用到這種寫法涡尘,那就代表要渲染的表單數(shù)量可能有點(diǎn)大忍弛,像我們項(xiàng)目中有些頁(yè)面新增時(shí)表單項(xiàng)多達(dá)100+,所以悟衩,如果頁(yè)面只用到個(gè)位數(shù)的表單的情況剧罩,這里還是建議親親直接使用ele提供的標(biāo)簽寫法。
渲染大量表單
那渲染大量表單時(shí)要怎么配置呢座泳?
其實(shí)也很簡(jiǎn)單惠昔,套個(gè)form就行了(主要以提交form表單為主)
我們可以先來(lái)準(zhǔn)備個(gè)form表單組件,用的時(shí)候只需引入該組件即可
// formRender.vue
<template>
<el-form
ref="formRender"
:model="model"
:rules="rules"
>
<div class="block">
<el-form-item
v-for="item in items"
:key="item.key"
:label="item.label"
:prop="item.key"
>
<item-render
:type="item.type"
:config="{
placeholder: item.placeholder || '',
...item.config,
model,
key: item.key
}"
>
</item-render>
</el-form-item>
</div>
</el-form>
</template>
<script>
import ItemRender from './itemRenders';
export default {
components: { 'item-render': ItemRender },
props: {
model: {
type: Object,
default: () => {}
},
// 規(guī)則
rules: {
type: Object,
default: () => {}
},
items: {
type: Array,
default: () => []
}
},
}
</script>
項(xiàng)目中使用
<template>
<FormRender
ref="formRender"
:model="form"
:rules="basicRules"
:items="items1"
></FormRender>
</template>
<script>
import FormRender from './formRender';
export default {
components: {FormRender},
data() {
return {
form: {
formItem1: '',
formItem2: [],
formItem3: ''
},
items1: [
// 輸入框
{
type: 'input',
key: 'formItem1',
label: 'formItem1:',
placeholder: '請(qǐng)輸入',
}挑势,
// 下拉框
{
type: 'select',
key: 'formItem2',
label: 'formItem2:',
placeholder: '請(qǐng)選擇',
config: {
props: {
filterable: true,
clearable: true,
disabled: false
},
options: () => _this.formItem2s,
optionConfig: {
label: 'name',
value: 'code'
},
listeners: {
change: _this.handleChange
}
}
}
],
basicRules: {
formItem1: [ { required: true, trigger: 'blur' }]
},
formItem2s: []
}
}
}
</script>
其余表單配置項(xiàng)
這里只提供部分常用的給大家做參考镇防,大家也可以從別的角度去考慮,適合我們的并不一定適合所有人
-
下拉選擇框
function selectItem(h, config) { let { model, key, props, listeners, options, optionConfig, placeholder, syncConfig } = config; let opl = 'label'; let opv = 'value'; if (optionConfig) { if (optionConfig.label) { opl = optionConfig.label; } if (optionConfig.value) { opv = optionConfig.value; } } let opts = options(); let ops = []; if (opts) { ops = opts.map((option) => { return h('el-option', { key: option[opv], props: { label: option[opl] || option['text'], value: option[opv], disabled: option.disabled } }); }); } let on = listeners || {}; let pps = props || {}; if (syncConfig) { pps = { ...pps, ...syncConfig() }; } return h( 'el-select', { props: { placeholder, ...pps, value: model[key] }, on: { ...on, change: (val) => { model[key] = val; if (on.change) { on.change(val); } } } }, ops ); }
-
日期選擇器
function dateItem(h, config) { let { model, key, props, listeners, placeholder, type } = config; let pps = props || {}; let on = listeners || {}; return h('el-date-picker', { props: { ...pps, value: model[key] }, attrs: { placeholder: placeholder || pps.placeholder || '請(qǐng)輸入', type: type || 'date' }, on: { ...on, input: (val) => { model[key] = val; } } }); }
-
計(jì)數(shù)器
function inputNumberItem(h, config) { let { model, key, props } = config; let pps = props || {}; return h('el-input-number', { props: { ...pps, value: model[key] }, on: { change: (val) => { model[key] = val; } } }); }
-
上傳
function uploadItem(h, config) { let { model, key, props, btnProps, listeners, iconProps } = config; let pps = props || {}; let btnProp = btnProps || {}; let iconProp = iconProps || {}; let on = listeners || {}; return h( 'el-upload', { props: { ...pps, 'file-list': model[key] }, class: { upload_demo: true }, on: { ...on } }, [ h( 'el-button', { props: { ...btnProp, size: btnProp.size || 'small' } }, [ h('svg-icon', { style: iconProp.styles, props: { iconClass: iconProp.iconClass } }), '上傳' ] ) ] ); }
-
級(jí)聯(lián)選擇器
function cascaderItem(h, config) { let { model, key, props, listeners, options, optionProps } = config; let ops = options(); let on = listeners || {}; let pps = props || {}; return h('el-cascader', { props: { value: model[key], ...pps, options: ops, props: optionProps }, on: { change: (val) => { model[key] = val; if (on.change) { on.change(val); } } } }); }
-
時(shí)間選擇器
function timePicker(h, config) { let { model, key, props, listeners, placeholder, type, syncConfig } = config; let pps = props || {}; if (syncConfig) { pps = { ...pps, ...syncConfig() }; } let on = listeners || {}; return h('el-time-picker', { props: { ...pps, value: model[key] }, attrs: { placeholder: placeholder || pps.placeholder || '請(qǐng)輸入', type: type || 'date' }, on: { ...on, input: (val) => { model[key] = val; } } }); }