2021/08/13更新,添加了
placeholder
自定义提示
# 新建 Form.vue
src/components/Form.vue
<template>
<div>
<el-form
ref="form"
:model="form"
:inline="inline"
:rules="rules"
:label-width="labelWidth"
class="form"
>
<el-form-item
:label="item.title"
:prop="item.key"
v-for="item in options"
:key="item.key"
>
<div v-if="$slots[item.key]">
<slot :name="item.key"></slot>
</div>
<div v-else>
<el-input
:size="size"
v-model="form[item.key]"
v-if="item.type === 'text'"
:placeholder="
item.placeholder ? item.placeholder : '请输入' + item.title
"
:disabled="item.disabled"
clearable
></el-input>
<el-input
:size="size"
type="textarea"
:autosize="{ minRows: 3, maxRows: 6 }"
:placeholder="
item.placeholder ? item.placeholder : '请输入' + item.title
"
v-model="form[item.key]"
v-if="item.type === 'textarea'"
:disabled="item.disabled"
>
</el-input>
<el-switch
:size="size"
v-model="form[item.key]"
v-if="item.type === 'switch'"
active-color="#13ce66"
inactive-color="#ff4949"
:disabled="item.disabled"
>
</el-switch>
<el-time-picker
:size="size"
:placeholder="
item.placeholder ? item.placeholder : '请选择' + item.title
"
v-model="form[item.key]"
value-format="HH:mm:ss"
v-if="item.type === 'time'"
:disabled="item.disabled"
>
</el-time-picker>
<el-date-picker
type="date"
:placeholder="
item.placeholder ? item.placeholder : '请选择' + item.title
"
value-format="yyyy-MM-dd"
:size="size"
v-model="form[item.key]"
v-if="item.type === 'date'"
:disabled="item.disabled"
></el-date-picker>
<el-date-picker
type="datetime"
:placeholder="
item.placeholder ? item.placeholder : '请选择' + item.title
"
value-format="yyyy-MM-dd HH:mm:ss"
:size="size"
v-model="form[item.key]"
v-if="item.type === 'datetime'"
:disabled="item.disabled"
></el-date-picker>
<el-date-picker
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="yyyy-MM-dd HH:mm:ss"
:size="size"
v-model="form[item.key]"
v-if="item.type === 'date-to-date'"
:disabled="item.disabled"
></el-date-picker>
<el-date-picker
type="datetimerange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="yyyy-MM-dd HH:mm:ss"
:size="size"
v-model="form[item.key]"
v-if="item.type === 'datetime-to-datetime'"
:disabled="item.disabled"
></el-date-picker>
<el-input-number
:size="size"
v-model="form[item.key]"
v-if="item.type === 'number'"
:disabled="item.disabled"
></el-input-number>
<el-select
:size="size"
v-model="form[item.key]"
v-if="item.type === 'select'"
clearable
:placeholder="
item.placeholder ? item.placeholder : '请选择' + item.title
"
:disabled="item.disabled"
>
<el-option
v-for="data in item.option.data"
:key="data[item.option.props.key]"
:label="data[item.option.props.label]"
:value="data[item.option.props.value]"
>
</el-option>
</el-select>
<el-cascader
:size="size"
v-model="form[item.key]"
v-if="item.type === 'cascader'"
:disabled="item.disabled"
:options="item.option.data"
:props="item.option.props"
clearable
></el-cascader>
<el-radio-group
:size="size"
v-model="form[item.key]"
v-if="item.type === 'radio'"
:disabled="item.disabled"
>
<el-radio
v-for="data in item.option.data"
:key="data[item.option.props.key]"
:label="data[item.option.props.value]"
>{{ data[item.option.props.label] }}</el-radio
>
</el-radio-group>
<el-checkbox-group
:size="size"
v-model="form[item.key]"
v-if="item.type === 'checkbox'"
:disabled="item.disabled"
>
<el-checkbox
v-for="data in item.option.data"
:key="data[item.option.props.key]"
:label="data[item.option.props.label]"
>{{ data[item.option.props.label] }}</el-checkbox
>
</el-checkbox-group>
<el-upload
v-if="item.type === 'file'"
action="/api/upload_ajax"
:show-file-list="false"
:on-success="handleAvatarSuccess"
:disabled="item.disabled"
>
<img
v-if="form[item.key]"
:src="form[item.key]"
alt=""
style="width: 178px; height: 178px; border-radius: 6px;"
@click="setFileKey(item.key)"
/>
<i
v-else
class="el-icon-plus avatar-uploader-icon"
@click="setFileKey(item.key)"
></i>
</el-upload>
</div>
</el-form-item>
<slot name="options"></slot>
</el-form>
</div>
</template>
<script>
export default {
name: "index",
props: {
// 表单
form: {
type: Object,
default: function() {
return {};
}
},
// 表单配置
options: {
type: Array,
default: function() {
return [];
}
},
// 表单验证规则
rules: {
type: Object,
default: function() {
return {};
}
},
// 是否行内表单
inline: {
type: Boolean,
default: function() {
return false;
}
},
// 表单表头宽度
labelWidth: {
type: String,
default: function() {
return "80px";
}
},
// 控件大小
size: {
type: String,
default: function() {
return "medium";
}
}
},
data() {
return {
fileKey: ""
};
},
mounted() {
// 动态添加验证
this.addRules(this.options);
// 初始化完成清除一下表单验证规则
this.$nextTick(function() {
this.clearValidate();
});
},
methods: {
/**
* @description: 动态添加表单验证
* @author: chenbz
* @date: 2021/4/27
*/
addRules(options) {
for (let i = 0; i < options.length; i++) {
// 判断是否需要添加验证
if (options[i].rules) {
// 判断用户是否已经自定义验证了
if (!this.rules[options[i].key]) {
// 动态生成验证方法
let data = [
{
required: true,
message: "",
trigger: ""
}
];
// 动态添加验证方式:失去焦点触发/确认时触发
if (options[i].type === ("input" || "text")) {
data[0].message = "请输入" + options[i].title;
data[0].trigger = "blur";
} else {
data[0].message = "请选择" + options[i].title;
data[0].trigger = "change";
}
// 触发vue更新
this.$set(this.rules, options[i].key, data);
}
}
}
},
/**
* @description: 表单验证
* @author: chenbz
* @date: 2021/5/10
*/
validate() {
let isValidate = false;
this.$refs.form.validate(valid => {
if (valid) {
isValidate = true;
}
});
return isValidate;
},
/**
* @description: 清除表单验证
* @author: chenbz
* @date: 2021/5/18
*/
clearValidate() {
this.$refs.form.clearValidate();
},
setFileKey(key) {
// console.log(key);
this.fileKey = key;
},
handleAvatarSuccess(res, file) {
// console.log(res);
// console.log(file);
file;
this.form[this.fileKey] = res.data;
}
}
};
</script>
<style scoped>
>>> .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
width: 178px;
height: 178px;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
line-height: 178px;
text-align: center;
}
>>> .el-upload:hover {
border-color: #409eff;
}
</style>
# 引入页面
src/views/home.vue
<template>
<div>
<div>
<MyForm
:form="formData.form"
:inline="true"
:options="formData.options"
:rules="formData.rules"
:labelWidth="formData.labelWidth"
></MyForm>
<el-divider></el-divider>
<MyForm
:form="formData.form"
:options="formData.options"
:rules="formData.rules"
:labelWidth="formData.labelWidth"
></MyForm>
<el-divider></el-divider>
</div>
</div>
</template>
<script>
import MyForm from "@/components/Form.vue";
export default {
name: "home",
components: {
MyForm
},
data() {
return {
formData: {
form: {
date: "",
sex: "",
name: ""
},
options: [
{
title: "日期",
key: "date",
type: "date",
rules: true
},
{
title: "性别",
key: "sex",
type: "select",
option: {
data: [
{
id: 1,
title: "女",
type: 0
},
{
id: 2,
title: "男",
type: 1
}
],
props: {
key: "id", // 指定key对应data的字段
label: "title", // 指定label对应data的字段
value: "type" // 指定value对应data的字段
}
}
},
{
title: "名字",
key: "name",
type: "input",
rules: true
}
],
rules: {},
labelWidth: "80px"
}
};
},
mounted() {},
methods: {
}
};
</script>
<style scoped></style>
# 自定义图片上传
Home.vue
src/views/Home.vue
<template>
<div>
<div>
<MyForm
:form="formData.form"
:options="formData.options"
:rules="formData.rules"
:labelWidth="formData.labelWidth"
>
<template slot="options">
<el-form-item label="文件上传">
<el-upload
action=""
:show-file-list="false"
:on-change="handleChange"
:auto-upload="false"
>
<img v-if="formData.form.img" :src="formData.form.img" alt="" />
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
</el-form-item>
</template>
</MyForm>
<el-divider></el-divider>
</div>
</div>
</template>
<script>
import MyForm from "@/components/Form.vue";
export default {
name: "Home",
components: {
MyForm
},
data() {
return {
formData: {
form: {
date: ""
},
options: [
],
rules: {},
labelWidth: "80px"
}
};
},
mounted() {},
methods: {
handleChange(file, fileList) {
fileList;
this.formData.form.img = URL.createObjectURL(file.raw);
}
}
};
</script>
<style></style>
使用
template
包含自定义的内容,slot
一定要是options
上传组件根据业务而定,我预留了一个
slot
插槽用来显示自定义的组件
# 参数说明
属性 | 描述 | 默认值 | 必需 |
---|---|---|---|
form | form表单 | {} | 是 |
options | 表单配置 | [] | 是 |
inline | 是否开启行内表单 | false | 否 |
rules | 表单验证规则 | {} | 否 |
labelWidth | 表单表头宽度 | 80px | 否 |
size | 控件大小 | medium | 否 |
# options
属性 | 描述 | 必需 |
---|---|---|
title | 表单表头 | 是 |
key | 表单对应的字段 | 是 |
type | 控件类型 | 是 |
rules | 是否开启验证 | 否 |
option | 表单配置 | 否 |
disabled | 是否禁用 | 否 |
placeholder | 自定义提示 | 否 |
# type
属性 | 描述 |
---|---|
input | 输入框 |
text | 文本输入框 |
switch | 开关 |
time | 时间 |
date | 日期 |
datetime | 日期时间 |
date-to-date | 开始日期 — 结束日期 |
datetime-to-datetime | 开始日期时间 — 结束日期时间 |
number | 数字 |
select | 选择框 |
cascader | 联级选择框 |
radio | 单选框 |
checkbox | 多选框 |
file | 图片上传 |
checkbox绑定字段的值一定要是: []
例如:sex: []
# option
只有在
type
为[select, cascader, radio, checkbox]
时才需要设置
属性 | 描述 | 必需 |
---|---|---|
data | 数据源 | 是 |
props | 配置 | 是 |
# props
type
为[cascader]
的配置可以直接看官网
属性 | 描述 | 必需 |
---|---|---|
Key | 指定唯一字段 | 是 |
label | 指定显示标题字段 | 是 |
value | 指定选中后值字段 | 是 |
# 方法
属性 | 描述 | 回调函数 |
---|---|---|
validate() | 表单验证 | isValidate |
clearValidate() | 清除表单验证 |
# 效果图
# 更多表单
Home.vue
src/views/Home.vue
<template>
<div>
<div>
<MyForm
:form="formData.form"
:options="formData.options"
:rules="formData.rules"
:labelWidth="formData.labelWidth"
>
</MyForm>
<el-divider></el-divider>
</div>
</div>
</template>
<script>
import MyForm from "@/components/Form.vue";
export default {
name: "home",
components: {
MyForm
},
data() {
return {
formData: {
form: {
input: "",
text: "",
switch: true,
time: "10:10:10",
date: "2021-04-27",
datetime: "2021-04-27 10:10:10",
"date-to-date": "",
"datetime-to-datetime": "",
number: 0,
select: "",
radio: "",
checkbox: []
},
options: [
{
title: "输入框",
key: "input",
type: "input"
},
{
title: "文本",
key: "text",
type: "text"
},
{
title: "开关",
key: "switch",
type: "switch"
},
{
title: "时间",
key: "time",
type: "time"
},
{
title: "日期",
key: "date",
type: "date"
},
{
title: "日期时间",
key: "datetime",
type: "datetime"
},
{
title: "开始日期 --- 结束日期",
key: "date-to-date",
type: "date-to-date"
},
{
title: "开始日期时间 --- 结束日期时间",
key: "datetime-to-datetime",
type: "datetime-to-datetime"
},
{
title: "数字",
key: "number",
type: "number"
},
{
title: "选择框",
key: "select",
type: "select",
option: {
data: [
{
id: 1,
title: "女",
type: 0
},
{
id: 2,
title: "男",
type: 1
}
],
props: {
key: "id", // 指定key对应data的字段
label: "title", // 指定label对应data的字段
value: "type" // 指定value对应data的字段
}
}
},
{
title: "单选框",
key: "radio",
type: "radio",
option: {
data: [
{
id: 1,
title: "女",
type: 0
},
{
id: 2,
title: "男",
type: 1
}
],
props: {
key: "id", // 指定key对应data的字段
label: "title", // 指定label对应data的字段
value: "type" // 指定value对应data的字段
}
}
},
{
title: "多选框",
key: "checkbox",
type: "checkbox",
option: {
data: [
{
id: 1,
title: "1"
},
{
id: 2,
title: "2"
},
{
id: 3,
title: "3"
}
],
props: {
key: "id", // 指定key对应data的字段
label: "title", // 指定label对应data的字段
}
}
}
],
rules: {},
labelWidth: "250px"
}
};
},
mounted() {},
methods: {}
};
</script>
<style></style>