需求
element-ui的 el-autocomplete 组件支持远程搜索输入相关数据,可以翻页,
要求实现下滑到底部的时候,触发翻页请求,查看更多结果
实现效果
实现思路
通过监听到滚动事件,判断是否到达底部,然后进行page++ 搜索
通过自定义指令v-autocomplete-scroll 来监听滚动事件,需要注意事件的监听与移除监听
封装组件
<template>
<el-autocomplete
:disabled="disabled"
:highlight-first-item="highlightFirstItem"
popper-class="eng-mo-autocomplete"
v-model="keyword"
:fetch-suggestions="querySearch"
:debounce="debounce"
:hide-loading="hideLoading"
:clearable="clearable"
ref="autocomplete"
v-autocomplete-scroll="handleScroll"
:placeholder="placeholder"
@select="handleSelect"
@focus="handleFocus"
@blur="handleBlur"
>
</el-autocomplete>
</template>
<script>
export default {
name: 'search-input',
model: {
prop: 'value',
event: 'change'
},
props: {
value: {
type: String,
default: ''
},
/**
* 查询的url 默认总包的工程名称
* 获取nc工程编号列表
* /engineering/searchCondition/getNcProjectNoList/{moduleType}
* moduleType:1 总包;2 分包
* 获取工程名称列表
* /engineering/searchCondition/getProjectNameList/{moduleType}
* moduleType:1 总包;2 分包
*/
url: {
type: String,
default: '/engineering/searchCondition/getProjectNameList/1'
},
// 查询的字段 默认工程名称
filed: {
type: String,
default: 'projectName' // ncProjectNo 工程编号 projectName 工程名称
},
placeholder: {
type: String,
default: '请输入'
},
disabled: {
type: Boolean,
default: false
},
// 是否默认突出显示远程搜索建议中的第一项
highlightFirstItem: {
type: Boolean,
default: false
},
clearable: {
type: Boolean,
default: true
},
hideLoading: {
type: Boolean,
default: true
},
// 获取输入建议的去抖延时
debounce: {
type: Number,
default: 300
}
},
computed: {},
data() {
return {
inputWidth: 50,
page: 1,
size: 20,
keyword: this.value,
loading: false
}
},
watch: {
value(newVal) {
this.keyword = newVal
},
keyword(val) {
this.$emit('change', val)
}
},
directives: {
'autocomplete-scroll': {
bind(el, binding, vnode) {
// 此处为了简单,直接判断触底了
function handleScroll(e) {
let isBottom =
e.target.clientHeight + e.target.scrollTop >= e.target.scrollHeight - 1
console.log(e.target.clientHeight + e.target.scrollTop, e.target.scrollHeight, vnode.context.loading, vnode.context, 'debounce')
if (isBottom && !vnode.context.loading) {
console.log(e.target.clientHeight + e.target.scrollTop, e.target.scrollHeight, vnode.context.loading, 'debounce')
binding.value()
}
}
// 监听滚动
let wrapDom = el.querySelector('.el-autocomplete-suggestion__wrap')
el.__handleScroll__ = handleScroll
el.__wrapDom__ = wrapDom
wrapDom.addEventListener('scroll', handleScroll, false)
},
unbind(el, binding, vnode) {
console.log('unbind')
// 解除事件监听
el.__wrapDom__.removeEventListener('scroll', el.__handleScroll__, false)
}
}
},
mounted() {
},
methods: {
// 分页搜索
async search(keywords) {
console.log('page', this.page)
// let start = (this.page - 1) * this.size
// let end = start + this.size
let list = []
const queryParam = {}
queryParam[this.filed] = this.keyword
queryParam.size = this.size
queryParam.current = this.page
// this.$refs['autocomplete'].loading = true
this.loading = true
let {data} = await this.$http({
url: this.$http.adornUrl(this.url),
method: 'post',
data: queryParam
}).finally(() => {
// this.$refs['autocomplete'].loading = false
this.loading = false
})
console.log(data, 'url')
if (data && data.page && data.page.page && data.page.page.records) {
list = data.page.page.records.map(item => {
return {
id: item.id,
value: item[this.filed]
}
})
}
return list
},
// 焦点触发搜索第一页
async querySearch(queryString, cb) {
this.page = 1
let list = await this.search(queryString)
// 调用 callback 返回建议列表的数据
cb(list)
},
// 滚动触发翻页
async handleScroll() {
console.log('handleScroll')
// 限制翻页限度
if (this.page > 20) {
return
}
this.page++
let list = await this.search(this.keywords)
console.log(list, this.page)
this.$refs['autocomplete'].$data.suggestions.push(...list)
},
handleFocus(el) {
console.log(el, 'focus')
// 避免上次数据影响
this.$refs['autocomplete'].suggestions.splice(0, this.$refs['autocomplete'].suggestions.length)
this.$emit('focus', el)
},
handleBlur(el) {
console.log(el, 'handleBlur')
// 避免上次数据影响
// this.$refs['autocomplete'].suggestions.splice(0, this.$refs['autocomplete'].suggestions.length)
this.$emit('blur', el)
},
handleSelect(item) {
console.log(item)
this.$emit('handleSelect', item)
}
}
}
</script>
<style lang="scss">
.eng-mo-autocomplete {
width: auto !important;
}
</style>