vueelementui实现搜索栏公共组件封装的实例代码
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
vueelementui实现搜索栏公共组件封装的实例代码
1、背景
vue后台管理系统,会有很多表格页⾯,表格上⽅会有⼀些搜索选项,表格直接使⽤el-table即可,⽽搜索栏区域每次写起来都很繁琐,⽽且多⼈开发情况下每个⼈写的样式都不相同,布局样式⽆法统⼀。
所以要考虑对搜索栏做⼀个封装,统⼀配置引⽤,提升开发维护效率和界⾯统⼀。
完成后的效果⼤概就是长这样:
2、分析
项⽬使⽤的是elementui框架,搜索栏这种表单提交,⾸先要使⽤el-form组件来封装,⽽复杂点就是表单项可能有很多种,例如input输⼊框、select选择框、⽇期时间选择框、⽇期时间范围选择框、cascader级联选择框等,每⼀项的字段名prop、名称label、绑定的属性⽅法都不尽相同。
所以不能通过普通的绑定个别属性的⽅式来处理,⽽slot插槽的⽅式也⽆法简化,最终决定通过传递⼀个配置项数组的形式来解析⽣成相应的结构。
3、实现
⽬前实现的⽅式由两部分组成,⼀部分是form表单组件,接受⽗组件传递的配置项数组,⼀部分是封装⼀些常⽤的表单项组件,通过v-if来控制,form表单组件⾥引⼊该表单项组件,循环遍历,根据传递的表单项类型来匹配显⽰具体的表单项。
form表单组件(searchForm.vue)⽰例代码:
<el-form
:model="formData"
ref="formRef"
:inline="true"
>
<el-form-item
v-for="(item, index) in formOptions"
:key="newKeys[index]"
:prop="item.prop"
:label="bel ? (bel + ':') : ''"
:rules="item.rules"
>
<formItem
v-model="formData[item.prop]"
:itemOptions="item"
/>
</el-form-item>
</el-form>
formItem表单项组件(formItem.vue)⽰例代码:
<el-input
v-if="isInput"
v-model="currentVal"
v-bind="bindProps"
v-on="bindEvents"
size="mini"
></el-input>
<el-select
v-if="isSelect"
v-model="currentVal"
v-bind="bindProps"
v-on="bindEvents"
size="mini"
clearable
>
<el-option
v-for="item in itemOptions.options"
:key="item.value"
:label="bel"
:value="item.value"
></el-option>
</el-select>
4、关键点
由于elementui表单组件本⾝有很多配置属性,不可能把所有的属性和⽅法都写死封装,要想⽆缝⽀持,需要⽤到vue的v-bind 和v-on特性,vue的v-bind和v-on⽀持赋值为对象类型,vue会⾃动遍历对象⾥的属性依次绑定,v2.4.0+⽀持。
5、参数配置项解释
(1)⽰例:
[{
label: '⽤户名', // label⽂字
prop: 'username', // 字段名
element: 'el-input', // 指定elementui组件
initValue: '阿黄', // 字段初始值
placeholder: '请输⼊⽤户名', // elementui组件属性
rules: [{ required: true, message: '必填项', trigger: 'blur' }], // elementui组件属性
events: { // elementui组件⽅法
input (val) {
console.log(val)
},
}
}]
label ⽤于绑定给el-form-item上的label,表单项标题
prop ⽤于绑定给el-form-item上的prop,字段名,必填
element 指定elementui表单项的组件名,必填
initValue 表单项的初始值,可选
events 对象,对象⾥加⽅法,js原⽣⽅法或者elementui表单项组件⽀持的⽅法都可以加进去,通过v-on遍历绑定
… 其他elementui表单项组件⽀持的属性或者html原⽣属性都可以添加,常⽤的例如rules表单校验、placeholder提⽰,通过v-bind遍历绑定
(2)参数传递解析的流程:
⾸先,searchForm.vue组件⾥通过props接收参数:
formOptions: {
type: Array,
required: true,
default () {
return []
}
},
created组件⾥处理初始值:
// 添加初始值
addInitValue () {
const obj = {}
this.formOptions.forEach(v => {
if (v.initValue !== undefined) {
obj[v.prop] = v.initValue
}
})
this.formData = obj
}
⼀部分配置项绑定在el-form-item上,⼀部分传递给formItem表单项组件再绑定:
<el-form-item
v-for="(item, index) in formOptions"
:key="newKeys[index]"
:prop="item.prop"
:label="bel ? (bel + ':') : ''"
:rules="item.rules"
>
<formItem
v-model="formData[item.prop]"
:itemOptions="item"
/>
</el-form-item>
formItem.vue表单项组件⾥props接受传参:
itemOptions: {
type: Object,
default () {
return {}
}
}
computed⾥处理接收的参数itemOptions,⽣成要绑定的所有属性对象bindProps:
// 绑定属性
bindProps () {
let obj = { ...this.itemOptions }
// 移除已使⽤的或不相关的冗余属性
delete bel
delete obj.prop
delete obj.element
delete obj.initValue
delete obj.rules
delete obj.events
if (obj.element === 'el-select') {
delete obj.options
}
return obj
},
computed⾥⽣成要绑定的所有⽅法对象bindEvents:
// 绑定⽅法
bindEvents () {
return this.itemOptions.events || {}
},
最后dom⾥使⽤这些数据绑定:
<el-input
v-if="isInput"
v-model="currentVal"
v-bind="bindProps"
v-on="bindEvents"
></el-input>
(3)特殊情况的处理
由于elementui的el-select⾥是通过el-option遍历实现的,⽽遍历数组options按elementui官⽅不是绑定在el-select上的,所以针对el-select的配置项再加⼀个options⾥属性,即select选择项的数据数组。
<el-select
v-if="isSelect"
v-model="currentVal"
v-bind="bindProps"
v-on="bindEvents"
size="mini"
clearable
>
<el-option
v-for="item in itemOptions.options"
:key="item.value"
:label="bel"
:value="item.value"
></el-option>
</el-select>
elementui的⽇期时间选择器分了很多种,根据业务需要分别处理⼀下,我这⾥是根据type划分成了三种分开处理,最常⽤的是datetimerange⽇期时间范围选择器,作为默认项,还有⼀种monthrange,其余的都划为⼀种。
(具体处理见⽂章末尾的完整代码)
6、按钮组
按钮其实就那么⼏个,没必要做太多的封装,根据业务有哪些按钮就封装进去,⽬前我这⾥就封装了三个按钮。
通过props接受⼀个字符串标识按钮组:
// 提交按钮项,多个⽤逗号分隔(query搜索, export导出, reset重置)btnItems: {
type: String,
default () {
return 'search'
}
}
7、使⽤⽅式⽰例
dom:
<!-- 搜索 -->
<searchForm :formOptions="formOptions" @onSearch="onSearch"/> vue data⾥:
formOptions: [
{
label: '意见内容',
prop: 'content',
element: 'el-input'
},
{
label: '类型',
prop: 'type',
element: 'el-select',
options: [
{ label: '给点意见', value: '1' },
{ label: '售后问题', value: '2' }
]
},
{
label: '状态',
prop: 'status',
element: 'el-select',
options: getFeedbackStatus()
},
{
label: '提交时间',
prop: 'timeRange',
element: 'el-date-picker'
}
],
vue methods⾥:
// 获取搜索表单提交的数据
onSearch (val) {
console.log(val)
}
8、完整代码
(1)searchForm.vue
/**
* Created by hanxueqiang on 200107
*
* 搜索栏公共组件
*/
<template>
<div class="search-form-box">
<el-form
:model="formData"
ref="formRef"
:inline="true"
>
<el-form-item
v-for="(item, index) in formOptions"
:key="newKeys[index]"
:prop="item.prop"
:label="bel ? (bel + ':') : ''"
:rules="item.rules"
>
<formItem
v-model="formData[item.prop]"
:itemOptions="item"
/>
</el-form-item>
</el-form>
<!-- 提交按钮 -->
<div class="btn-box">
<el-button
v-if="btnItems.includes('search')"
size="mini"
type="primary"
class="btn-search"
@click="onSearch"
>搜索</el-button>
<el-button
v-if="btnItems.includes('export')"
size="mini"
type="primary"
class="btn-export"
@click="onExport"
>导出</el-button>
<el-button
v-if="btnItems.includes('reset')"
size="mini"
type="default"
class="btn-reset"
@click="onReset"
>重置</el-button>
</div>
</div>
</template>
<script>
import formItem from './formItem'
import tools from '@/utils/tools'
export default {
props: {
/**
* 表单配置
* ⽰例:
* [{
* label: '⽤户名', // label⽂字
* prop: 'username', // 字段名
* element: 'el-input', // 指定elementui组件
* initValue: '阿黄', // 字段初始值
* placeholder: '请输⼊⽤户名', // elementui组件属性
* rules: [{ required: true, message: '必填项', trigger: 'blur' }], // elementui组件属性 * events: { // elementui组件⽅法
* input (val) {
* console.log(val)
* },
* ...... // 可添加任意elementui组件⽀持的⽅法
* }
* ...... // 可添加任意elementui组件⽀持的属性
* }]
*/
formOptions: {
type: Array,
required: true,
default () {
return []
}
},
// 提交按钮项,多个⽤逗号分隔(query, export, reset)
btnItems: {
type: String,
default () {
return 'search'
}
}
},
data () {
return {
formData: {}
}
},
computed: {
newKeys () {
return this.formOptions.map(v => { return tools.createUniqueString()
})
}
},
created () {
this.addInitValue()
},
methods: {
// 校验
onValidate (callback) {
this.$refs.formRef.validate(valid => { if (valid) {
console.log('提交成功')
console.log(this.formData)
callback()
}
})
},
// 搜索
onSearch () {
this.onValidate(() => {
this.$emit('onSearch', this.formData) })
},
// 导出
onExport () {
this.onValidate(() => {
this.$emit('onExport', this.formData) })
},
onReset () {
this.$refs.formRef.resetFields() },
// 添加初始值
addInitValue () {
const obj = {}
this.formOptions.forEach(v => {
if (v.initValue !== undefined) {
obj[v.prop] = v.initValue
}
})
this.formData = obj
}
},
components: { formItem }
}
</script>
<style lang='less' scoped>
.search-form-box {
display: flex;
margin-bottom: 15px;
.btn-box {
padding-top: 5px;
display: flex;
button {
height: 28px;
}
}
.el-form {
/deep/ .el-form-item__label {
padding-right: 0;
}
.el-form-item {
margin-bottom: 0;
&.is-error {
margin-bottom: 22px;
}
}
// el-input宽度
/deep/ .form-item {
> .el-input:not(.el-date-editor) {
width: 120px;
}
}
/deep/ .el-select {
width: 120px;
}
/deep/ .el-cascader {
width: 200px;
}
}
}
</style>
(2)formItem.vue
/**
* Created by hanxueqiang on 200107
*
* 表单匹配项
*/
<template>
<div class='form-item'>
<el-input
v-if="isInput"
v-model="currentVal"
v-bind="bindProps"
v-on="bindEvents"
size="mini"
></el-input>
<el-input-number
v-if="isInputNumber"
v-model="currentVal"
v-bind="bindProps"
v-on="bindEvents"
:controls-position="itemOptions['controls-position'] || 'right'" size="mini"
></el-input-number>
<el-select
v-if="isSelect"
v-model="currentVal"
v-bind="bindProps"
v-on="bindEvents"
size="mini"
clearable
>
<el-option
v-for="item in itemOptions.options"
:key="item.value"
:label="bel"
:value="item.value"
></el-option>
</el-select>
<!-- datetimerange/daterange -->
<el-date-picker
v-if="isDatePickerDateRange"
v-model="currentVal"
v-bind="bindProps"
v-on="bindEvents"
:type="itemOptions.type || 'datetimerange'"
size="mini"
clearable
:picker-options="pickerOptionsRange"
start-placeholder="开始⽇期"
range-separator="⾄"
end-placeholder="结束⽇期"
:default-time="['00:00:00', '23:59:59']"
value-format="yyyy-MM-dd HH:mm:ss"
></el-date-picker>
<!-- monthrange -->
<el-date-picker
v-if="isDatePickerMonthRange"
v-model="currentVal"
v-bind="bindProps"
v-on="bindEvents"
type="monthrange"
size="mini"
clearable
:picker-options="pickerOptionsRangeMonth"
start-placeholder="开始⽇期"
range-separator="⾄"
end-placeholder="结束⽇期"
value-format="yyyy-MM"
></el-date-picker>
<!-- others -->
<el-date-picker
v-if="isDatePickerOthers"
v-model="currentVal"
v-bind="bindProps"
v-on="bindEvents"
:type="itemOptions.type"
size="mini"
clearable
placeholder="请选择⽇期"
></el-date-picker>
<el-cascader
v-if="isCascader"
v-model="currentVal"
v-bind="bindProps"
v-on="bindEvents"
size="mini"
clearable
></el-cascader>
</div>
</template>
<script>
import tools from '@/utils/tools'
export default {
inheritAttrs: false,
props: {
value: {},
itemOptions: {
type: Object,
default () {
return {}
}
}
},
data () {
return {
pickerOptionsRange: tools.pickerOptionsRange,
pickerOptionsRangeMonth: tools.pickerOptionsRangeMonth }
},
computed: {
// 双向绑定数据值
currentVal: {
get () {
return this.value
},
set (val) {
this.$emit('input', val)
}
},
// 绑定属性
bindProps () {
let obj = { ...this.itemOptions }
// 移除冗余属性
delete bel
delete obj.prop
delete obj.element
delete obj.initValue
delete obj.rules
delete obj.events
if (obj.element === 'el-select') {
delete obj.options
}
return obj
},
// 绑定⽅法
bindEvents () {
return this.itemOptions.events || {}
},
// el-input
isInput () {
return this.itemOptions.element === 'el-input'
},
// el-input-number
isInputNumber () {
return this.itemOptions.element === 'el-input-number'
},
// el-select
isSelect () {
return this.itemOptions.element === 'el-select'
},
// el-date-picker (type: datetimerange/daterange)
isDatePickerDateRange () {
const isDatePicker = this.itemOptions.element === 'el-date-picker'
const isDateRange = !this.itemOptions.type ||
this.itemOptions.type === 'datetimerange' ||
this.itemOptions.type === 'daterange'
return isDatePicker && isDateRange
},
// el-date-picker (type: monthrange)
isDatePickerMonthRange () {
const isDatePicker = this.itemOptions.element === 'el-date-picker'
const isMonthRange = this.itemOptions.type === 'monthrange'
return isDatePicker && isMonthRange
},
// el-date-picker (type: other)
isDatePickerOthers () {
const isDatePicker = this.itemOptions.element === 'el-date-picker'
return isDatePicker && !this.isDatePickerDateRange && !this.isDatePickerMonthRange },
// el-cascader
isCascader () {
return this.itemOptions.element === 'el-cascader'
}
},
created () {},
methods: {},
components: {}
}
</script>
<style lang='less' scoped>
</style>
(3)依赖引⼊的⼀些函数⽅法 tools.js
/**
* 创建唯⼀的字符串
* @return {string} ojgdvbvaua40
*/
function createUniqueString () {
const timestamp = +new Date() + ''
const randomNum = parseInt((1 + Math.random()) * 65536) + '' return (+(randomNum + timestamp)).toString(32)
}
// elementui⽇期时间范围快捷选项
const pickerOptionsRange = {
shortcuts: [
{
text: '今天',
onClick (picker) {
const end = new Date()
const start = new Date(new Date().toDateString())
start.setTime(start.getTime())
picker.$emit('pick', [start, end])
}
}, {
text: '最近⼀周',
onClick (picker) {
const end = new Date()
const start = new Date()
start.setTime(end.getTime() - 3600 * 1000 * 24 * 7)
picker.$emit('pick', [start, end])
}
}, {
text: '最近⼀个⽉',
onClick (picker) {
const end = new Date()
const start = new Date()
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
picker.$emit('pick', [start, end])
}
}, {
text: '最近三个⽉',
onClick (picker) {
const end = new Date()
const start = new Date()
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90)
picker.$emit('pick', [start, end])
}
}
]
}
// elementui⽉份范围快捷选项
const pickerOptionsRangeMonth = {
shortcuts: [
{
text: '今年⾄今',
onClick (picker) {
const end = new Date()
const start = new Date(new Date().getFullYear(), 0)
picker.$emit('pick', [start, end])
}
},
{
text: '最近半年',
onClick (picker) {
const end = new Date()
const start = new Date()
start.setMonth(start.getMonth() - 6)
picker.$emit('pick', [start, end])
}
},
{
text: '最近⼀年',
onClick (picker) {
const end = new Date()
const start = new Date()
start.setMonth(start.getMonth() - 12)
picker.$emit('pick', [start, end])
}
}
]
}
(4)⼀些elmentui全局样式的修改// el-input-number (controls-position="right") .el-input-number.is-controls-right {
.el-input-number__decrease {
display: none;
}
.el-input-number__increase {
display: none;
top: 2px; // fix style bug
}
&:hover {
.el-input-number__decrease {
display: inline-block;
}
.el-input-number__increase {
display: inline-block;
}
}
.el-input__inner {
text-align: left;
padding-left: 5px;
padding-right: 40px;
}
}
// el-date-picker datetimerange
.el-date-editor.el-date-editor--datetimerange { .el-range-separator {
width: 24px;
color: #999;
padding: 0;
}
.el-range__icon {
margin-left: 0;
}
&.el-input__inner {
vertical-align: middle;
padding: 3px 5px;
}
&.el-range-editor--medium {
width: 380px;
.el-range-separator {
line-height: 30px;
}
}
&.el-range-editor--mini {
width: 330px;
.el-range-separator {
line-height: 22px;
}
}
}
// el-date-picker not datetimerange
.el-date-editor {
.el-input__prefix {
left: 0;
top: 1px;
}
.el-input__suffix {
right: 0;
top: 1px;
}
.el-input__inner {
padding: 0 25px;
}
&.el-input--mini {
width: 175px;
}
&.el-input--medium {
width: 195px;
}
}
// input padding
.el-input__inner {
padding: 0 5px;
}
总结
以上所述是⼩编给⼤家介绍的vue elementui 实现搜索栏公共组件封装,希望对⼤家有所帮助,如果⼤家有任何疑问请给我留⾔,⼩编会及时回复⼤家的。
在此也⾮常感谢⼤家对⽹站的⽀持!
如果你觉得本⽂对你有帮助,欢迎转载,烦请注明出处,谢谢!。