ProFormGroup 分组表单
ProFormGroup 分组表单组件是在 超级表单 的基础上,添加了分组功能,因此完全兼容这些组件的所有 Props 配置、Emit 事件、Slots 插槽。
基础用法
vue
<script setup lang="ts">
import type { ElFormProps } from "@/components/pro/form";
import type { FormItemColumnProps } from "@/components/pro/form-item";
import type { FormGroupColumn } from "@/components/pro/form-group";
import { ref } from "vue";
import { CreditCard, Calendar, Soccer } from "@element-plus/icons-vue";
import { ProFormGroup } from "@/components/pro/form-group";
const state = ref({
status: "0",
name: "",
rate: 4,
progress: 100,
switch: true,
time: new Date().toString(),
});
const elFormProps: Partial<ElFormProps> = {
rules: {
name: [{ required: true, message: "请输入名称" }],
tag: [{ required: true, message: "请输入标签" }],
},
};
const columns: FormGroupColumn[] = [
{
title: "第一分组",
icon: CreditCard,
columns: [
{
label: "名称",
prop: "name",
tooltip: "名称最多显示6个字符",
},
{
label: "状态",
prop: "status",
el: "el-select",
options: [
{ label: "未解决", value: "0" },
{ label: "已解决", value: "1" },
{ label: "解决中", value: "2" },
{ label: "失败", value: "3" },
],
},
],
},
{
title: "第二分组",
icon: Calendar,
columns: [
{ label: "标签", prop: "tag" },
{ label: "执行进度", prop: "progress" },
{ label: "评分", prop: "rate", el: "el-rate" },
{ label: "是否显示", prop: "switch", el: "el-switch" },
],
},
{
title: "第三分组",
icon: Soccer,
columns: [
{ label: "时间", prop: "time", el: "el-date-picker" },
{ label: "数量", prop: "number", el: "el-input-number", elProps: { precision: 2, step: 2 } },
{
label: "城市",
prop: "city",
el: "el-cascader",
options: [
{
value: "0",
label: "陕西",
children: [
{
value: "0-0",
label: "西安",
children: [
{ value: "0-0-0", label: "新城区" },
{ value: "0-0-1", label: "高新区" },
{ value: "0-0-2", label: "灞桥区" },
],
},
],
},
{
value: "1",
label: "山西",
children: [
{
value: "1-0",
label: "太原",
children: [
{ value: "1-0-0", label: "小店区" },
{ value: "1-0-1", label: "古交市" },
{ value: "1-0-2", label: "万柏林区" },
],
},
],
},
],
},
{
label: "地区",
prop: "place",
tooltip: "请精确到门牌号",
elProps: {
placeholder: "请精确到门牌号",
},
},
{
label: "经度",
prop: "lng",
tooltip: "请保留两位小数",
},
{
label: "纬度",
prop: "lat",
tooltip: "请保留两位小数",
},
{
label: "要求",
prop: "demand",
el: "el-checkbox",
options: [
{ label: "四六级", value: "0" },
{ label: "计算机二级证书", value: "1" },
{ label: "普通话证书", value: "2" },
],
},
{
label: "梦想",
prop: "gift",
el: "el-radio",
options: [
{ label: "诗", value: "0" },
{ label: "远方", value: "1" },
{ label: "美食", value: "2" },
],
},
{
label: "到期时间",
prop: "endTime",
el: "el-date-picker",
elProps: {
type: "datetimerange",
startPlaceholder: "请选择开始时间",
endPlaceholder: "请选择结束时间",
},
},
{ label: "奖励", prop: "price" },
{ label: "提成", prop: "percentage" },
{
label: "说明",
prop: "desc",
el: "el-input",
elProps: {
type: "textarea",
maxlength: 10,
showWordLimit: true,
autosize: { minRows: 2, maxRows: 4 },
},
},
],
},
];
const handleChange = (value: unknown, model: Record<string, any>, column: FormItemColumnProps) => {
console.log(value, model, column.prop, "change");
};
const handleSubmit = (model: Record<string, any>) => {
console.log(model, "Submit");
};
const handleSubmitError = (err: any) => {
console.log(err, "err");
};
const handleReset = (model: Record<string, any>) => {
console.log(model, "handleReset");
};
</script>
<template>
<ProFormGroup
v-model="state"
:columns
:el-form-props
@change="handleChange"
@submit="handleSubmit"
@submit-error="handleSubmitError"
@reset="handleReset"
/>
</template>
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
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
隐藏源代码
动态隐藏
通过 hidden 配置项,可以动态隐藏表单。
vue
<script setup lang="ts">
import type { ElFormProps } from "@/components/pro/form";
import type { FormItemColumnProps } from "@/components/pro/form-item";
import type { FormGroupColumn } from "@/components/pro/form-group";
import { computed, ref } from "vue";
import { CreditCard, Calendar, Soccer } from "@element-plus/icons-vue";
import { ProFormGroup } from "@/components/pro/form-group";
const state = ref({
status: "0",
name: "",
rate: 4,
progress: 100,
switch: true,
time: new Date().toString(),
});
const hidden = ref(false);
const elFormProps: Partial<ElFormProps> = {
rules: {
name: [{ required: true, message: "请输入名称" }],
tag: [{ required: true, message: "请输入标签" }],
},
};
const columns: FormGroupColumn[] = [
{
title: "第一分组",
icon: CreditCard,
hidden: true,
columns: [
{
label: "名称",
prop: "name",
tooltip: "名称最多显示6个字符",
},
{
label: "状态",
prop: "status",
el: "el-select",
options: [
{ label: "未解决", value: "0" },
{ label: "已解决", value: "1" },
{ label: "解决中", value: "2" },
{ label: "失败", value: "3" },
],
},
],
},
{
title: "第二分组",
icon: Calendar,
hidden: computed(() => hidden.value),
columns: [
{ label: "标签", prop: "tag" },
{ label: "执行进度", prop: "progress" },
{ label: "评分", prop: "rate", el: "el-rate" },
{ label: "是否显示", prop: "switch", el: "el-switch" },
],
},
{
title: "第三分组",
icon: Soccer,
hidden: () => false,
columns: [
{ label: "时间", prop: "time", el: "el-date-picker" },
{ label: "数量", prop: "number", el: "el-input-number", elProps: { precision: 2, step: 2 } },
{
label: "城市",
prop: "city",
el: "el-cascader",
options: [
{
value: "0",
label: "陕西",
children: [
{
value: "0-0",
label: "西安",
children: [
{ value: "0-0-0", label: "新城区" },
{ value: "0-0-1", label: "高新区" },
{ value: "0-0-2", label: "灞桥区" },
],
},
],
},
{
value: "1",
label: "山西",
children: [
{
value: "1-0",
label: "太原",
children: [
{ value: "1-0-0", label: "小店区" },
{ value: "1-0-1", label: "古交市" },
{ value: "1-0-2", label: "万柏林区" },
],
},
],
},
],
},
{
label: "地区",
prop: "place",
tooltip: "请精确到门牌号",
elProps: {
placeholder: "请精确到门牌号",
},
},
{
label: "经度",
prop: "lng",
tooltip: "请保留两位小数",
},
{
label: "纬度",
prop: "lat",
tooltip: "请保留两位小数",
},
{
label: "要求",
prop: "demand",
el: "el-checkbox",
options: [
{ label: "四六级", value: "0" },
{ label: "计算机二级证书", value: "1" },
{ label: "普通话证书", value: "2" },
],
},
{
label: "梦想",
prop: "gift",
el: "el-radio",
options: [
{ label: "诗", value: "0" },
{ label: "远方", value: "1" },
{ label: "美食", value: "2" },
],
},
{
label: "到期时间",
prop: "endTime",
el: "el-date-picker",
elProps: {
type: "datetimerange",
startPlaceholder: "请选择开始时间",
endPlaceholder: "请选择结束时间",
},
},
{ label: "奖励", prop: "price" },
{ label: "提成", prop: "percentage" },
{
label: "说明",
prop: "desc",
el: "el-input",
elProps: {
type: "textarea",
maxlength: 10,
showWordLimit: true,
autosize: { minRows: 2, maxRows: 4 },
},
},
],
},
];
const handleChange = (value: unknown, model: Record<string, any>, column: FormItemColumnProps) => {
console.log(value, model, column.prop, "change");
};
const handleSubmit = (model: Record<string, any>) => {
console.log(model, "Submit");
};
const handleSubmitError = (err: any) => {
console.log(err, "err");
};
const handleReset = (model: Record<string, any>) => {
console.log(model, "handleReset");
};
</script>
<template>
<el-button type="primary" plain style="margin-bottom: 10px" @click="hidden = !hidden">
{{ `${hidden ? "显示" : "隐藏"}第二分组表单` }}
</el-button>
<ProFormGroup
v-model="state"
:columns
:el-form-props
@change="handleChange"
@submit="handleSubmit"
@submit-error="handleSubmitError"
@reset="handleReset"
/>
</template>
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
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
隐藏源代码
插槽
- 通过
header插槽,可以自定义表单头部内容 - 通过设置每个分组卡片的
prop来生成对应值的插槽,可以自定义每一步内容渲染。 - 通过设置每一个表单项的
prop来生成对应值的插槽,可以自定义表单组件渲染。
如果想完全重新基于 ProForm 重新生成每一个分组卡片的内容,则使用 form-main 插槽(这里不演示)。
vue
<script setup lang="ts">
import type { ElFormProps } from "@/components/pro/form";
import type { FormItemColumnProps } from "@/components/pro/form-item";
import type { FormGroupColumn } from "@/components/pro/form-group";
import { ref } from "vue";
import { CreditCard, Calendar, Soccer } from "@element-plus/icons-vue";
import { ProFormGroup } from "@/components/pro/form-group";
const state = ref({
status: "0",
name: "",
rate: 4,
progress: 100,
switch: true,
time: new Date().toString(),
});
const elFormProps: Partial<ElFormProps> = {
rules: {
name: [{ required: true, message: "请输入名称" }],
tag: [{ required: true, message: "请输入标签" }],
},
};
const columns: FormGroupColumn[] = [
{
title: "第一分组",
icon: CreditCard,
prop: "first",
columns: [
{
label: "名称",
prop: "name",
tooltip: "名称最多显示6个字符",
},
{
label: "状态",
prop: "status",
el: "el-select",
options: [
{ label: "未解决", value: "0" },
{ label: "已解决", value: "1" },
{ label: "解决中", value: "2" },
{ label: "失败", value: "3" },
],
},
],
},
{
title: "第二分组",
icon: Calendar,
columns: [
{ label: "标签", prop: "tag" },
{ label: "执行进度", prop: "progress" },
{ label: "评分", prop: "rate", el: "el-rate" },
{ label: "是否显示", prop: "switch", el: "el-switch" },
],
},
{
title: "第三分组",
icon: Soccer,
columns: [
{ label: "时间", prop: "time", el: "el-date-picker" },
{ label: "数量", prop: "number", el: "el-input-number", elProps: { precision: 2, step: 2 } },
{
label: "城市",
prop: "city",
el: "el-cascader",
options: [
{
value: "0",
label: "陕西",
children: [
{
value: "0-0",
label: "西安",
children: [
{ value: "0-0-0", label: "新城区" },
{ value: "0-0-1", label: "高新区" },
{ value: "0-0-2", label: "灞桥区" },
],
},
],
},
{
value: "1",
label: "山西",
children: [
{
value: "1-0",
label: "太原",
children: [
{ value: "1-0-0", label: "小店区" },
{ value: "1-0-1", label: "古交市" },
{ value: "1-0-2", label: "万柏林区" },
],
},
],
},
],
},
{
label: "地区",
prop: "place",
tooltip: "请精确到门牌号",
elProps: {
placeholder: "请精确到门牌号",
},
},
{
label: "经度",
prop: "lng",
tooltip: "请保留两位小数",
},
{
label: "纬度",
prop: "lat",
tooltip: "请保留两位小数",
},
{
label: "要求",
prop: "demand",
el: "el-checkbox",
options: [
{ label: "四六级", value: "0" },
{ label: "计算机二级证书", value: "1" },
{ label: "普通话证书", value: "2" },
],
},
{
label: "梦想",
prop: "gift",
el: "el-radio",
options: [
{ label: "诗", value: "0" },
{ label: "远方", value: "1" },
{ label: "美食", value: "2" },
],
},
{
label: "到期时间",
prop: "endTime",
el: "el-date-picker",
elProps: {
type: "datetimerange",
startPlaceholder: "请选择开始时间",
endPlaceholder: "请选择结束时间",
},
},
{ label: "奖励", prop: "price" },
{ label: "提成", prop: "percentage" },
{
label: "说明",
prop: "desc",
el: "el-input",
elProps: {
type: "textarea",
maxlength: 10,
showWordLimit: true,
autosize: { minRows: 2, maxRows: 4 },
},
},
],
},
];
const handleChange = (value: unknown, model: Record<string, any>, column: FormItemColumnProps) => {
console.log(value, model, column.prop, "change");
};
const handleSubmit = (model: Record<string, any>) => {
console.log(model, "Submit");
};
const handleSubmitError = (err: any) => {
console.log(err, "err");
};
const handleReset = (model: Record<string, any>) => {
console.log(model, "handleReset");
};
</script>
<template>
<ProFormGroup
v-model="state"
:columns
:el-form-props
@change="handleChange"
@submit="handleSubmit"
@submit-error="handleSubmitError"
@reset="handleReset"
>
<template #header="{ title, icon }">
<div class="custom-header">
<span>
{{ title }}
<el-icon><component :is="icon" /></el-icon>
</span>
<el-button type="primary">添加</el-button>
</div>
</template>
<template #first="scoped">
我是一个【{{ scoped.index }}】 自定义 {{ scoped.title }} 内容 【{{ scoped.prop }}】
</template>
<template #tag="{ value }">我是一个表单组件插槽 {{ value }}</template>
</ProFormGroup>
</template>
<style lang="scss" scoped>
.custom-header {
display: flex;
align-items: center;
color: var(--el-color-primary);
justify-content: space-between;
.el-icon {
margin-right: 5px;
}
}
</style>
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
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
隐藏源代码
Attributes
| 属性名 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| model-value / v-model | 表单绑定值 | object | |
| columns | 分组表单配置信息 | array FormGroupColumn[] | |
| cardProps | el-card 的 Props | object CardProps | |
| ... | 其他扩展属性,支持所有 ProForm Props | ... | ... |
FormGroupColumn 配置项
| 属性名 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| title | 卡片标题 | string | |
| prop | 当前分组表单唯一标识,用于生成插槽 | string | |
| icon | el-icon 组件的图标 | object | |
| cardProps | el-card 的 Props,优先级高于整体的 cardProps | object CardProps | |
| hidden | 是否隐藏分组,支持响应式 | boolean | |
| columns | 超级表单列配置 | array FormColumn |
Event
| 名称 | 说明 | 类型 |
|---|---|---|
| ... | 其他扩展属性,支持所有 ProForm Events | ... |
Slots
| 插槽名 | 说明 | 作用域插槽参数 |
|---|---|---|
| header | ElCard 的 header 插槽 | |
| 'prop' | 分组表单自定义每一步内容,自动根据 prop 生成对应的插槽 | |
| ... | 其他扩展属性,支持所有 ProForm Slots | ... |
Exposes
| 名称 | 说明 | 类型 |
|---|---|---|
| model | 表单数据 | object |
| setValues | 设置 model 的值 | function |
| setProps | 设置 ProForm 组件的 props | function |
| setColumn | 设置 column | function |
| addColumn | 添加 column | function |
| delColumn | 删除 column | function |
| submitForm | 点击提交按钮校验通过触发的事件 | function |
| resetForm | 点击重置按钮触发的事件 | function |
| getOptionsMap | 获取所有 options 缓存数据 | function |
| getProFormInstance | 获取 ProForm 实例 | object |
| getProFormMainInstance | 获取 FormMain 实例 | object |
| getElFormInstance | 获取 ElForm 实例 | object |
| getElFormItemInstance | 获取指定 prop 的 ElFormItem 实例 | function |
| getElInstance | 获取表单组件实例 | function |