直接上代码:
首先是全局指令:
/**
* el-select加载事件(可用于做select的无限滚动)
* 使用方法(两种方法,推荐第2种):
* 1. 查询方法自定义(远程搜索也需要自定义):
* v-load-more.method="function(){}"
* 2. 传入url就可实现无限滚动和远程搜索,要求接口分页参数为“pageSize”和“pageNo”:
* v-load-more="{url:'operationSupport_qualification/ColBase/findAllByPage', options: relColIdOptions}"
* url:(必传)后端接口地址
* options:(必传)el-option中用来v-for的对象数组
* modelField: (必传)用于回显时查询用的字段以及返回时对应key的字段
* pageSize:(非必传)每次加载的条数,不传则默认10
* model: (非必传)用于回显时查询用的字段值,填了这一项才能回显,这一项和el-select的v-model相等,要求后端接口支持
* 回显还有另一种用法,就是给options设置初始值,初始值内容为需要回显的内容
* searchField:(非必传)远程搜索用的字段,不传则不能实现远程搜索;
* 要求接口支持和el-select开启搜索(filterable,并重置自定义搜索方法(:filter-method="()=>{}"))
* searchData:(非必传)远程搜索固定内容
*/
Vue.directive('loadMore', {
bind(el, binding) {
// 如果有method由调用方实现,没有则在这里实现加载和远程搜索的功能
if (binding.modifiers.method) {
// 节流
let timer
// 滚动监听
el.querySelector('.el-select-dropdown .el-select-dropdown__wrap').addEventListener('scroll', function() {
const condition = this.scrollHeight - this.scrollTop <= this.clientHeight + 100
if (!timer && condition) {
// 滚动加载(调用自定义的加载方法)
binding.value()
timer = setTimeout(() => {
clearTimeout(timer)
timer = null
}, 500)
}
})
} else {
// 传入的对象
let value = binding.value
// 节流
let timer
// 无搜索内容变量
let pageNo = 1
let pages = 1
// 远程搜索内容变量
let searchPageNo = 1
let searchPages = 1
// 每次加载的条数
let pageSize = isNaN(value.pageSize) ? 10 : parseInt(value.pageSize)
// 远程搜索变量
let searchField = value.searchField
// 搜索固定内容
let searchData = value.searchData
// 接口地址
let url = value.url
// 下拉数组,这个options在本方法中必须永远指向value.options,否则整个功能都将失效
let options = value.options
// 无搜索拷贝数组,此处是为了在加载的基础上加一些默认的下拉项
let optionsCopy = JSON.parse(JSON.stringify(value.options))
// 远程搜索拷贝数组
let optionsSearch = []
// 远程搜索内容
let searchValue = ''
// 加载逻辑
const loadOptions = (searchField, search, searchData) => {
let params = {
pageSize: pageSize
}
if (searchData) {
Object.assign(params, searchData)
}
// 这里不能改变options的指向,否则会使整个功能失效(不能用options = [])
options.length = 0
// 判断是否为远程搜索,true-是
if (searchField && search) {
// 当到最大页数时不再查询
if (searchPages >= searchPageNo) {
params.pageNo = searchPageNo++
params[searchField] = search
api[url](params).then(res => {
if (res) {
searchPages = Math.ceil(res.data.total / pageSize)
optionsSearch = optionsSearch.concat(res.data.data)
dataProcessing(optionsSearch)
}
})
}
} else {
// 当到最大页数时不再查询
if (pages >= pageNo) {
params.pageNo = pageNo++
api[url](params).then(res => {
if (res) {
pages = Math.ceil(res.data.total / pageSize)
optionsCopy = optionsCopy.concat(res.data.data)
dataProcessing(optionsCopy)
}
})
}
}
}
// 返回数据处理
let dataProcessing = (optionsCopy) => {
// 这里不能改变options的指向,否则会使整个功能失效
optionsCopy.forEach(item => {
let check = options.find(t => {
return t[value.modelField] === item[value.modelField]
})
if (!check) {
options.push(item)
}
})
}
// 首次加载
loadOptions(undefined, undefined, searchData)
// 判断是否需要回显
if (value.model && value.modelField) {
// 回显方法
let echo = (model, modelField, searchData) => {
let params = {}
params[modelField] = model
if (searchData) {
Object.assign(params, searchData)
}
api[url](params).then(res => {
if (res) {
optionsCopy = optionsCopy.concat(res.data.data)
dataProcessing(optionsCopy)
}
})
}
if (optionsCopy.length > 0) {
let check = optionsCopy.find((item) => {
return item[value.modelField] === value.model
})
if (!check) {
echo(value.model, value.modelField, searchData)
}
} else {
echo(value.model, value.modelField, searchData)
}
}
// 滚动监听(无限滚动)
el.querySelector('.el-select-dropdown .el-select-dropdown__wrap').addEventListener('scroll', function() {
const condition = this.scrollHeight - this.scrollTop <= this.clientHeight + 100
if (!timer && condition) {
// 滚动加载
loadOptions(searchField, searchValue, searchData)
timer = setTimeout(() => {
clearTimeout(timer)
timer = null
}, 200)
}
})
// 输入监听(远程搜索)
if (searchField) {
const elInput = el.getElementsByTagName('input')[0]
// 输入搜索
elInput.addEventListener('input', function() {
if (this.value) {
searchPageNo = 1
searchPages = 1
optionsSearch = []
searchValue = this.value
loadOptions(searchField, searchValue, searchData)
} else {
searchValue = ''
dataProcessing(optionsCopy)
}
})
// 失去焦点时清除输入内容
elInput.addEventListener('blur', function() {
searchValue = ''
dataProcessing(optionsCopy)
})
}
}
}
})
方法一(不推荐):
(方法一只实现了无限滚动,如果需要其他的,可以参考方法二,这里不推荐是因为用到的代码还是会很多,方法都需要自己去定义)
<el-select v-model="id"
filterable
placeholder="请选择关联人员"
v-load-more.method="load"
style="width: 100%">
<el-option
v-for="item in options"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
data() {
return {
options:[],
pages: 1,
pageNo: 1,
pageSize: 10,
id: ''
}
}
methods: {
load() {
if (this.pages >= this.pageNo) {
// todo 这里的请求是我们项目自己封装的,需要换成你们自己的请求方法
this.$api['url']({
pageSize: this.pageSize,
pageNo: this.pageNo++
}).then(res => {
if (res) {
this.pages = Math.ceil(res.data.total / this.pageSize)
this.options.push(...res.data.data)
}
})
}
}
}
mounted() {
this.load()
}
方法二(推荐):
<el-select v-model="id"
filterable
:filter-method="()=>{}"
v-load-more="{
url:'url',
options: options,
model: id,
modelField: 'id',
searchField: 'name'}"
style="width: 100%">
<el-option
v-for="item in options"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
data() {
return {
options:[],
id: ''
}
}
ps:如果有不会用、看不懂和可以改进的地方,都可以提出来,我会实时更新