• 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;
        }
      }
    }
  • 相关阅读:
    WebAPi返回类型到底应该是什么才合适,这是个问题?
    NuGet程序包安装SQLite后完全抽离出SQLite之入门介绍及注意事项,你真的懂了吗?
    完全抽离WebAPi之特殊需求返回HTML、Css、JS、Image
    模板引擎Nvelocity实例
    C#由变量捕获引起对闭包的思考
    AngularJS之指令中controller与link(十二)
    AngularJS之ng-class(十一)
    AngularJS之WebAPi上传(十)
    AngularJS之代码风格36条建议【一】(九)
    两个List合并去重
  • 原文地址:https://www.cnblogs.com/phoebeyue/p/12846587.html
Copyright © 2020-2023  润新知