一,封装组件背景
elementui框架Select组件远程搜索不支持上拉加载功能, 本教程用vue 自定义指令(directive)结合element Select远程搜索加mixin混合模式,封装select选择器上拉夹杂支持远程搜索功能组件
二,首先要封装一个自定义组件
loadMore.js 文件里的代码如下
import { addClass } from '../../utils'
export default {
bind(el, binding) {
// 获取element-ui定义好的scroll盒子
const SELECTWRAP_DOM = el.querySelector('.el-select-dropdown .el-select-dropdown__wrap')
const loadingDom = document.createElement('div')
loadingDom.style.width = '100%'
loadingDom.style.height = '50px'
loadingDom.style.lineHeight = '30px'
loadingDom.style.backgroundColor = '#fff'
loadingDom.style.color = '#606266'
loadingDom.style.textAlign = 'center'
loadingDom.innerHTML = '加载中...'
SELECTWRAP_DOM.addEventListener('scroll', function() {
/*
* scrollHeight 获取元素内容高度(只读)
* scrollTop 获取或者设置元素的偏移值,常用于, 计算滚动条的位置, 当一个元素的容器没有产生垂直方向的滚动条, 那它的scrollTop的值默认为0.
* clientHeight 读取元素的可见高度(只读)
* 如果元素滚动到底, 下面等式返回true, 没有则返回false:
* ele.scrollHeight - ele.scrollTop === ele.clientHeight;
*/
const CONDITION = this.scrollHeight - this.scrollTop <= this.clientHeight
if (CONDITION) {
SELECTWRAP_DOM.append(loadingDom)
binding.value()
}
})
}
}
(本案例不讲解自定义组件的知识,请自行学习或者加入我们的qq群或者关注公众号 程序员蜗牛,回复你的问题)
文件结构是
---src--
-------directive--
------------------index.js(directive入口文件,解释如下)
------------------loadMore--
------------------------------index.js和loadMore.js
index.js 里面代码
import loadMore from '@/directive/load-more/index'
Vue.directive('loadMore', loadMore)
main.js里面代码
import '@/directive/index'
自此自定义指令就可以引用了
在需要使用自定义指令的地方 v-load-more=loadMore(接收函数)
结果:
这样做不太完美,接下来就是高度封装代码
在 loadMore.js
import { addClass, removeClass } from '../../utils'
export default {
bind(el, binding, vNode) {
// 获取element-ui定义好的scroll盒子
const SELECTWRAP_DOM = el.querySelector('.el-select-dropdown .el-select-dropdown__wrap')
const loadingDom = document.createElement('div')
loadingDom.style.width = '100%'
loadingDom.style.height = '50px'
loadingDom.style.lineHeight = '30px'
loadingDom.style.backgroundColor = '#fff'
loadingDom.style.color = '#606266'
loadingDom.style.textAlign = 'center'
loadingDom.style.fontSize = '14px'
loadingDom.className = 'loadingBox'
const loadingIcon = document.createElement('i')
loadingDom.append(loadingIcon)
SELECTWRAP_DOM.append(loadingDom)
const i = el.getElementsByClassName('loadingBox')[0].getElementsByTagName('i')[0]
SELECTWRAP_DOM.addEventListener('scroll', function() {
/*
* scrollHeight 获取元素内容高度(只读)
* scrollTop 获取或者设置元素的偏移值,常用于, 计算滚动条的位置, 当一个元素的容器没有产生垂直方向的滚动条, 那它的scrollTop的值默认为0.
* clientHeight 读取元素的可见高度(只读)
* 如果元素滚动到底, 下面等式返回true, 没有则返回false:
* ele.scrollHeight - ele.scrollTop === ele.clientHeight;
*/
const CONDITION = this.scrollHeight - this.scrollTop <= this.clientHeight
if (CONDITION) {
addClass(i, 'el-icon-loading')
binding.value()
} else {
removeClass(i, 'el-icon-loading')
}
})
}
}
然后在自定义组件里新建hospitalForm.vue
<template>
<el-form-item label="医院名称:" prop="hospitalNo">
<el-select
v-model="ruleForm.hospitalNo"
v-load-more="loadMore"
filterable
placeholder="请选择"
remote
:remote-method="remoteMethod"
value=""
clearable
:loading="loading"
popper-class="option-box"
:style="styles"
@change="handleChange"
>
<el-option
v-for="(item, index) in hospitalNoOptions"
:key="index"
:label="item.hospitalName"
:value="item.hospitalNo"
/>
</el-select>
</el-form-item>
</template>
<script>
import { searchHospital } from '../../api/user'
import { S_OK } from '../../utils/constant'
export default {
name: 'HospitalForm',
props: {
hospitalNo: {
type: String,
default: ''
},
styles: {
type: String,
default: ''
}
},
data() {
return {
ruleForm: {
hospitalNo: ''
},
hospitalNoOptions: [],
loading: false,
pageNo: 0,
searchKey: {
'hospitalNo': '',
'hospitalName': '',
'pageNo': 1
},
hospitalTotalCount: 0
}
},
mounted() {
this.searchHospitalFun()
},
methods: {
// 初始化数据,并上啦加载时
searchHospitalFun() {
searchHospital(this.searchKey).then(res => {
if (res.resultCode === S_OK) {
this.hospitalNoOptions = this.hospitalNoOptions.concat(res.resultObj.list)
this.hospitalTotalCount = res.resultObj.totalCount
if (res.resultObj.pages * 10 > res.resultObj.totalCount) {
this.searchKey.pageNo = res.resultObj.pages
this.$refs['hospitalForm'].popperElm.getElementsByClassName('loadingBox')[0].innerHTML = '~见底了~'
}
}
})
},
// 上拉加载
loadMore() {
if (this.searchKey.pageNo * 10 <= this.hospitalTotalCount) {
this.handleLoad()
this.searchHospitalFun()
}
this.searchKey.pageNo += 1
},
// 远程搜索
remoteMethod(query) {
this.handleLoad()
this.handleTop()
if (query !== '') {
this.loading = true
this.searchKey.hospitalName = query
this.searchKey.pageNo = 1
searchHospital(this.searchKey).then(res => {
if (res.resultCode === S_OK) {
this.hospitalNoOptions = res.resultObj.list
this.hospitalTotalCount = res.resultObj.totalCount
this.loading = false
if (res.resultObj.pages * 10 > res.resultObj.totalCount) {
this.searchKey.pageNo = res.resultObj.pages
this.$refs['hospitalForm'].popperElm.getElementsByClassName('loadingBox')[0].innerHTML = '~见底了~'
}
}
})
} else {
this.hospitalNoOptions = []
this.searchKey = {
'hospitalNo': '',
'hospitalName': '',
'pageNo': 1
}
this.searchHospitalFun()
}
},
// 点击搜索时,就将见底了变回加载icon
handleLoad() {
this.$refs['hospitalForm'].popperElm.getElementsByClassName('loadingBox')[0].innerHTML = `
<i class='el-icon-loading'></i>
`
},
handleChange() {
this.$emit('update:hospitalNo', this.ruleForm.hospitalNo)
},
handleTop() {
const dom = document.getElementsByClassName('option-box')[0].getElementsByClassName('el-scrollbar__wrap')[0]
dom.scrollTop = 0 // 回到顶部,有点问题,滚动条没有回去,其实是回到顶部了,有看出问题的也欢迎留言
}
}
}
</script>
<style scoped>
</style>
回到顶部,有点问题,滚动条没有会去,其实是回到顶部了,有看出问题的也欢迎提留言
定义全局组件:在main.js中
Vue.component('hospitalForm', () => import('@/components/hospitalForm'))
接下来就是使用了
<hospital-form styles="width: 225px" :hospital-no.sync="ruleForm.hospitalNo" />
其中子组件改变父组件传过来的值并没有保存是因为使用了.sync修饰符
https://www.jianshu.com/p/3dbbbc7a259c
最终实现结果为
欢迎关注我的公众号:程序员蜗牛,会有惊喜哦