表单基础
基础用法
传入 columns 即可快速构建一个表单。
<script lang="ts" setup>
import type { FormValidateCallback } from "element-plus";
import type { ElFormProps, FormColumn } from "@/components/pro/form";
import type { FormItemColumnProps } from "@/components/pro/form-item";
import { ref, computed } from "vue";
import { ProForm } from "@/components/pro/form";
const state = ref({
status: "0",
name: "",
rate: 4,
progress: 100,
switch: true,
time: new Date().toString(),
endTime: [],
img: "https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg",
});
const elFormProps: Partial<ElFormProps> = {
rules: {
name: [{ required: true, message: "请输入名称" }],
tag: [{ required: true, message: "请输入标签" }],
},
};
const columns: FormColumn[] = [
{
label: "名称",
prop: "name",
tooltip: computed(() => (state.value.name as string) || "提示:复制名称"),
},
{
label: "状态",
prop: "status",
el: "el-select",
options: [
{ label: "未解决", value: "0" },
{ label: "已解决", value: "1" },
{ label: "解决中", value: "2" },
{ label: "失败", value: "3" },
],
},
{ label: "标签", prop: "tag" },
{ label: "执行进度", prop: "progress" },
{ label: "评分", prop: "rate", el: "el-rate" },
{ label: "是否显示", prop: "switch", el: "el-switch" },
{ label: "图片", prop: "img" },
{ 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: "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: "desc",
el: "el-input",
elProps: {
type: "textarea",
maxlength: 10,
showWordLimit: true,
autosize: { minRows: 2, maxRows: 4 },
},
},
];
const handleChange = (values: unknown, model: Record<string, any>, column: FormItemColumnProps) => {
console.log(values, model, column, "change");
};
const handleSubmit = (model: Record<string, any>) => {
console.log(model, "Submit");
};
const handleSubmitError = (err: Parameters<FormValidateCallback>[1]) => {
console.log(err, "err");
};
const handleReset = () => {
console.log("handleReset");
};
</script>
<template>
<ProForm
v-model="state"
:columns
:el-form-props
@change="handleChange"
@submit="handleSubmit"
@submit-error="handleSubmitError"
@reset="handleReset"
/>
</template>
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
自定义排版
每一个表单默认是使用 ElRow 和 ElCol 组件进行排版的。
可以通过 rowProps 和 colProps 自定义全局排版,其中每个 column 都支持 colProps 配置项,它将覆盖全局的 colProps 配置项。
<script lang="ts" setup>
import type { FormValidateCallback } from "element-plus";
import type { ElFormProps, FormColumn } from "@/components/pro/form";
import type { FormItemColumnProps } from "@/components/pro/form-item";
import { ref, computed } from "vue";
import { ProForm } from "@/components/pro/form";
const state = ref({
status: "0",
name: "",
rate: 4,
progress: 100,
switch: true,
time: new Date().toString(),
endTime: [],
img: "https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg",
});
const elFormProps: Partial<ElFormProps> = {
rules: {
name: [{ required: true, message: "请输入名称" }],
tag: [{ required: true, message: "请输入标签" }],
},
};
const columns: FormColumn[] = [
{
label: "名称",
prop: "name",
tooltip: computed(() => (state.value.name as string) || "提示:复制名称"),
},
{
label: "状态",
prop: "status",
el: "el-select",
options: [
{ label: "未解决", value: "0" },
{ label: "已解决", value: "1" },
{ label: "解决中", value: "2" },
{ label: "失败", value: "3" },
],
},
{ label: "标签", prop: "tag" },
{ prop: "empty", el: "empty" },
{ label: "执行进度", prop: "progress" },
{ label: "评分", prop: "rate", el: "el-rate" },
{ label: "是否显示", prop: "switch", el: "el-switch" },
{
label: "图片",
prop: "img",
colProps: { span: 24 },
},
{ label: "时间", prop: "time", el: "el-date-picker" },
{ prop: "empty", el: "empty" },
{
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: "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: "desc",
el: "el-input",
elProps: {
type: "textarea",
maxlength: 10,
showWordLimit: true,
autosize: { minRows: 2, maxRows: 4 },
},
colProps: { span: 24 },
},
];
const flexLayout = ref(true);
const handleChange = (values: unknown, model: Record<string, any>, column: FormItemColumnProps) => {
console.log(values, model, column, "change");
};
const handleSubmit = (model: Record<string, any>) => {
console.log(model, "Submit");
};
const handleSubmitError = (err: Parameters<FormValidateCallback>[1]) => {
console.log(err, "err");
};
const handleReset = () => {
console.log("handleReset");
};
</script>
<template>
<el-button type="primary" plain @click="flexLayout = !flexLayout" style="margin-bottom: 20px">
{{ `${flexLayout ? "取消" : "使用"} Flex 布局` }}
</el-button>
<ProForm
v-model="state"
:columns
:el-form-props
:flex-layout
:row-props="{ gutter: 40 }"
:col-props="{ span: 12 }"
@change="handleChange"
@submit="handleSubmit"
@submit-error="handleSubmitError"
@reset="handleReset"
/>
</template>
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
多级显示
columns 的 prop 支持 x.y.z 形式的 多(无限)级数据形式。
<script lang="ts" setup>
import type { ElFormProps, FormColumn } from "@/components/pro/form";
import type { FormItemColumnProps } from "@/components/pro/form-item";
import { h, ref } from "vue";
import { ProForm } from "@/components/pro/form";
const state = ref({
a: {
b: {
c: "我是一段有想法的文本",
e: "3",
f: ["0", "3"],
},
},
});
const elFormProps: Partial<ElFormProps> = {
rules: {
"a.b.c": [{ required: true, message: "请输入名称" }],
"a.b.d": [{ required: true, message: "请输入标签" }],
},
};
const columns: FormColumn[] = [
{
label: "文本",
prop: "a.b.c",
render: scope => h("span", {}, { default: () => scope.value + " - 自定义" }),
},
{ label: "name", prop: "a.b.d" },
{
label: "tag",
prop: "a.b.e",
el: "el-select",
options: [
{ label: "未解决", value: "0" },
{ label: "已解决", value: "1" },
{ label: "解决中", value: "2" },
{ label: "失败", value: "3" },
],
},
{
label: "tag1",
prop: "a.b.f",
el: "el-select",
fieldProps: {
multiple: true,
},
options: [
{ label: "未解决", value: "0" },
{ label: "已解决", value: "1" },
{ label: "解决中", value: "2" },
{ label: "失败", value: "3" },
],
},
];
const handleChange = (values: unknown, model: Record<string, any>, column: FormItemColumnProps) => {
console.log(values, model, column, "change");
};
const handleSubmit = (model: Record<string, any>) => {
console.log(model, "Submit");
};
</script>
<template>
<ProForm v-model="state" :columns="columns" :el-form-props @change="handleChange" @submit="handleSubmit" />
</template>
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
隐藏 label
表单整体的 showLabel 设置为 false 时, 隐藏 label。配置 columns 中的 showLabel 可以控制单个表单项的 label 是否显示,优先级会更高。
<script lang="ts" setup>
import type { FormColumn } from "@/components/pro/form";
import { ref, computed } from "vue";
import { ProForm } from "@/components/pro/form";
const state = ref({
status: "0",
name: "",
});
const columns1: FormColumn[] = [
{
label: "名称",
prop: "name",
tooltip: "名称最多显示6个字符",
// 优先级会更高
showLabel: true,
},
{
label: "名称1",
prop: "name1",
showLabel: ref(true),
},
{
label: "名称2",
prop: "name2",
showLabel: computed(() => true),
},
{
label: "状态",
prop: "status",
el: "el-select",
options: [
{ label: "未解决", value: "0" },
{ label: "已解决", value: "1" },
{ label: "解决中", value: "2" },
{ label: "失败", value: "3" },
],
},
];
const columns2: FormColumn[] = [
{
label: "名称",
prop: "name",
tooltip: "名称最多显示6个字符",
// 优先级会更高
showLabel: false,
},
{
label: "名称1",
prop: "name1",
showLabel: ref(false),
},
{
label: "名称2",
prop: "name2",
showLabel: computed(() => false),
},
{
label: "状态",
prop: "status",
el: "el-select",
options: [
{ label: "未解决", value: "0" },
{ label: "已解决", value: "1" },
{ label: "解决中", value: "2" },
{ label: "失败", value: "3" },
],
},
];
</script>
<template>
<el-card>
<ProForm v-model="state" :columns="columns1" :show-label="false" />
</el-card>
<el-card style="margin-top: 20px">
<ProForm v-model="state" :columns="columns2" />
</el-card>
</template>
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
options 字典
在 column 中配置 options 属性来给 ElSelect、ElCascader 等表单组件添加字典数据,支持数组,computed、函数和 Promise。
ProForm 内置字典缓存功能,无需担心重复调用接口问题。
如果 options 的 key 不是 label 和 value,则通过 optionField 指定 label 和 value 的字段。
<script lang="ts" setup>
import type { Ref } from "vue";
import type { ElFormProps, FormColumn } from "@/components/pro/form";
import type { ElOption } from "@/components/pro/form-item";
import { ref, computed } from "vue";
import { ProForm } from "@/components/pro/form";
const state = ref({
status: "0",
});
const elFormProps: Partial<ElFormProps> = {
rules: {
name: [{ required: true, message: "请输入名称" }],
},
};
const options: Ref<ElOption[]> = ref([]);
const getOptions = async () => {
// 等待 2s
return await new Promise(resolve => {
setTimeout(() => {
options.value = [
{ label: "未解决", value: "0" },
{ label: "已解决", value: "1" },
];
resolve("");
}, 2000);
});
};
getOptions();
const columns: FormColumn[] = [
{
label: "状态",
prop: "status",
el: "el-select",
options: computed(() => options.value),
formItemProps: () => {
return { labelWidth: "120px" };
},
},
{
label: "状态",
prop: "status",
el: "el-select",
options: async () => {
// 等待 2s
await new Promise(resolve => {
setTimeout(() => {
resolve("");
}, 2000);
});
return [
{ dictLabel: "未解决", dictValue: "0" },
{ dictLabel: "已解决", dictValue: "1" },
{ dictLabel: "解决中", dictValue: "2" },
{ dictLabel: "失败", dictValue: "3" },
];
},
optionField: { label: "dictLabel", value: "dictValue" },
formItemProps: () => {
return { labelWidth: "100px" };
},
},
{
label: "城市",
prop: "city",
el: "el-cascader",
options: async () => {
// 等待 2s
await new Promise(resolve => {
setTimeout(() => {
resolve("");
}, 2000);
});
return [
{
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: "万柏林区" },
],
},
],
},
];
},
formItemProps: () => {
return { labelWidth: "100px" };
},
},
{
label: "地区",
prop: "place",
tooltip: "请精确到门牌号",
elProps: {
placeholder: "请精确到门牌号",
},
formItemProps: () => {
return { labelWidth: "100px" };
},
},
{
label: "要求",
prop: "demand",
el: "el-checkbox",
formItemProps: () => {
return { labelWidth: "100px" };
},
options: () => {
return new Promise(resolve => {
setTimeout(() => {
resolve([
{ label: "四六级", value: "0" },
{ label: "计算机二级证书", value: "1" },
{ label: "普通话证书", value: "2" },
]);
}, 2000);
});
},
},
{
label: "要求1",
prop: "demand1",
el: "el-checkbox",
options: computed(() =>
state.value.status === "0"
? [
{ label: "四六级", value: "0" },
{ label: "计算机二级证书", value: "1" },
{ label: "普通话证书", value: "2" },
]
: [{ label: "四六级", value: "0" }]
),
},
{
label: "梦想",
prop: "gift",
el: "el-radio",
options: new Promise(resolve => {
setTimeout(() => {
resolve([
{ label: "诗", value: "0" },
{ label: "远方", value: "1" },
{ label: "美食", value: "2" },
]);
}, 2000);
}),
},
];
</script>
<template>
<ProForm v-model="state" :columns="columns" :el-form-props />
</template>
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
数据属性联动
<script lang="ts" setup>
import type { FormColumn } from "@/components/pro/form";
import { ref, computed } from "vue";
import { ProForm } from "@/components/pro/form";
const state = ref({
status: "0",
});
const columns: FormColumn[] = [
{
label: "状态",
prop: "status",
el: "el-select",
options: [
{ label: "未解决", value: "0" },
{ label: "已解决", value: "1" },
],
},
{
label: "要求1",
prop: "demand1",
el: "el-checkbox",
options: computed(() =>
state.value.status === "0"
? [
{ label: "四六级", value: "0" },
{ label: "计算机二级证书", value: "1" },
{ label: "普通话证书", value: "2" },
]
: [{ label: "四六级", value: "0" }]
),
},
{
label: "名称1",
prop: "name1",
tooltip: "名称最多显示6个字符",
fieldProps: computed(() => ({ disabled: state.value.status === "1" })),
},
{
label: "标签1",
prop: "tag1",
hidden: computed(() => state.value.status === "1"),
},
{
label: "名称2",
prop: "name2",
tooltip: "名称最多显示6个字符",
elProps: computed(() => ({ disabled: state.value.status === "1" })),
},
{
label: "标签2",
prop: "tag2",
formItemProps: computed(() => ({ required: state.value.status === "1" })),
},
];
</script>
<template>
<ProForm v-model="state" :columns="columns" />
</template>
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
v-model 修饰符的使用
对于输入框想添加 .trim,.number 和 .lazy 这些修饰符,可以配置 columns 中 elProps 的 modelModifiers 属性。
<script lang="ts" setup>
import type { FormColumn } from "@/components/pro/form";
import { ref, h } from "vue";
import { ElInput } from "element-plus";
import { ProForm } from "@/components/pro/form";
const state = ref({
autocomplete: "vue",
input: "100",
input1: "单行文本",
textarea: "多行文本",
});
interface RestaurantItem {
value: string;
link: string;
}
const restaurants = ref<RestaurantItem[]>([]);
const createFilter = (queryString: string) => {
return (restaurant: RestaurantItem) => {
return restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0;
};
};
const loadAll = () => {
return [
{ value: "vue", link: "https://github.com/vuejs/vue" },
{ value: "element", link: "https://github.com/ElemeFE/element" },
{ value: "cooking", link: "https://github.com/ElemeFE/cooking" },
{ value: "mint-ui", link: "https://github.com/ElemeFE/mint-ui" },
{ value: "vuex", link: "https://github.com/vuejs/vuex" },
{ value: "vue-router", link: "https://github.com/vuejs/vue-router" },
{ value: "babel", link: "https://github.com/babel/babel" },
];
};
restaurants.value = loadAll();
const columns: FormColumn[] = [
{
label: "autocomplete",
prop: "autocomplete",
el: "el-autocomplete",
tooltip: "自动补全输入框",
elProps: {
modelModifiers: { trim: true },
fetchSuggestions: (queryString: string, cb: any) => {
const results = queryString ? restaurants.value.filter(createFilter(queryString)) : restaurants.value;
cb(results);
},
},
},
{
label: "input",
prop: "input",
elProps: {
modelModifiers: { trim: true, number: true, lazy: true },
},
},
{
label: "input",
prop: "input1",
// elProps 会同时作用于 render 返回值的 props,优先级低于返回值的 props,建议直接写在 render 中
elProps: {
modelModifiers: { trim: true, number: false },
},
render: (value, update) => {
return h(ElInput, { modelValue: value as string, onChange: update, modelModifiers: { trim: true } });
},
},
{
label: "textarea",
prop: "textarea",
el: "el-input",
elProps: {
type: "textarea",
modelModifiers: { trim: true },
},
},
];
</script>
<template>
<ProForm v-model="state" label-width="140px" :columns="columns" />
</template>
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
width 指定宽度
通过 width 配置表单的宽度,默认为 100%。
<script lang="ts" setup>
import type { FormValidateCallback } from "element-plus";
import type { ElFormProps, FormColumn } from "@/components/pro/form";
import type { FormItemColumnProps } from "@/components/pro/form-item";
import { ref, computed } from "vue";
import { ProForm } from "@/components/pro/form";
const state = ref({
status: "0",
name: "",
rate: 4,
progress: 100,
switch: true,
time: new Date().toString(),
img: "https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg",
});
const elFormProps: Partial<ElFormProps> = {
rules: {
name: [{ required: true, message: "请输入名称" }],
tag: [{ required: true, message: "请输入标签" }],
},
};
const columns: FormColumn[] = [
{
label: "名称",
prop: "name",
width: 200,
tooltip: computed(() => (state.value.name as string) || "提示:复制名称"),
},
{
label: "状态",
prop: "status",
width: 400,
el: "el-select",
options: [
{ label: "未解决", value: "0" },
{ label: "已解决", value: "1" },
{ label: "解决中", value: "2" },
{ label: "失败", value: "3" },
],
},
{ label: "标签", prop: "tag", width: 200 },
{ label: "执行进度", prop: "progress", width: 300 },
{ label: "评分", prop: "rate", width: 400, el: "el-rate" },
{ label: "是否显示", prop: "switch", width: 500, el: "el-switch" },
{ label: "图片", prop: "img", width: 600 },
{ label: "时间", prop: "time", width: 600, el: "el-date-picker" },
{ label: "数量", prop: "number", width: 700, el: "el-input-number", elProps: { precision: 2, step: 2 } },
];
const handleChange = (values: unknown, model: Record<string, any>, column: FormItemColumnProps) => {
console.log(values, model, column, "change");
};
const handleSubmit = (model: Record<string, any>) => {
console.log(model, "Submit");
};
const handleSubmitError = (err: Parameters<FormValidateCallback>[1]) => {
console.log(err, "err");
};
const handleReset = () => {
console.log("handleReset");
};
</script>
<template>
<ProForm
v-model="state"
:columns
:el-form-props
@change="handleChange"
@submit="handleSubmit"
@submit-error="handleSubmitError"
@reset="handleReset"
/>
</template>
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
divider 分割线
当 el 为 el-divider 时,可以传入 elProps.labelSize 来指定 label 大小。
<script lang="ts" setup>
import type { FormColumn } from "@/components/pro/form";
import { ProForm } from "@/components/pro/form";
const columns: FormColumn[] = [
{
label: "Small divider",
prop: "dividerSmall",
el: "el-divider",
elProps: {
labelSize: "small",
},
},
{
label: "Default divider",
prop: "dividerDefault",
el: "el-divider",
elProps: {
labelSize: "default",
},
},
{
label: "Large divider",
prop: "dividerLarge",
el: "el-divider",
elProps: {
labelSize: "large",
},
},
];
</script>
<template>
<ProForm :columns />
</template>
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
底部按钮位置
通过 footerAlign 配置底部按钮的位置,可选值有 left、center、right。
<script lang="ts" setup>
import type { ElFormProps, FormColumn } from "@/components/pro/form";
import { ref } from "vue";
import { ProForm } from "@/components/pro/form";
const state = ref({
status: "0",
content: "",
});
const footerAlign = ref<"left" | "center" | "right">("right");
const elFormProps: Partial<ElFormProps> = {
rules: {
content: [{ required: true, message: "请输入原因" }],
},
};
const columns: FormColumn[] = [
{ label: "原因", prop: "content" },
{
label: "状态",
prop: "status",
el: "el-select",
options: [
{ label: "未解决", value: "0" },
{ label: "已解决", value: "1" },
{ label: "解决中", value: "2" },
{ label: "失败", value: "3" },
],
},
];
</script>
<template>
<el-radio-group v-model="footerAlign" style="margin-bottom: 10px">
<el-radio value="left">left</el-radio>
<el-radio value="center">center</el-radio>
<el-radio value="right">right</el-radio>
</el-radio-group>
<ProForm v-model="state" :columns="columns" :el-form-props :footer-align />
</template>
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
底部按钮插槽
通过 footer 插槽可以自定义底部按钮。
<script lang="ts" setup>
import type { ElFormProps, FormColumn } from "@/components/pro/form";
import { ref } from "vue";
import { ProForm } from "@/components/pro/form";
const state = ref({
status: "0",
content: "",
});
const submitLoading = ref(false);
const hasReset = ref(true);
const elFormProps: Partial<ElFormProps> = {
rules: {
content: [{ required: true, message: "请输入原因" }],
},
};
const columns: FormColumn[] = [
{ label: "原因", prop: "content" },
{
label: "状态",
prop: "status",
el: "el-select",
options: [
{ label: "未解决", value: "0" },
{ label: "已解决", value: "1" },
{ label: "解决中", value: "2" },
{ label: "失败", value: "3" },
],
},
];
</script>
<template>
<ProForm v-model="state" :columns="columns" :el-form-props>
<template #footer="{ handleSubmit, handleReset }">
<div style="margin: 0 auto">
<el-button type="info">返回</el-button>
<el-button type="primary" :loading="submitLoading" @click="handleSubmit">通过</el-button>
<el-button v-if="hasReset" type="warning" @click="handleReset">重置</el-button>
<el-button type="danger">驳回</el-button>
</div>
</template>
</ProForm>
</template>
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
如果您只想额外添加底部按钮,可以通过 footer-before(前) 或 footer-after(后) 插槽进行拓展。
这两个插槽可以接收三个参数,分别是:
- handleSubmit:点击提交按钮时触发的函数
- handleReset:点击重置按钮时触发的函数
- model:表单数据
Tip 图标自定义
通过 tooltip-icon 插槽,可以自定义 Tip 图标。
<script lang="ts" setup>
import type { FormColumn } from "@/components/pro/form";
import { ref } from "vue";
import { ProForm } from "@/components/pro/form";
import { Warning } from "@element-plus/icons-vue";
const state = ref({
input: "文本",
});
const columns: FormColumn[] = [
{
label: "输入框",
prop: "input",
el: "el-input",
tooltip: "自动补全输入框",
},
];
</script>
<template>
<ProForm v-model="state" label-width="100px" :columns="columns">
<template #tooltip-icon>
<el-icon :size="16" style="margin-left: 10px"><Warning /></el-icon>
</template>
</ProForm>
</template>
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
树形表单
当 el 为 tree 时,将使用 Teek 基于 ElTree 封装的树形表单,elProps 支持配置 ElTree 的所有属性。
除了 ElTree 的属性外,还支持以下属性:
checkBaseValueType:v-model返回的格式,可选值为keys、nodes,其中keys返回选中的节点nodeKey,nodes为返回选中的节点,默认keysexpandSelected:初始化时存在默认选中的节点,是否展开选中节点的所有父节点,默认falsecheckbox:是否开启工具栏,默认falsesearch:是否开启搜索功能,默认falseselect:是否开启全选/全不选功能,默认false
<script lang="ts" setup>
import type { FormColumn } from "@/components/pro/form";
import { computed, ref } from "vue";
import { ProForm } from "@/components/pro/form";
const state = ref({
tree: [1],
});
const checkbox = ref(false);
const search = ref(false);
const select = ref(true);
const checkBaseValueType = ref("keys");
const columns: FormColumn[] = [
{
label: "",
prop: "tree",
el: "tree",
elProps: computed(() => ({
checkbox: checkbox.value,
search: search.value,
select: select.value,
expandSelected: true, // 初始化时存在默认选中的节点,是否展开选中节点的所有父节点,默认 false
checkBaseValueType: checkBaseValueType.value,
})),
options: [
{
id: 1,
label: "Level one 1",
children: [
{
id: 4,
label: "Level two 1-1",
children: [
{ id: 9, label: "Level three 1-1-1" },
{ id: 10, label: "Level three 1-1-2" },
],
},
],
},
{
id: 2,
label: "Level one 2",
children: [
{ id: 5, label: "Level two 2-1" },
{ id: 6, label: "Level two 2-2" },
],
},
{
id: 3,
label: "Level one 3",
children: [
{ id: 7, label: "Level two 3-1" },
{ id: 8, label: "Level two 3-2" },
],
},
],
},
];
</script>
<template>
<div style="margin-bottom: 20px">
<el-checkbox v-model="checkbox">开启工具栏</el-checkbox>
<el-checkbox v-model="search">开启搜索功能</el-checkbox>
<el-checkbox v-model="select">开启全选/全不选功能</el-checkbox>
<el-switch
v-model="checkBaseValueType"
inline-prompt
active-text="获取 keys"
active-value="keys"
inactive-text="获取 nodes"
inactive-value="nodes"
style="margin-left: 16px; vertical-align: 4px"
/>
</div>
<el-card>
<ProForm v-model="state" :columns :show-label="false" />
{{ state }}
</el-card>
</template>
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
所有内置的 ElementPlus 表单
在项目里,除了 ElementPlus 的表单类型外,还支持以下类型:
WangEditor:富文本编辑器Tinymce:富文本编辑器IconPicker:图标选择器
<script lang="ts" setup>
import type { FormColumn } from "@/components/pro/form";
import { ref } from "vue";
import { ProForm } from "@/components/pro/form";
interface RestaurantItem {
value: string;
link: string;
}
interface Option {
key: number;
label: string;
disabled: boolean;
}
const state = ref({
autocomplete: "vue",
cascader: ["0", "0-0", "0-0-0"],
cascaderMultiple: [
["0", "0-0", "0-0-0"],
["0", "0-0", "0-0-1"],
["0", "0-0", "0-0-2"],
],
checkbox: ["0"],
colorPicker: "rgba(255, 69, 0, 0.68)",
year: "2024",
years: ["2024", "2005"],
month: "2024-02",
date: "2024-03-05",
dates: ["2024-03-05", "2024-03-06"],
datetime: "2024-03-19 00:00:00",
week: "2024-03-19",
datetimerange: ["2024-03-07 00:00:00", "2024-03-09 00:00:00"],
daterange: ["2024-02-29", "2024-03-29"],
monthrange: ["2024-03", "2024-05"],
select: "0",
selectMultiple: ["0", "1"],
input: "单行文本",
textarea: "多行文本",
inputNumber: 4,
rate: 3,
switch: true,
radio: "0",
slider: 50,
timePicker: "2024-03-18 09:55:31",
timeSelect: "09:55:31",
transfer: [1, 2, 3, 4, 5, 6],
plusDatePicker: ["2024-03-18 09:55:31", "2024-03-20 09:55:31"],
inputTag: ["tag", "tag1"],
selectV2: "Option 1",
selectV2Multiple: ["Option 1", "Option 2"],
mention: "Fuphoenixes",
segmented: "Mon",
checkboxSelect: "option1",
checkboxSelectMultiple: ["option1", "option2"],
text: "我是一段文本",
});
const generateData = () => {
const data: Option[] = [];
for (let i = 1; i <= 15; i++) {
data.push({
key: i,
label: `Option ${i}`,
disabled: i % 4 === 0,
});
}
return data;
};
const restaurants = ref<RestaurantItem[]>([]);
const createFilter = (queryString: string) => {
return (restaurant: RestaurantItem) => {
return restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0;
};
};
const loadAll = () => {
return [
{ value: "vue", link: "https://github.com/vuejs/vue" },
{ value: "element", link: "https://github.com/ElemeFE/element" },
{ value: "cooking", link: "https://github.com/ElemeFE/cooking" },
{ value: "mint-ui", link: "https://github.com/ElemeFE/mint-ui" },
{ value: "vuex", link: "https://github.com/vuejs/vuex" },
{ value: "vue-router", link: "https://github.com/vuejs/vue-router" },
{ value: "babel", link: "https://github.com/babel/babel" },
];
};
restaurants.value = loadAll();
const cascaderOptions = [
{
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: "万柏林区" },
],
},
],
},
];
const columns: FormColumn[] = [
{
label: "autocomplete",
prop: "autocomplete",
el: "el-autocomplete",
tooltip: "自动补全输入框",
elProps: {
fetchSuggestions: (queryString: string, cb: any) => {
const results = queryString ? restaurants.value.filter(createFilter(queryString)) : restaurants.value;
cb(results);
},
},
},
{
label: "cascader",
prop: "cascader",
el: "el-cascader",
options: cascaderOptions,
},
{
label: "cascader-multiple",
prop: "cascaderMultiple",
el: "el-cascader",
options: cascaderOptions,
elProps: {
multiple: true,
},
},
{
label: "checkbox",
prop: "checkbox",
el: "el-checkbox",
options: [
{ label: "四六级", value: "0" },
{ label: "计算机二级证书", value: "1" },
{ label: "普通话证书", value: "2" },
],
},
{
label: "color-picker",
prop: "colorPicker",
el: "el-color-picker",
width: 35,
},
{
label: "year",
prop: "year",
el: "el-date-picker",
elProps: {
type: "year",
},
},
{
label: "years",
prop: "years",
el: "el-date-picker",
elProps: {
type: "years",
},
},
{
label: "month",
prop: "month",
el: "el-date-picker",
elProps: {
type: "month",
},
},
{
label: "date",
prop: "date",
el: "el-date-picker",
elProps: {
type: "date",
},
},
{
label: "dates",
prop: "dates",
el: "el-date-picker",
elProps: {
type: "dates",
},
},
{
label: "datetime",
prop: "datetime",
el: "el-date-picker",
elProps: {
type: "datetime",
},
},
{
label: "week",
prop: "week",
el: "el-date-picker",
elProps: {
type: "week",
},
},
{
label: "datetimerange",
prop: "datetimerange",
el: "el-date-picker",
elProps: {
type: "datetimerange",
},
},
{
label: "daterange",
prop: "daterange",
el: "el-date-picker",
elProps: {
type: "daterange",
},
},
{
label: "monthrange",
prop: "monthrange",
el: "el-date-picker",
elProps: {
type: "monthrange",
},
},
{
label: "select",
prop: "select",
el: "el-select",
options: [
{ label: "未解决", value: "0" },
{ label: "已解决", value: "1" },
{ label: "解决中", value: "2" },
{ label: "失败", value: "3" },
],
},
{
label: "select-multiple",
prop: "selectMultiple",
el: "el-select",
options: [
{ label: "未解决", value: "0" },
{ label: "已解决", value: "1" },
{ label: "解决中", value: "2" },
{ label: "失败", value: "3" },
],
elProps: {
multiple: true,
},
},
{ label: "input", prop: "input" },
{
label: "input-number",
prop: "inputNumber",
el: "el-input-number",
elProps: { precision: 2, step: 2 },
},
{
label: "textarea",
prop: "textarea",
el: "el-input",
elProps: {
type: "textarea",
maxlength: 10,
showWordLimit: true,
autosize: { minRows: 2, maxRows: 4 },
},
},
{ label: "rate", prop: "rate", el: "el-rate" },
{ label: "switch", prop: "switch", el: "el-switch" },
{
label: "radio",
prop: "radio",
el: "el-radio",
options: [
{ label: "诗", value: "0" },
{ label: "远方", value: "1" },
{ label: "美食", value: "2" },
],
},
{ label: "slider", prop: "slider", el: "el-slider" },
{ label: "time-picker", prop: "timePicker", el: "el-time-picker" },
{ label: "time-select", prop: "timeSelect", el: "el-time-select" },
{
label: "transfer",
prop: "transfer",
el: "el-transfer",
elProps: {
data: generateData(),
},
},
{ label: "divider", prop: "divider", el: "el-divider" },
{
label: "tree-select",
prop: "treeSelect",
el: "el-tree-select",
elProps: {
data: cascaderOptions,
},
},
{
label: "input-tag",
prop: "inputTag",
el: "el-input-tag",
},
{
label: "select-v2 ",
prop: "selectV2",
el: "el-select-v2",
options: () => {
const initials = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"];
return Array.from({ length: 1000 }).map((_, idx) => ({
value: `Option ${idx + 1}`,
label: `${initials[idx % 10]}${idx}`,
}));
},
},
{
label: "select-v2-multiple",
prop: "selectV2Multiple",
el: "el-select-v2",
options: () => {
const initials = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"];
return Array.from({ length: 1000 }).map((_, idx) => ({
value: `Option ${idx + 1}`,
label: `${initials[idx % 10]}${idx}`,
}));
},
elProps: {
multiple: true,
},
},
{
label: "mention",
prop: "mention",
el: "el-mention",
options: [
{ label: "Fuphoenixes", value: "Fuphoenixes" },
{ label: "kooriookami", value: "kooriookami" },
{ label: "Jeremy", value: "Jeremy" },
{ label: "btea", value: "btea" },
],
},
{
label: "segmented",
prop: "segmented",
el: "el-segmented",
options: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
},
{
label: "checkbox-select",
prop: "checkboxSelect",
el: "checkbox-select",
options: [
{ label: "选择 1", value: "option1" },
{ label: "选择 2", value: "option2" },
{ label: "选择 3", value: "option3" },
],
},
{
label: "checkbox-select-multiple",
prop: "checkboxSelectMultiple",
el: "checkbox-select",
elProps: {
multiple: true,
},
options: [
{ label: "选择 1", value: "option1" },
{ label: "选择 2", value: "option2" },
{ label: "选择 3", value: "option3" },
],
},
{
label: "tree",
prop: "tree",
el: "tree",
elProps: {
multiple: true,
},
options: [
{
label: "Level one 1",
children: [
{
label: "Level two 1-1",
children: [{ label: "Level three 1-1-1" }],
},
],
},
{
label: "Level one 2",
children: [
{
label: "Level two 2-1",
children: [{ label: "Level three 2-1-1" }],
},
{
label: "Level two 2-2",
children: [{ label: "Level three 2-2-1" }],
},
],
},
{
label: "Level one 3",
children: [
{
label: "Level two 3-1",
children: [{ label: "Level three 3-1-1" }],
},
{
label: "Level two 3-2",
children: [{ label: "Level three 3-2-1" }],
},
],
},
],
},
{
label: "text",
prop: "text",
el: "el-text",
},
];
</script>
<template>
<ProForm v-model="state" label-width="140px" :columns="columns" />
{{ state }}
</template>
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
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454