自定义el-select多选下拉框,添加搜索全选等功能,解决搜索无数据时搜索框消失问题

1,680 阅读3分钟

一、前言

在自定义多选可搜索下拉框的时候,当搜索无匹配数据的时候,下拉框内自定义的搜索框等元素都会消失,该组件完美地解决了这个问题!特此记录一下。

二、实现效果

  1. 选择下拉框打开时可自动聚焦到input输入框;
  2. 点击全选可选择全部item;
  3. 可清空已选择项;
  4. 已选择项在input框里呈同一行可滑动显示;
  5. 可选项过多时搜索框不会跟随着滚动,固定搜索框。

实现效果如下:

企业微信截图_16593222463884.png

三、代码实现

1、创建组件

在父组件Demos创建一个MultipleSelect子组件,如下:

企业微信截图_16593225247132.png

2、MultipleSelect子组件代码

<template>
  <el-select
    multiple 
    v-model="selectNameArr"  
    placeholder="请选择" 
    clearable
    :style="{'width':width}"
    @focus="autoFocusInput('search_input1')"
    @change="changeSelectBrand" 
    @remove-tag="removeTag"
    popper-class="select-multiple">
    <!-- 当下拉框搜索为空时展现的内容 -->
    <template slot="empty">
      <el-row style="padding:6px 10px 0" v-if="!inputVisible">
        <el-col :span="18">
          <span>选择收件人:</span>
          <el-input class="search_input" size="mini" style="width:calc(100% - 100px)" v-model="searchValue" placeholder="请输入关键字搜索" clearable @input="searchUser"></el-input>
        </el-col>
        <el-col :span="6">
          <el-button type="primary" size="mini" @click.native="selectAll()">全选</el-button>
          <el-button type="warning" size="mini" @click.native="clearAll()">清空</el-button>
        </el-col>
      </el-row>
      <el-divider class="row_divider"></el-divider>
      <div class="el-select-dropdown__empty">无匹配数据</div>
    </template>
    <!-- 当下拉框不为空时展现的内容 -->
    <div class="select-box">
      <el-row type="flex" justify="center" style="padding:0 10px" v-if="inputVisible">
        <el-col :span="18">
          <span>选择收件人:</span>
          <el-input class="search_input1" size="mini" style="width:calc(100% - 100px)" v-model="searchValue" placeholder="请输入关键字搜索" clearable @input="searchUser"></el-input>
        </el-col>
        <el-col :span="6">
          <el-button type="primary" size="mini" @click.native="selectAll()">全选</el-button>
          <el-button type="warning" size="mini" @click.native="clearAll()">清空</el-button>
        </el-col>
      </el-row>
      <el-divider class="row_divider"></el-divider>
      <div class="select-content" v-if="!loading">
        <span v-for="item in receiverNameOptModel" :key="item.id">
          <el-tooltip effect="dark" :open-delay="500" :content="item.name+' '+item.name_en" placement="top">
            <el-option :label="item.name+' '+item.name_en" :value="item.name+' '+item.name_en"></el-option>
          </el-tooltip>
        </span>
      </div>
    </div>
  </el-select>
</template>
<script>
export default {
    name: 'MultipleSelect',
    props:{
      receiverNameOpt: { //接受的数据为[{id:1,name:'张三',name_en:'zhangsan',...},...]格式
          type: Array,
          default: ()=>[]
      },
      width:{ //传入宽度值为字符串形式,如'50%'
          type: String,
          default:'100%',
      }
    },
    data(){
      return{
        searchValue: '',
        selectNameArr: [],
        receiverNameOptModel: [],
        loading: false,
        inputVisible: false,
      }
    },
    mounted(){
      this.$bus.$on('clearReciever',this.clearAll)
    },
    watch:{
      receiverNameOpt:{
        immediate:true,
        handler(val){
          if(val.length === 0){
            this.loading = true
          }else{
            this.loading = false
          }
          this.receiverNameOptModel = JSON.parse(JSON.stringify(this.receiverNameOpt))
        }
      },
      receiverNameOptModel:{
        immediate:true,
        handler(val){
          if(val.length === 0){
            this.inputVisible = false
            this.autoFocusInput("search_input")
          }else{
            this.inputVisible = true
            this.autoFocusInput("search_input1")
          }
        }
      },
    },
    methods:{
      // 全选全部品类
      selectAll() {
        this.selectNameArr = this.receiverNameOpt.map(p=>p.name+' '+p.name_en)
        let ret =[]
        ret = this.receiverNameOptModel.filter(p=>{ return this.selectNameArr.includes(p.name+p.name_en) })
        this.$emit('getmultipleReceiver',ret)
      },
      // input自动聚焦
      autoFocusInput(classname) {
        this.$nextTick(() => {
          let dom = document.getElementsByClassName(classname)[0];
          if(dom) setTimeout(()=>{ dom.children[0].focus() },200)
        });
      },
      // 选择收件人
      changeSelectBrand(val) {
        let ret =[]
        ret = this.receiverNameOptModel.filter(p=>{return val.includes(p.name+p.name_en)})
        this.$emit('getmultipleReceiver',ret)
      },
      removeTag(val) {
        for(let i=0;i<this.selectNameArr.length;i++){
          if(this.selectNameArr[i] === val) this.selectNameArr.splice(index,1)
        }
      },
      // 清空已选择的收件人
      clearAll() {
        this.selectNameArr.splice(0,this.selectNameArr.length)
        this.searchValue = ''
        this.receiverNameOptModel = JSON.parse(JSON.stringify(this.receiverNameOpt))
      },
      // 搜索用户收件人
      searchUser(query) {
        if (query !== '') {
          this.receiverNameOptModel = this.receiverNameOpt.filter((p)=>{
            return p.name.toLocaleLowerCase().includes(query.toLocaleLowerCase()) || p.name_en.toLocaleLowerCase().includes(query.toLocaleLowerCase())
          })
        } else {
          this.receiverNameOptModel = JSON.parse(JSON.stringify(this.receiverNameOpt))
        }
      },
    }
}
</script>
<style>
.select-multiple.is-multiple{
  width: 50%;
}
</style>
<style scoped>
.select-multiple .select-content {
  height: max-content;
  height: -webkit-max-content;
  height: -moz-max-content;
  max-height: 200px;
  overflow: hidden;
  overflow-y: auto;
}
.select-multiple .select-content::-webkit-scrollbar{
  width: 2px!important;
}
.select-multiple .el-select-dropdown__item {
  width: 33%;
  height: 26px;
  line-height: 26px;
  margin-right: 1px;
  padding: 0 10px;
  display: inline-block;
  border-bottom: 1px solid #cccccc40;
  border-right: 1px solid #cccccc40;
  box-shadow: 1px 1px 4px #e4e4e4;
}
.select-multiple .el-select-dropdown__item.selected:hover,
.select-multiple .el-select-dropdown__item:hover{
  color: #fff;
  background-color: #409EFF;
}
::v-deep .el-select__tags-text {
  display: inline-block;
  max-width: 80px;
  overflow: hidden;
  text-overflow:ellipsis;
  white-space: nowrap;
}
.is-multiple .select-content ::v-deep .el-select-dropdown__item.selected::after{
  right: 2px!important;
}
::v-deep .el-select__tags {
  flex-wrap: nowrap;
  overflow-x: auto;
  margin-left: 3px;
}
::v-deep .el-select__tags::-webkit-scrollbar{
  height: 2px;
  border-radius: 1px;
  background-color: #e4e4e4;
}
.row_divider{
  margin: 6px 0;
}
</style>

3、Demos父组件调用MultipleSelect子组件

<template>
<!-- 省略... -->
<el-tab-pane label="自定义select下拉框" name="third">
  <MultipleSelect :width="'50%'" :receiverNameOpt="multipleSelectDataList"></MultipleSelect>
</el-tab-pane>
<!-- 省略... -->
</template>
<script>
import MultipleSelect from "./MultipleSelect";
export default {
  name:"Demos",
  components:{ MultipleSelect },
  data() {
    return {
      multipleSelectDataList:[
        {
          id:1,
          name:'张三',
          name_en:'zhangsan',
        },
        {
          id:2,
          name:'李四',
          name_en:'lisi',
        },
        {
          id:3,
          name:'王五',
          name_en:'wangwu',
        },
        {
          id:4,
          name:'赵六',
          name_en:'zhaoliu',
        },
        //省略...
      ],
    }
  }
}
</script>

四、总结

此篇文章的重点就在于搜索无匹配数据时搜索框消失的情况,还有两个搜索inpu框实时切换自动聚焦的问题。共勉^o^