缘由

系统页面大部分为form+tabel的结构,系统复杂起来代码就看起来非常冗余,要修改起来也很麻烦,于是决定将表单封装起来。


结构

  1. 业务与逻辑区分

    • 新建const.js文件,处理表单逻辑
  2. 配置化

    • json对象化配置表单属性
  3. 扩展&特殊处理

    • 配置slot插槽

开始

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
<template>
<el-form
ref="form"
:id="formConfig.id"
:class="formConfig.className"
:label-width="formConfig.labelWidth || '100px'"
:size="formConfig.size || 'medium'"
:rules="formConfig.rules"
:model="formData"
>
<template
v-for="(item, index) in getConfigList()"
:key="index"
>
<el-form-item
:prop="item.value"
:label="item.label"
:label-width="item.labelWidth"
:class="item.className"
>
<!-- solt -->
<template v-if="item.type === 'slot'">
<slot :name="'form-' + item.value" />
</template>
<component
:is="item.type"
v-model="formData[item.value]"
:type="item.dataType"
:picker-options="item.timePickerOptions"
:clearable="item.clearable"
:disabled="item.disabled"
:start-placeholder="item.startPlaceholder"
:end-placeholder="item.endPlaceholder"
:range-separator="item.rangeSeparator || '-'"
:filterable="item.filterable"
:multiple="item.multiple"
:collapse-tags="item.collapseTags"
default-first-option
:allow-create="item.allowCreate"
:no-data-text="item.noDataText || ''"
:placeholder="getPlaceholder(item)"
:value-format="item.valueFormat"
:format="item.valueFormat"
:style="{ width: item.width || '100%' }"
:maxlength="item.maxLength"
:autosize="item.autosize || { minRows: 2, maxRows: 10 }"
@click="handleEvent(item.event, formData[item.value], 'click')"
@change="handleEvent(item.event, formData[item.value], 'change')"
@input="handleEvent(item.event, formData[item.value], 'input')"
@blur="handleEvent(item.event, formData[item.value], 'blur')"
>
<template v-if="item.type === 'el-select'">
<el-option
v-for="(childItem, childIndex) in listTypeInfo[item.list]"
:key="`${childItem.value}${childIndex}`"
:label="childItem.label"
:value="childItem.value"
></el-option>
</template>
{{ item.title || '' }}
</component>
</el-form-item>
</template>
</el-form>
</template>
<script>

export default {
name: 'FormItemComp',
data() {
return { formData: {} };
},
props: {
// 表单配置项
formConfig: {
type: Object,
default: () => {},
},
// 表单数据
data: { type: Object },
// 下拉选项list
listTypeInfo: { type: Object },
// ref
refObj: { type: Object },
},
watch: {
data: {
handler(val) {
if (!val) return;
this.formData = val;
// 将form实例返回到父级
this.$emit('update:refObj', this.$refs.form);
},
deep: true,
},
},
mounted() {
this.formData = this.data;
// 将form实例返回到父级
this.$emit('update:refObj', this.$refs.form);
},
methods: {
// 获取字段列表
getConfigList() {
return this.formConfig.fieldList.filter(
(item) => !Object.prototype.hasOwnProperty.call(item, 'show')
|| (Object.prototype.hasOwnProperty.call(item, 'show') && item.show),
);
},
// 得到placeholder的显示
getPlaceholder(row) {
let placeholder;
const types = ['el-select', 'el-date-picker'];
if (row.type === 'el-input') {
placeholder = `请输入${row.placeholder || ''}`;
} else if (types.includes(row.type)) {
placeholder = `请选择${row.placeholder || ''}`;
} else {
placeholder = row.label;
}
return placeholder;
},
// 表单事件派发
handleEvent(event, data, type) {
this.$emit('handleEvent', { event, data, type });
},
},
};
</script>


使用

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
// const.js
export const FormJson = {
id: 'form',
rules: {},
labelWidth: '100px',
size: 'mini',
className: 'el-form-component',
fieldList: [
{
label: '名称:',
value: 'name',
type: 'el-select',
clearable: true,
list: 'nameList',
},
{
label: '来源:',
value: 'source',
type: 'el-select',
clearable: true,
list: 'formSelect1',
},
{
label: '主体:',
value: 'subject',
type: 'el-input',
},
{
label: '分类:',
value: 'nature',
type: 'el-select',
clearable: true,
list: 'formSelect2',
},
{
label: '日期:',
value: 'date',
type: 'el-date-picker',
dataType: 'daterange',
valueFormat: 'yyyy-MM-dd',
startPlaceholder: '开始日期',
endPlaceholder: '结束日期',
rangeSeparator: '至',
clearable: true,
},
{
label: '状态:',
value: 'status',
type: 'el-select',
clearable: true,
list: 'projectStatus',
},
// slot插槽
{
label: '',
value: 'btn',
type: 'slot',
colWidth: 18,
className: 'text-right',
},
],
};

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
<template>
<FormItemComp
:formConfig="FormJson"
:refObj.sync="formRef"
:data="formData"
:listTypeInfo="listTypeInfo"
@handleEvent="handleEvent"
>
<template slot="form-btn">
<div>
<el-button type="search" @click="">查询</el-button>
<el-button type="reset" @click="">重置</el-button>
</div>
</template>
</FormItemComp>
</template>

<script>
import FormItemComp from './FormItemComp'
import { FormJson } from './const.js'
export default {
data(){
return {
FormJson,
formRef: null, // form ref
formData: {
name: '',
source: '',
subject: '',
nature: '',
date: '',
status: '',
},
// el-select选项
listTypeInfo: {
formSelect1:[],
formSelect2:[],
projectStatus:[],
nameList:[],
}

}
},
components: { FormItemComp },

methods: {
// 表单的相关事件
handleEvent(val) {

}
}
</script>