PaginationSearchSelect - 自定义分页搜索选择器组件
2023-02-17

一、 问题说明及解决方案

1. 问题说明

主要问题来源来自一个下拉框中有四千多条数据,默认官方的Select会一次性加载所有数据,并不含分页加载功能。 在性能较差的电脑上会造成页面卡死、卡顿等情况,搜索速度过慢。

2. 解决方案

根据官方文档:选择器 Select

事件名称说明回调参数
popupScroll下拉列表滚动时的回调function

通过该事件我们可以获取滚动时Scroll的高度及最低高度,这样我们便可以进行动态的数据加载。

配合a-spin组件 可以带来比较好的用户体验

二、 组件使用说明

1. 前端组件说明

1.1 必选项

参数说明类型传参Demo
listUrl数据请求地址String
valueName下拉框value的NameStringvalueName="id"即:value="item.id"
showNameArr下拉项中显示的名称数组,多个通过“-”进行连接Array:showNameArr="['name','id']"

1.2 选传项

参数说明类型默认值
multiple设置 Select 的模式为多选Booleanfalse
placeholder默认提示文字String请输入
pageSize默认分页加载条数Number10
openSearch是否开启搜索功能Booleanfalse
triggerChange回调值的方式,使用v-decorator需要传入trueBooleanfalse

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>