vue 带搜索框可多选可记录下拉组件

一、组件样式

 二、依赖

elmentui的el-popover组件

三、代码

<!-- 带搜索框支持多选的下拉组件 -->
<template>
  <div class="vue-dropdown default-theme" ref="select_box">
    <!-- @click="isShow =! isShow" -->
    <div class="cur-name"  v-popover:popoverSelect>
      <input type="hidden" v-model="value" />
      <span class="one-ellipsis" style="100%;display:inline-block;">{{ selectValue }}</span>
    </div>
    <!-- 下拉弹框 -->
    <el-popover v-model="isShow"
      ref="popoverSelect"
      popper-class="select-popover"
      placement="bottom"
      :width="popwidth"
      trigger="click">
        <div class="search-module clearfix" v-show="isNeedSearch">
          <input class="search-text" v-model="searchText" />
        </div>
        <ul class="list-module">
          <li
            v-for="(item,index) in itemlist"
            @click="selectToggle(item)"
            :class="activeValue===item.name ? 'isactive':''"
            :key="index"
          >
            <span class="list-item-text">{{item.name}}</span>
            <span class="'el-icon-check'" v-if="multiple&&item.checked"></span>
          </li>
        </ul>
        <div class="tip-nodata" v-show="isNoData">No results matched "{{searchText}}"</div>
    </el-popover>
  </div>
</template>

<script>
export default {
  data() {
    return {
      selectValue: "",
      activeValue: "", // 当前选中项
      itemlist: [], // 全部下拉选项
      isShow: false, // 下拉框是否显示
      isNoData: false, // 是否无数据
      searchText: "", // 搜索词
      selectData: [], // 选中的数据
      pop 200, // 下拉框的宽度
    };
  },
  props: {
    placeholder: { //input placeholder的默认值
      type: String,
      default: "请选择" 
    },
    isNeedSearch: {
      //是否需要搜索框
      type: Boolean,
      default: false
    },
    nodatatext: { //没有搜索到时的文本提示
      type: String,
      default: "未找到结果" 
    },
    value: { // 默认选中的值
      type: String
    },
    optionlist: { //选择项数组
      type: Array 
    },
    multiple: { // 是否可以多选      
      type: Boolean,
      default: false
    },
    record: {
      // 是否记录上一次的选择
      type: String, // 记录的识别ID
      default: ''
    },
  },
  watch: {
    searchText(newVal, oldVal) { // 搜索框实时检索
      this.search(newVal);
    },
    optionlist: { // 选项列表变化时
      deep: true,
      handler: function (newVal,oldVal){
        this.init()
      }
    },
    value(newVal, oldVal) { // 外部传入的选中值变化时
      console.log(newVal)
      this.initValue()
    }
  },
  mounted() {
    this.init()
    this.popwidth = this.$refs.select_box.clientWidth
  },
  methods: {
    // 初始选中值回显
    initValue() {
       let that = this
      let valueSelected = this.optionlist.filter(option => option.id === that.value)
      let valueName = '' 
      if(valueSelected.length){
        valueName = valueSelected[0].name
      }
      this.selectValue = valueName ? valueName : this.placeholder;
      this.activeValue = this.selectValue;      
    },
    // 初始化数据
    init() {
      let that = this
      this.itemlist = this.optionlist;
      this.initValue()
      if(this.record) { // 如果有记录功能
        let selected = localStorage.getItem('recordInfo_'+this.record)      
        if(selected){
          selected = JSON.parse(selected)
          let recordList = []
          selected.map(item => {
            let filter = that.optionlist.filter(option => option.id === item.id)
            if(filter.length){
              recordList.push(filter[0])
            }
          })    
          localStorage.setItem('recordInfo_'+this.record, JSON.stringify(recordList))
          if(recordList.length){
            this.selectToggle(recordList[0])
          } else {
            this.selectToggle(this.optionlist[0])
          }
        } else {
          this.selectToggle(this.optionlist[0])
        }
      }
      //点击组件以外的地方,收起
      document.addEventListener(
        "click",
        e => {
          if (!this.$el.contains(e.target)) {
            this.isShow = false;
          }
        },
        false
      );
    },
    // 点击选中事件
    selectToggle(data) {
      if (this.multiple) { // 如果是多选
        data.checked = !data.checked;
        if (data.checked) {
          this.selectData.push(data);
        } else {
          let index = this.selectData.findIndex(item => item.id === data.id);
          this.selectData.splice(index, 1);
        }
        let selectName = this.selectData.map(item => item.name);
        if (selectName.length) {
          this.selectValue = selectName.join(",");
        } else {
          this.selectValue = "";
        }
        this.activeValue = this.selectValue;
        this.setRecordList(this.selectData)       
        this.$emit("item-click", this.selectData);
      } else { // 只能单选时
        this.isShow = false;
        this.selectValue = data.name;
        this.activeValue = this.selectValue;
        this.setRecordList(data)        
        this.$emit("item-click", data);
      }
    },
    // 设置选中记录
    setRecordList(data) {
      if(this.record) {
        let recordList = localStorage.getItem('recordInfo_'+this.record)
        if(recordList) {
          recordList = JSON.parse(recordList)
        } else {
          recordList = []
        }
        let index = recordList.findIndex(item => item.id === data.id)
        if(index > -1) {
          recordList.splice(index,1)
        }
        recordList.unshift(data)
        if(recordList.length>3) {
          recordList = recordList.slice(0, 3)
        }
        localStorage.setItem('recordInfo_'+this.record, JSON.stringify(recordList))
      }
    },
    // 搜索事件
    search(val) {
      this.itemlist = this.optionlist.filter(item => {
        return item.name.indexOf(val) != -1;
      });
      if (this.itemlist.length > 0) {
        this.activeValue = this.itemlist[0].name;
        this.isNoData = false;
      } else {
        this.isNoData = true;
      }
    }
  }
};
</script>

<style lang="stylus" scoped>
.list-and-search {
  margin-top: 1px;
  min- 100%;
  z-index: 1000;
  background: #fff;
  border: 1px solid #cfcfcf;
  // border-radius: 4px;
  // position: absolute;
  // box-shadow: 5px 5px rgba(102, 102, 102, 0.1);
  display: none;

  &.on {
    display: block;
  }
}

.cur-name {
  height: 100%;
  line-height: 1.44;
  position: relative;
  border: 1px solid #cfcfcf;
  border-radius: 4px;
  outline: none;
  color: #555;
  cursor: pointer;
  font-size: 14px;
  padding: 6px 25px 6px 12px;

  &::after {
    display: inline-block;
    content: '';
     8px;
    height: 8px;
    margin-left: 2px;
    border-bottom: 1px solid #999;
    border-left: 1px solid #999;
    position: absolute;
    top: 50%;
    right: 12px;
    margin-top: -7px;
    vertical-align: middle;
    transform: rotate(-45deg);
  }
}

.vue-dropdown.default-theme {
   100%;
  height: 32px;
  display: inline-block;
  vertical-align: middle;
  // z-index: 10;
  cursor: pointer;
  -webkit-user-select: none;
  user-select: none;
  position: relative;

  /* &:focus{
      background-color: #d4d4d4;
      border-color: #8c8c8c;
  } */
  &._self-show {
    display: block !important;
  }
  input::-webkit-input-placeholder {
    font-size: 14px;
  }  
}

.tip-nodata {
  padding: 3px;
  background: #f5f5f5;
  margin: 0 5px;
  white-space: nowrap;
  font-size: 14px;
  color: #333;
}
</style>

四、elmentui组件样式修改

.el-popper[x-placement^=bottom], .el-popper[x-placement^=right]{
    &.select-popover{ // 可搜索可多选下拉组件
    margin-top: 2px;
    background: #fff;
    box-shadow: none;
    padding: 0;
    .popper__arrow{
      display: none;
    }  
    .search-module {
      position: relative;
      padding: 4px 8px;
      .search-text {
         100%;
        height: 30px;
        // text-indent: 10px;
        padding: 6px 12px;
        font-size: 14px;
        border: 1px solid #cfcfcf;
        border-radius: 4px;
        box-shadow: none;
        outline: none;
      }
    }

    input::-webkit-input-placeholder {
      font-size: 14px;
    }  
    .list-module {
      max-height: 200px;
      overflow-y: auto;

      li {
        &._self-hide {
          display: none;
        }
        cursor: pointer;
        padding: 0 15px;
        height : 36px;
        line-height : 36px;
        color: #555;
        &:hover{
          background: #ddeeff;
        }   
        &.isactive {       
          background-color: #f5f5f5;
        }
      }
    }
  }
  /deep/ .popover-item{
    border-bottom: 1px solid #EEEEEE;
    height:70px;
    line-height:30px;
    &:last-child{
      border-bottom: none;
    }
  }
}
原文地址:https://www.cnblogs.com/phoebeyue/p/12846587.html