发布于 

el-form 表单组件封装

缘由

公司后台管理表单页过多,且大部分都是普通 input select 框,重复度过高,为了避免无用劳动则对 form 进行封装

BasicForm组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
<template>
<el-form
ref="BasicForm"
class="page-form"
:model="data"
:rules="rules"
:label-position="labelPosition"
:label-width="labelWidth"
>
<template v-for="(item, index) in getFieldList()">
<el-form-item
v-if="!item.hide"
:key="index"
:prop="item.value"
:label="item.label"
:class="item.className"
:style="{ width: `calc(100%/${count} - 50px)` }"
>
<!-- solt -->
<template v-if="item.type === 'slot'">
<slot :name="'form-' + item.value" />
</template>
<!-- 普通输入框 -->
<el-input
v-if="item.type === 'input' || item.type === 'password'"
v-model="data[item.value]"
clearable
autocomplete="new-password"
:size="formSize"
:maxlength="item.maxlength || 50"
:show-password="item.type === 'password'"
:type="item.type"
:disabled="item.disabled"
:placeholder="getPlaceholder(item)"
@blur="handleEvent($event, item.value)"
/>
<!-- 文本输入框 -->
<el-input
v-if="item.type === 'textarea'"
v-model="data[item.value]"
:size="formSize"
:maxlength="item.maxlength || 50"
:type="item.type"
:disabled="item.disabled"
:placeholder="getPlaceholder(item)"
:autosize="item.autosize || { minRows: 2, maxRows: 10 }"
@blur="handleEvent($event, item.value)"
/>
<!-- 计数器 -->
<el-input-number
v-if="item.type === 'inputNumber'"
v-model="data[item.value]"
:size="formSize"
v-bind="item"
@change="handleEvent($event, item.value, 'change')"
/>
<!-- 选择框 -->
<el-select
v-if="item.type === 'select'"
v-model="data[item.value]"
:size="formSize"
v-bind="item"
:placeholder="getPlaceholder(item)"
@change="handleEvent($event, item.value, 'change')"
>
<el-option
v-for="childItem in selectOption[item.list]"
:key="childItem.id"
:label="childItem.name"
:value="childItem.id"
/>
</el-select>
<!-- 日期选择框 -->
<el-date-picker
v-if="item.type === 'date'"
v-model="data[item.value]"
v-bind="item"
:type="item.dateType"
size="mini"
style="width: 100%;"
start-placeholder="开始"
end-placeholder="结束"
:placeholder="getPlaceholder(item)"
@change="handleEvent($event, item.value, 'change')"
/>
</el-form-item>
</template>
</el-form>
</template>
<script>

export default {
name: 'BasicForm',

data() {
return {

}
},

props: {
/** 实例 */
refObj: {
type: Object,
default: () => ({}),
},
/** 表单数据 */
data: {
type: Object,
default: () => ({}),
},
/** 验证规则 */
rules: {
type: Object,
default: () => ({}),
},
/** 表单相关字段配置 */
fieldList: {
type: Array,
default: () => [],
},
/** 下拉列表配置 */
selectOption: {
type: Object,
default: () => ({}),
},
/** label 宽度 */
labelWidth: {
type: String,
default: '120px',
},
/** label 位置 */
labelPosition: {
type: String,
default: 'right',
},
/** 全部禁用 */
allDisabled: {
type: String,
default: null,
},
/** 列数量 */
count: {
type: [Number, String],
default: 1,
},
/** 大小 */
formSize: {
type: String,
default: 'mini',
},
},

watch: {
data: {
handler() {
this.$emit('update:refObj', this.$refs.BasicForm);
},
deep: true,
},
},

mounted() {
this.$emit('update:refObj', this.$refs.BasicForm);
},

methods: {
getFieldList() {
let resolveList = JSON.parse(JSON.stringify(this.fieldList))
// console.log('resolveList',resolveList);
return resolveList
},
/**
* @method 判断placeholder显示内容
* @param {Object} row
* @returns {String} placeholder
* @desc 📝默认显示内容提示
*/
getPlaceholder(row) {
let placeholder;
if (
row.type === 'input' ||
row.type === 'textarea' ||
row.type === 'password'
) {
placeholder = '请输入' + row.label;
} else if (
row.type === 'select' ||
row.type === 'time' ||
row.type === 'date'
) {
placeholder = '请选择' + row.label;
} else {
placeholder = row.label;
}
return placeholder;
},
/**
* @method 绑定相关事件
* @param {Event} event
* @param {String | Number} val
* @param {String} change
* @desc 📝事件处理, 当前form项失去焦点时, 获取 value 值和 字段名称
* @desc 📝change 事件特殊处理: change 只能拿到选中值. 此时的 event 为选中值的 value
*/
handleEvent(event, val, change) {
let obj = {
value: change === 'change' ? event : event.target.value,
label: val,
};
this.$emit('handleEvent', obj);
},
},
};
</script>
<style lang="scss" scoped>
.el-form-item {
margin: 0 10px 20px 10px;
display: inline-block;
.el-form-item__content {
.el-input,
.el-select,
.el-textarea {
width: 100%;
}
.el-input-number {
.el-input {
width: inherit;
}
}
}
.el-form-block {
display: block;
width: 100%;
.el-form-item__content {
.el-input,
.el-select,
.el-textarea {
width: 100%;
}
}
}
}
</style>

使用

引用 注册 form 组件

1
2
3
4
5
import BasicForm from '@/components/BasicForm';

components: {
BasicForm,
},

在模板中使用

1
2
3
4
5
6
7
8
9
10
11
12
13
<BasicForm
v-loading="loading"
element-loading-text="拼命加载中..."
element-loading-background="#FFFFFF"
:ref-obj.sync="formInfo.ref"
:data="formInfo.data"
:rules="formInfo.rules"
:fieldList="formInfo.fieldList"
:selectOption="formInfo.selectOption"
count="3"
labelWidth="150px"
@handleEvent="handleEvent"
/>

数据结构

组件 json 数据格式,为了不混淆统一在 formInfo 这个大对象里

1
2
3
4
5
6
7
formInfo: {
ref: null,
data: {},
selectOption: {},
fieldList: [],
rules: {}
}
  • ref 组件实例
  • data 表单数据
  • selectOption 下拉列表对象,里面存放下拉框数组,有几个就放几个(后端返回的下拉列表数据)
  • fieldList 字段类型、label列表
  • rules 表单验证规则

如果 fieldList 列表里 type 类型是 select,则 list 字段的 value 就是 selectOption 对象所对应的数组列表

1
2
3
fieldList: [
{ label: '通道选择', type: 'select', value: 'passId', list: 'passOptions', hide: false },
]

数据结构示例

关于表单校验规则封装可以看上一篇文章: el-form 表单校验规则封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
data() {
return {
formInfo: {
/** 表单组件实例 */
ref: null,
/** 表单数据 */
data: {
id: '',
name: '',
code: '',
passQuantity: '',
ipAddress: '',
macAddress: '',
model: '',
manufacturer: '',
location: '',
backupOften: ''
},
/** 下拉列表参数 */
selectOption: {
passOptions: [],
categoryOptions: [],
},
/** 字段类型、label列表 */
fieldList: [
{ label: '主机名', type: 'input', value: 'name' },
{ label: '通道选择', type: 'select', value: 'passId', list: 'passOptions', hide: false },
{ label: '传感器类型', type: 'select', value: 'categoryId', list: 'categoryOptions', hide: false },
{ label: '主机编号', type: 'input', value: 'code'},
{ label: '通道数量', type: 'input', value: 'passQuantity' },
{ label: 'IP地址', type: 'input', value: 'ipAddress' },
{ label: 'Mac地址', type: 'input', value: 'macAddress' },
{ label: '型号', type: 'input', value: 'model' },
{ label: '生产厂家', type: 'input', value: 'manufacturer' },
{ label: '存放地址', type: 'input', value: 'location' },
{ label: '备份频率', type: 'input', value: 'backupOften' },
],
/** 表单规则 */
rules: {
name: vxRule(),
categoryId: vxRule(true, null, 'change', '请选择传感器类型'),
distance: vxRule(),
code: vxRule(),
passQuantity: vxRule(true, "Number"),
ipAddress: vxRule(true, "IpAddress"),
macAddress: vxRule(true, "MacAddress"),
backupOften: vxRule(),
},
},
}
},

事件函数

该组件对外暴露 handleEvent() 事件函数,可以操纵每个 form-item,用来自定义事件。

1
2
3
handleEvent (row) {
console.log('表单绑定相关事件:',row); // 表单绑定相关事件row: {value: "安徽蓝海之光模拟巷道", label: "location"}
},

结语

无论是十五岁,三十岁,四十岁,抑或五十岁,人们都为同样的事愤怒,为同样的事欢笑振奋;同样狡猾,同样软弱,卑微