vue2中使用antd vueUI 二次封装远程搜索下拉框组件,支持滚动分页,单选和多选,最多选择数量限制,滚动获取数据防抖
具体组件代码如下
<template>
<a-select
:disabled="disabled"
:mode="mode"
:maxTagCount="maxTagCount"
show-search
allowClear
:filterOption="false"
:label-in-value="labelInValue"
:placeholder="placeholderStr"
style="width: 100%"
:not-found-content="fetching ? undefined : notFoundContent"
@search="handleSearch1"
@change="handleChange"
@popupScroll="popupScroll1"
:value="selectValue"
:getPopupContainer="(node) => node.parentNode"
>
<a-select-option
v-for="item in dataOptions"
:disabled="item.disabled"
:key="item.id"
:value="item[optionValueKey]"
>{{ item[optionLabelKey] }}</a-select-option
>
<a-select-option :disabled="true" key="loading"
><a-spin :spinning="fetching">
<div style="height: 20px; text-align: center">
{{
dataOptions.length == 0
? notFoundContent
: noMoreData
? '已经到底了~'
: '加载中'
}}
</div>
</a-spin></a-select-option
>
</a-select>
</template>
<script>
import debounce from 'lodash/debounce';
export default {
name: 'RemoteSelect',
props: {
mode: {
type: String,
default: 'default',
},
labelInValue: { type: Boolean, default: false },
maxTagCount: Number,
maxLimit: Number, //多选时,最大数量
placeholderStr: {
type: String,
default: '请输入',
},
url: String,
defaultValue: { type: [Array, String, Number], default: '' }, //初始值
params: {
type: Object,
default: () => ({}),
}, //调用接口的入参字段
searchKey: { type: String, default: 'name' }, //搜索的字段
returnParams: { type: Object, default: () => ({}) }, //调用接口的返回参数
optionValueKey: { type: String, default: 'id' }, //option下拉选项的value用哪个字段
optionLabelKey: { type: String, default: 'name' }, //option下拉选项的显示用哪个字段
method: {
type: String,
default: 'post',
},
showSearch: {
type: Boolean,
default: true,
},
optionFilterProp: {
type: String,
default: 'value',
},
notFoundContent: {
type: String,
default: '暂无查询数据',
},
pageSize: {
type: Number,
default: 50,
},
disabled: {
type: Boolean,
default: false,
},
// 是否默认选择第一条数据
firstItemSelected: {
type: Boolean,
default: false,
},
},
data() {
this.handleSearch1 = debounce(this.handleSearch, 100);
this.popupScroll1 = debounce(this.popupScroll, 100);
return {
dataOptions: [],
fetching: false,
currentPage: 1,
noMoreData: false,
searchValue: '',
selectValue: !this.defaultValue ? undefined : this.defaultValue,
total: 0,
};
},
mounted() {
this.handleSearch();
},
watch: {
defaultValue: {
handler(newVal, oldVal) {
this.handleChange(newVal);
},
},
},
methods: {
async handleSearch(value) {
if (this.fetching == true) return;
this.searchValue = value;
this.currentPage = 1;
this.noMoreData = false;
this.fetchData(value);
},
//处理选择的上限处理逻辑
initSelectOptions() {
if (
this.maxLimit &&
this.selectValue &&
this.selectValue.length >= this.maxLimit
) {
if (!this.labelInValue) {
this.dataOptions.forEach((element) => {
if (!this.selectValue.includes(element[this.optionValueKey])) {
element.disabled = true;
}
});
}
} else {
this.dataOptions.forEach((element) => {
element.disabled = false;
});
}
},
async handleChange(value) {
this.initSelectOptions();
const { onChange } = this;
this.selectValue = value;
console.log('selectValue', this.selectValue);
if (this.input) {
this.$emit('input', value);
}
this.$emit('onChange', value);
if (value == undefined) {
//清除选择项时,需要重新获取下拉框数据
this.handleSearch();
}
},
async fetchData(value) {
if (this.fetching == true) return;
const { url, params, pageSize } = this;
console.log('params', params);
this.fetching = true;
const res = await this.$request.post(url, {
pageIndex: this.currentPage,
pageSize: pageSize,
[this.searchKey]: value,
...params,
});
if (res.data.status == 200) {
const { models, sum } = res.data && res.data.data;
this.total = sum;
if (this.currentPage === 1) {
this.$set(this, 'dataOptions', models);
} else {
this.dataOptions = [...this.dataOptions, ...models];
}
//默认选择第一条数据
if (this.firstItemSelected && this.dataOptions.length > 0) {
this.handleChange(this.dataOptions[0][this.optionValueKey]);
}
if (sum <= this.dataOptions.length) {
this.noMoreData = true;
}
} else {
//接口报错
this.noMoreData = true;
}
this.fetching = false;
},
popupScroll(e) {
const { scrollTop, scrollHeight, clientHeight } = e.target;
if (
!this.noMoreData &&
!this.fetching &&
scrollTop + clientHeight >= scrollHeight - 100
) {
this.currentPage++;
this.fetchData(this.searchValue);
}
},
},
};
</script>