vue3封装一个el-autocomplete下拉分页加载的组件

1,984 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第28天,点击查看活动详情

写在前面

最近在写一个下拉远程搜索输入分页查询的需求,本来使用el-select的remote来实现的,但是在测试过程中发现el-select在输入内容过程中鼠标失焦的话会清空输入的值,并且会触发remote-method方法,但是产品想要的交互是t在输入内容过程中鼠标失焦的话输入的值不会清空。然后查看elementplus文档发现el-autocomplete组件可以达到这个效果。

封装一个el-autocomplete下拉分页加载的组件

在项目开发过程中如果有联想输入的需求的话一般会是使用el-autocomplete组件来实现,但是这样查询的话会匹配到很多数据,接口查询会很慢,因此做成了下拉分页查询。

1. 实现下拉加载的指令

const scrollLoad = {
  bind(el, binding, vnode) {
    let wrapDom = el.querySelector('.el-autocomplete-suggestion__wrap')
    let listDom = el.querySelector('.el-autocomplete-suggestion__wrap  .el-autocomplete-suggestion__list')
    wrapDom.addEventListener(
      'scroll',
      () => {
        // 注意load的使用,节流
        let condition = wrapDom.offsetHeight + wrapDom.scrollTop + 10 - listDom.offsetHeight;
        console.log('condition', condition, vnode.context.loading)
        if (condition > 0 && !vnode.context.loading) {
          //滚动到底部则执行滚动方法load,binding.value就是v-scrollLoad绑定的值,加()表示执行绑定的方法
          binding.value()
        }
      },
      false
    )
  }
};

export default scrollLoad;

然后在main.js中引入指令

import { createApp } from 'vue'
import App from './App.vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import scrollLoad from './directives/scrollLoad.js';

const app = createApp(App);
app.directive('scroll-load', scrollLoad);
app.use(ElementPlus).mount('#app');

2. 封装el-autocomplete组件

需要注意的是在vue3中指令需要绑定在el-autocomplete的上一级元素上,否则会报错

<template>
<div class="autocomplete-wrapper" v-scroll-load="selectLoadMore" v-loading="loading">
  <el-autocomplete
    ref="autocompleteRef"
    value-key="value"
    v-model="state"
    :fetch-suggestions="querySearch"
    :placeholder="placeholder"
    :trigger-on-focus="false"
    @select="handleSelect"
  ></el-autocomplete>
</div>
</template>
 
<script>
export default {
  name: 'InputLoadMore'
}
</script>
<script setup>
import { defineProps, defineEmits, ref, watch } from 'vue';
const props = defineProps({
  // 分页查询数据的方法
  getSelectOpts: {
    require: true
  },
  // 接口联想的key
  searchKey: {
    type: String,
    require: true
  },
  // v-model的绑定值
  value: {
    type: String,
    require: true
  },
  // placehoder
  placeholder: {
    type: String,
    default: '请输入'
  }
});
const emits = defineEmits(['input']);

const state = ref('');
const loading = ref(false);
const page = ref(1);
const pageTotal = ref(0);
const autocompleteRef = ref(null);

watch(
  () => state,
  (val) => {
    emits('input', val);
  },
  () => props.value,
  (val) => {
    state.value = val
  }
);

const querySearch = async (queryString, cb) => {
  page.value = 1
  loading.value = true
  try {
    let { result } = await props.getSelectOpts({
      page: 1,
      pageSize: 20,
      [props.searchKey]: queryString
    })
    // 根据实际接口返回数据格式将数据插入到选项中
    if (result.rows) {
      let arr = []
      result.rows.forEach(item => {
        arr.push({ value: item })
      })
      cb(arr)
    } else {
      cb([])
    }
    pageTotal.value = result.total || 0
  } catch(e) {
    // console.log(e)
  } finally {
    loading.value = false
  }
};
const handleSelect = (item) => {};
// 加载更多
const selectLoadMore = async () => {
  if(Number(pageTotal.value) <= autocompleteRef.value.suggestions.length) {
    return
  }
  page.value++;
  loading.value = true;
  try {
    let { result } = await props.getSelectOpts({
      page: page.value,
      pageSize: 20,
      [props.searchKey]: state.value
    })
    console.log('result', result);
    // 根据实际接口返回数据格式将数据插入到选项中
    if (result.rows) {
      const arr = result.rows.map(item => {
        return { value: item }
      })
      // 将数据添加到下拉列表
      autocompleteRef.value.suggestions = autocompleteRef.value.suggestions.concat(arr)
    }
    pageTotal.value = result.total || 0;
  } catch(e) {
    // console.log(e)
  } finally {
    loading.value = false
  }
}
</script>

3. 在组件中使用

<template>
  <div class="test-wrapper" style="color: blue">
  <AutoComplete :getSelectOpts="getSelectOpts" searchKey="query" :value="value" @input="inputFunc"></AutoComplete>
  </div>
</template>
<script setup>
import { ref } from "vue";
import AutoComplete from './AutoComplete.vue';

const value = ref('');

const getSelectOpts = () => {
  console.log('开始查询数据');
  return [{
    itemKey: 1,
    itemValue: '1'
  },
  {
    itemKey: 1,
    itemValue: '1'
  },
  {
    itemKey: 1,
    itemValue: '1'
  },
  {
    itemKey: 1,
    itemValue: '1'
  },
  {
    itemKey: 1,
    itemValue: '1'
  }]
};

const inputFunc = (data) => {
  console.log('input数据', data);
}

</script>
<style scoped>
</style>

效果如下:

image.png

el-select的remote和el-autocomplete的区别

  • el-autocomplete主要是针对输入建议,value会实时刷新,选中不会有选中效果。
  • el-select value不会实时刷新,选中才会更新value,并且选中会有选中效果。