PaginationSearchSelect - 自定义分页搜索选择器组件
2023-02-17
一、 问题说明及解决方案
1. 问题说明
主要问题来源来自一个下拉框中有四千多条数据,默认官方的Select会一次性加载所有数据,并不含分页加载功能。 在性能较差的电脑上会造成页面卡死、卡顿等情况,搜索速度过慢。
2. 解决方案
根据官方文档:选择器 Select
事件名称 | 说明 | 回调参数 |
---|---|---|
popupScroll | 下拉列表滚动时的回调 | function |
通过该事件我们可以获取滚动时Scroll的高度及最低高度,这样我们便可以进行动态的数据加载。
配合a-spin组件 可以带来比较好的用户体验
二、 组件使用说明
1. 前端组件说明
1.1 必选项
参数 | 说明 | 类型 | 传参Demo |
---|---|---|---|
listUrl | 数据请求地址 | String | |
valueName | 下拉框value的Name | String | valueName="id"即:value="item.id" |
showNameArr | 下拉项中显示的名称数组,多个通过“-”进行连接 | Array | :showNameArr="['name','id']" |
1.2 选传项
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
multiple | 设置 Select 的模式为多选 | Boolean | false |
placeholder | 默认提示文字 | String | 请输入 |
pageSize | 默认分页加载条数 | Number | 10 |
openSearch | 是否开启搜索功能 | Boolean | false |
triggerChange | 回调值的方式,使用v-decorator需要传入true | Boolean | false |
2. 后端接口说明
2.1 必接项
参数 | 说明 | 类型 |
---|---|---|
pageNo | 分页起始值 | Integer |
pageSize | 默认分页条数 | Integer |
2.2 选接项
参数 | 说明 | 类型 |
---|---|---|
searchWorld | 查询条件 | String |
2.3 例
使用Page及IPage进行查询封装,返回使用Result.ok(demoList)
说明:看个人情况使用分页方式,手动传入也是可以的
Page<Object> page = new Page<Standard>(Object, pageSize);
IPage<Object> pageList = testService.getList(page,searchWorld);
return Result.OK(pageList);
三、 组件代码
<template>
<a-spin :spinning="spinning" tip="加载中...">
<a-select
:show-search="openSearch"
:placeholder="placeholder"
@popupScroll="popupScroll"
@search="onSearch"
@change="change"
optionFilterProp="children"
:value="getValueSting"
>
<a-select-option v-for="(item, index) in dataList" :key="index" :value="item[valueName]">
{{ showNames(item) }}
</a-select-option>
</a-select>
</a-spin>
</template>
<script>
import { getAction } from '../../api/manage'
export default {
name: 'PaginationSearchSelect',
props: {
placeholder: {
type: String,
default: '请输入',
required: false,
},
listUrl: {
type: String,
required: true,
},
valueName: {
type: String,
required: true,
},
pageSize: {
type: Number,
default: 10,
required: false,
},
openSearch: {
type: Boolean,
default: false,
required: false,
},
showNameArr: {
type: Array,
required: true,
},
value: {
type: [String,Number],
required: false,
},
triggerChange: {
type: Boolean,
default: false,
required: false,
}
},
data() {
return {
dataList: [],
pageNo: 1,
max: false,
spinning: false,
searchWorld: undefined,
}
},
mounted() {
this.loadDatas()
},
methods: {
loadDatas() {
this.pageNo = 1
var param = {
pageNo: this.pageNo,
pageSize: this.pageSize,
searchWorld: this.searchWorld,
}
getAction(this.listUrl, param).then((res) => {
if (res.success) {
this.dataList = res.result.records
}
})
},
showNames(item){
var str = [];
this.showNameArr.forEach(element => {
str.push(item[element]);
});
return str.join(' - ');
},
filterOption(input, option) {
return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
},
//滚动条滚动监听方法
async popupScroll(e) {
const that = this
const { target } = e
const scrollHeight = target.scrollHeight - target.scrollTop //滑动总高度
const clientHeight = target.clientHeight //滑动最低高度
// 当下拉框滚动条到达底部的时候
if (!this.max && scrollHeight < clientHeight + 2) {
this.spinning = true
//数据起始条数
this.pageNo++
var newList = []
var param = {
pageNo: this.pageNo,
pageSize: this.pageSize,
searchWorld: this.searchWorld,
}
await getAction(this.listUrl, param).then((res) => {
if (res.success) {
newList = res.result.records
if (newList.length == 0) {
that.max = true
}
}
})
//数据追加
this.dataList = this.dataList.concat(newList)
this.spinning = false
}
},
//下拉框搜索方法
onSearch(val) {
if (val.length != 0) {
this.searchWorld = val
this.dataList = []
this.max = false
this.loadDatas()
} else {
this.searchWorld = undefined
this.dataList = []
this.max = false
this.loadDatas()
}
},
change(val){
if(this.triggerChange){
this.$emit('change', val);
}else{
this.$emit('input', val);
}
}
},
}
</script>
<style scoped>
.spin-content {
border: 1px solid #91d5ff;
background-color: #e6f7ff;
padding: 30px;
}
</style>