封装el-form
- 目前在编写项目中,每个页面都有el-from,所以对el-form做了二次封装, 组件在个人开发使用不错,但不确定能满足各种业务需求,所以这里主要和大家分享一下设计思路。用一次爽一次,越用越爽。
分析问题:
- el-form是element-ui库的表单组件,如果我们要将其进行二次封装,那么需要考虑几个问题:
- 如何设计表单渲染字段
- 不同类型的el-form-item怎么去渲染,比如input,select,或者自定义显示内容等
- 表单联动怎么去处理
- 事件绑定
- 表单验证
- 等等.
拿表单来举例,在这个表单中的需求分析。
正常情况下,我们使用form,它的子项会是这样的,比如有input和select:
// input类型
<el-form-item label="活动名称" prop="name">
<el-input v-model="ruleForm.name"></el-input>
</el-form-item>
// select类型
<el-form-item label="活动区域" prop="region">
<el-select v-model="ruleForm.region" placeholder="请选择活动区域">
<el-option label="区域一" value="shanghai"></el-option>
<el-option label="区域二" value="beijing"></el-option>
</el-select>
</el-form-item>
那由此我们可以设计出封装后样子:
<el-form-item label="label" prop="prop">
// 如果是input类型
<el-input v-if="input" v-model="ruleForm.name"></el-input>
// 如果是select类型
<el-select v-if="select" v-model="ruleForm.region" placeholder="请选择活动区域">
<el-option label="区域一" value="shanghai"></el-option>
<el-option label="区域二" value="beijing"></el-option>
</el-select>
</el-form-item>
- 表单联动、自定义、事件等等(请看↓↓组件内部)。
- 组件使用方法:(请查看《form表单API》)
组件内部template:
<template>
<div class="Form">
<el-form
ref="form"
class="page-form"
:class="className"
:model="data"
:rules="rules"
:label-width="labelWidth"
>
<el-form-item
v-if="!item.Noshow"
v-for="(item, index) in fieldList"
:key="index"
:prop="item.value"
:label="item.label+':'"
:class="item.className"
:style="typePattern? 'float: left;':''"
>
<!-- 普通输入框 -->
<el-input
v-if="item.type === 'input' || item.type === 'password'"
v-model="data[item.value]"
:type="item.type"
:placeholder="getPlaceholder(item)"
@blur="blurEvent(item.event)"
:show-password="item.showPassword"
:style="item.style"
/>
<!-- 文本输入框 -->
<el-input
v-if="item.type === 'textarea'"
v-model.trim="data[item.value]"
:type="item.type"
:disabled="item.disabled"
:placeholder="getPlaceholder(item)"
:autosize="{minRows: 2, maxRows: 10}"
:maxlength="item.maxlength"
show-word-limit
@blur="blurEvent(item.event)"
/>
<!-- 计数器 -->
<el-input-number
v-if="item.type === 'inputNumber'"
v-model="data[item.value]"
:step="item.step"
size="small"
:min="item.min"
:max="item.max"
/>
<!-- 选择框 -->
<el-select
v-if="item.type === 'select'"
v-model="data[item.value]"
:disabled="item.disabled"
:clearable="item.clearable"
:filterable="item.filterable"
:placeholder="getPlaceholder(item)"
@change="changeEvent(item.event, data[item.value])"
>
<el-option
v-for="(childItem, childIndex) in listTypeInfo[item.list]"
:key="childIndex"
:label="childItem.label"
:value="childItem.value"
/>
</el-select>
<!-- 联选择器 -->
<el-cascader
v-if="item.type === 'cascader'"
v-model="data[item.value]"
:options="listTypeInfo[item.list]"
:props="{ expandTrigger: 'hover' }"
:filterable="item.filterable"
:clearable="item.clearable"
@change="changeEvent(item.event, data[item.value])"
>
</el-cascader>
<!-- Radio 单选框 -->
<el-radio
v-if="item.type === 'radio'"
v-for="(childItem, childIndex) in listTypeInfo[item.list]"
:key="childIndex"
v-model="data[item.value]"
:label="childItem.value"
@change="changeEvent(item.event, data[item.value])"
>
{{childItem.label}}
</el-radio>
<!-- Checkbox 多选框-->
<el-checkbox
v-if="item.type === 'checked'"
v-for="(childItem, childIndex) in listTypeInfo[item.list]"
:key="childIndex"
v-model="data[item.value]"
:label="childItem.value"
@change="changeEvent(item.event, data[item.value])"
>
{{childItem.label}}
</el-checkbox>
<!-- Switch 开关-->
<el-switch
v-if="item.type === 'switch'"
v-model="data[item.value]"
:active-color="item.color"
>
</el-switch>
<!-- 日期选择框 -->
<el-date-picker
v-if="item.type === 'date'"
v-model="data[item.value]"
:type="item.dateType"
:clearable="item.clearable"
:disabled="item.disabled"
:placeholder="getPlaceholder(item)"
:format="item.format"
:value-format="item.valueFormat"
:range-separator="item.rangeSeparator"
:picker-options="listTypeInfo[item.list]"
unlink-panels
start-placeholder="开始日期"
end-placeholder="结束日期"
/>
<!-- 时间选择框 -->
<el-time-picker
v-if="item.type === 'time'"
:is-range="item.isRange"
v-model="data[item.value]"
:range-separator="item.rangeSeparator"
:placeholder="getPlaceholder(item)"
start-placeholder="开始时间"
end-placeholder="结束时间"
>
</el-time-picker>
<!-- 自定义标签 -->
<div v-if="item.type === 'tag'">
<el-tag
v-for="childItem in data[item.value]"
:key="childItem"
closable
:disable-transitions="false"
@close="handleClose(childItem,item.value)"
>
{{childItem}}
</el-tag>
<el-input
class="input-new-tag"
v-if="inputVisible"
v-model="inputValue"
ref="saveTagInput"
@keyup.enter.native="handleInputConfirm(item.value)"
@blur="handleInputConfirm(item.value)"
>
</el-input>
<el-button v-else class="button-new-tag" size="small" @click="showInput">{{item.btnName}}</el-button>
</div>
<!-- solt 通过具名插槽来实现多个自定义-->
<template v-if="item.type === 'slot'">
<slot :name="'form-' + item.value" />
</template>
</el-form-item>
<div v-if="isShowBtn" class="btn" :style="typePattern? 'float: left;margin-left: 20px;':''">
<el-button type="primary" @click="submitForm('form')">{{typePattern?'查 询':'提 交'}}</el-button>
<el-button v-if="typePattern" @click="resetForm('form')">重 置</el-button>
</div>
</el-form>
</div>
</template>
定义参数及事件:
<script>
export default {
name: 'wb-form',
props: {
// 自定义类名
className: {
type: String
},
// 表单数据
data: {
type: Object
},
// 相关字段
fieldList: {
type: Array
},
// 验证规则
rules: {
type: Object
},
// 相关的列表 (必填)
listTypeInfo: {
type: Object
},
// label宽度
labelWidth: {
type: String,
default: '100px'
},
// 展示类型(是否启用浮动模式)
typePattern: {
type: Boolean
},
// 是否显示按钮(不显示则自定义按钮)
isShowBtn: {
type: Boolean,
default: true
}
},
data () {
return {
// 是否显示标签名称
inputVisible: false,
// 添加的新标签名
inputValue: ''
}
},
// watch: {
// data: {
// handler: function (val) {
// console.log(val)
// // 将form实例返回到父级
// this.$emit('updateRef', this.$refs.form)
// },
// deep: true // 深度监听
// },
// },
methods: {
/**
* placeholder 输入框占位文本
* 通过方法,里面处理你想要的数据,最终return 出去
* */
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
},
/**
* 绑定的相关事件
* 可扩展Element 所有Event事件,以下都是常用的事件,(可自行扩展)
* */
// 失去焦点事件
blurEvent (evnet) {
let data = {
evnetName: evnet,
};
this.$emit('blurEvent', data)
},
// 发生改变的时候
changeEvent (evnet, value) {
let data = {
evnetName: evnet,
value: value
};
this.$emit('changeEvent', data)
},
// 派发按钮点击事件
handleClick (event, data) {
this.$emit('handleClick', event, data)
},
/**
* 标签专用函数
* */
// 关闭标签
handleClose(item,name) {
this.data[name].splice(this.data[name].indexOf(item), 1);
},
// 显示input
showInput() {
this.inputVisible = true;
},
// 失去焦点,所做的事情
handleInputConfirm(list) {
let inputValue = this.inputValue;
if (inputValue) {
this.data[list].push(inputValue);
}
this.inputVisible = false;
this.inputValue = '';
},
/**
* 提交表单
* */
submitForm (formName) {
if(this.type){
this.$emit('success')
}else {
this.$refs[formName].validate((valid) => {
if (valid) {
this.$emit('success')
} else {
console.log('error submit!!');
return false;
}
});
}
},
// 清除表单
resetForm (formName) {
this.$refs[formName].resetFields();
}
},
mounted () {
// 将form实例返回到父级 (为了更方便使用Ref)
this.$emit('formRef', this.$refs.form)
},
}
</script>
样式处理
<style scoped>
.el-tag + .el-tag {
margin-left: 10px;
}
.button-new-tag {
margin-left: 10px;
height: 32px;
line-height: 30px;
padding-top: 0;
padding-bottom: 0;
}
.input-new-tag {
width: 90px;
margin-left: 10px;
vertical-align: bottom;
}
.btn{
display: flex;
justify-content: center;
align-items: center;
}
</style>
添加自定义事件案例:
<el-input
v-if="item.type === 'input' || item.type === 'password'"
v-model="data[item.value]"
:type="item.type"
:placeholder="getPlaceholder(item)"
@blur="blurEvent(item.event)" <!--这个是重点 -->
:show-password="item.showPassword"
:style="item.style"
/>
methods: {
/**
* 可扩展Element 所有Event事件,以下都是常用的事件,(可自行扩展)
* 思路:给每一个元素添加相同事件,通过event名称来区分事件,然后写你的逻辑代码
* */
// 失去焦点事件(这个是重点)
blurEvent (evnet) {
let data = {
evnetName: evnet,
};
this.$emit('blurEvent', data)
},
}