Vue type:ahead 구성 요소 기능 구현(매우 신뢰 할 수 있 음)

11769 단어 vuetypeahead
머리말
이전에 그 typeahead 는 너무 일찍 써 서 현재 의 업무 수 요 를 만족 시 키 지 못 했다.
그리고 하 자 도 있 고 데이터 와 응답 데이터 가 들 어 오기 도 불편 합 니 다.
그래서 밀 어 버 리 고 다시 V2 버 전 을 썼 습 니 다.
그림 을 보고 세부 적 인 고려 가 많아 졌 다.실현 되 는 논리 코드 를 간소화 하 였 다.
효과 도
这里写图片描述
실 현 된 기능
1:드 롭 다운 상자 이외 의 영역 을 마우스 로 클릭 하여 드 롭 다운 상 자 를 닫 습 니 다.
2:키보드 상하 키 선택 지원,마우스 선택 지원
3:지원 목록 필터 검색
4:외부 전송 목록 JSON 형식의 맵 지원
5:placeholder 의 전송 지원
6:선택 한 대상 의 응답(.sync vue 2.3 구성 요소 통신 의 문법 사탕)
7:화살표 icon 의 맵,느낌 이 크 지 않 습 니 다.제거 되 었 습 니 다.
사용법

<select-search 
style="max-width:195px" 
placeholder="      " 
:asyncData.sync="adHostData" 
:mapData="adHostDataList" 
:mapDataFormat="{label:'userName',value:'userId'}">
</select-search>
  • asyncData:응답 하 는 데이터,즉 선택 한 것 입 니 다.돌아 오 는 대상 입 니 다
  • mapData:검색 한 목록 데 이 터 는 외부 에서 들 어 온 것 이 분명 합 니 다.
    mapData:목록 값 맵코드
    selectSearch.vue
    
    <template>
     <div class="select-search" v-if="typeaheadData" ref="selectSearch" @click.native="showHideMenu($event)">
      <div class="select-header">
       <input type="text" autocomplete="off" readonly :placeholder="placeholder" :value="placeholderValue" @keydown.down.prevent="selectChildWidthArrowDown" @keydown.up.prevent="selectChildWidthArrowUp" @keydown.enter="selectChildWidthEnter">
       <i class="fzicon " :class="isExpand?'fz-ad-jiantou1':'fz-ad-jiantou'"></i>
      </div>
      <div class="select-body" v-if="isExpand && typeaheadData">
       <input type="text" placeholder="   " v-model="searchVal" autocomplete="off" @keydown.esc="resetDefaultStatus" @keydown.down.prevent="selectChildWidthArrowDown" @keydown.up.prevent="selectChildWidthArrowUp" @keydown.enter="selectChildWidthEnter">
       <transition name="el-fade-in-linear" mode="out-in">
        <div class="typeahead-filter">
         <transition-group tag="ul" name="el-fade-in-linear" v-show="typeaheadData.length>0">
          <li v-for="(item,index) in typeaheadData" :key="index" :class="item.active ? 'active':''" @mouseenter="setActiveClass(index)" @mouseleave="setActiveClass(index)" @click="selectChild(index)">
           <a href="javascript:;" rel="external nofollow" >
            {{item[mapDataFormat.label]}}
           </a>
          </li>
         </transition-group>
         <p class="noFound" v-show="typeaheadData && typeaheadData.length === 0">     ,     !</p>
        </div>
       </transition>
      </div>
     </div>
    </template>
    <script>
     export default {
      name: 'selectSearch',
      data: function () {
       return {
        placeholderValue: '',//         
        isExpand: false,
        searchVal: '', //      
        resultVal: '', //        
        searchList: [], //        
        currentIndex: -1, //        index,
       }
      },
      computed: {
       mapFormatData () { //             mapData
        return this.mapData.map(item => {
         item[this.mapDataFormat.value] = item[this.mapDataFormat.value];
         return item;
        });
       },
       typeaheadData () {
        let temp = [];
        if (this.searchVal && this.searchVal === '') {
         return this.mapFormatData;
        } else {
         this.currentIndex = -1; //           
         this.mapFormatData.map(item => {
          if (item[this.mapDataFormat.label].indexOf(this.searchVal.toLowerCase().trim()) !== -1) {
           temp.push(item)
          }
          return item;
         })
         return temp;
        }
       }
      },
      props: {
       placeholder: {
        type: String,
        default: '--   --'
       },
       emptyText: {
        type: String,
        default: '    '
       },
       mapData: { //          
        type: Array,
        default: function () {
         return []
        }
       },
       mapDataFormat: { //          
        type: Object,
        default: function () {
         return {
          label: 'text',
          value: 'value',
          extraText: 'extraText'
         }
        }
       },
       asyncData: { //       
        type: [Object, String],
        default: function () {
         return {}
        }
       }
      },
      methods: {
       showHideMenu (e) { //             
        if (e) {
         if (this.$refs.selectSearch && this.$refs.selectSearch.contains(e.target)) {
          this.isExpand = true;
         } else {
          this.isExpand = false;
         }
        }
       },
       resetDefaultStatus () { //         
        this.searchVal = '';
        this.currentIndex = -1;
        this.typeaheadData.map(item => {
         this.$delete(item, 'active');
        })
       },
       setActiveClass (index) { //        
        this.typeaheadData.map((item, innerIndex) => {
         if (index === innerIndex) {
          this.$set(item, 'active', true);
          this.currentIndex = index; //         index,          ,     
         } else {
          this.$set(item, 'active', false)
         }
        })
       },
       selectChildWidthArrowDown () {
        //   index    
        if (this.currentIndex < this.typeaheadData.length) {
         this.currentIndex++;
         this.typeaheadData.map((item, index) => {
          this.currentIndex === index ? this.$set(item, 'active', true) : this.$set(item, 'active', false);
         })
        }
       },
       selectChildWidthArrowUp () {
        //   index    
        if (this.currentIndex > 0) {
         this.currentIndex--;
         this.typeaheadData.map((item, index) => {
          this.currentIndex === index ? this.$set(item, 'active', true) : this.$set(item, 'active', false);
         })
        }
       },
       selectChildWidthEnter () {
        //          ,     
        if (this.typeaheadData.length === 1) {
         this.$emit('update:asyncData', this.typeaheadData[0]); // emit    
         this.placeholderValue = this.typeaheadData[0][this.mapDataFormat.label];
        } else {
         //                  ,     
         this.typeaheadData.map(item => {
          if (this.searchVal === item[this.mapDataFormat.label] || item.active === true) {
           this.$emit('update:asyncData', item); // emit    
           this.placeholderValue = item[this.mapDataFormat.label];
          }
         })
        }
        this.isExpand = false;
       },
       selectChild (index) {
        //         
        this.typeaheadData.map((item, innerIndex) => {
         if (index === innerIndex || item.active) {
          this.placeholderValue = item[this.mapDataFormat.label];
          this.$emit('update:asyncData', item); // emit    
         }
        });
        this.isExpand = false;
       },
      },
      mounted () {
       window.addEventListener('click', this.showHideMenu);
      },
      beforeDestroy () {
       window.removeEventListener('click', this.showHideMenu);
      },
      watch: {
       'isExpand' (newValue) {
        if (newValue === false) {
         this.resetDefaultStatus();
        }
       }
      }
     }
    </script>
    <style scoped lang="scss">
     .el-fade-in-linear-enter-active,
     .el-fade-in-linear-leave-active,
     .fade-in-linear-enter-active,
     .fade-in-linear-leave-active {
      transition: opacity .2s linear;
     }
     .el-fade-in-enter,
     .el-fade-in-leave-active,
     .el-fade-in-linear-enter,
     .el-fade-in-linear-leave,
     .el-fade-in-linear-leave-active,
     .fade-in-linear-enter,
     .fade-in-linear-leave,
     .fade-in-linear-leave-active {
      opacity: 0;
     }
     .noFound {
      text-align: center;
     }
     .select-search {
      position: relative;
      z-index: 1000;
      a {
       color: #333;
       text-decoration: none;
       padding: 5px;
      }
      ul {
       list-style: none;
       padding: 6px 0;
       margin: 0;
       max-height: 200px;
       overflow-x: hidden;
       overflow-y: auto;
       li {
        display: block;
        width: 100%;
        padding: 5px;
        font-size: 14px;
        padding: 8px 10px;
        position: relative;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
        color: #48576a;
        height: 36px;
        line-height: 1.5;
        box-sizing: border-box;
        cursor: pointer;
        &.active {
         background-color: #20a0ff;
         a {
          color: #fff;
         }
        }
       }
      }
      .select-header {
       position: relative;
       border-radius: 4px;
       border: 1px solid #bfcbd9;
       outline: 0;
       padding: 0 8px;
       >input {
        border: none;
        -webkit-appearance: none;
        -moz-appearance: none;
        appearance: none;
        width: 100%;
        outline: 0;
        box-sizing: border-box;
        color: #1f2d3d;
        font-size: inherit;
        height: 36px;
        line-height: 1;
       }
       >i {
        transition: all .3s linear;
        display: inline-block;
        position: absolute;
        right: 3%;
        top: 50%;
        transform: translateY(-50%);
       }
      }
      .select-body {
       position: absolute;
       border-radius: 2px;
       background-color: #fff;
       box-sizing: border-box;
       margin: 5px 0;
       padding: 8px;
       width: 100%;
       box-shadow: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04);
       >input {
        -webkit-appearance: none;
        -moz-appearance: none;
        appearance: none;
        background-color: #fff;
        background-image: none;
        border-radius: 4px;
        border: 1px solid #bfcbd9;
        box-sizing: border-box;
        color: #1f2d3d;
        font-size: inherit;
        height: 36px;
        line-height: 1;
        outline: 0;
        padding: 3px 10px;
        transition: border-color .2s cubic-bezier(.645, .045, .355, 1);
        width: 100%;
        display: inline-block;
        &:focus {
         outline: 0;
         border-color: #20a0ff;
        }
       }
      }
     }
    </style>
    총결산
    위 에서 말 한 것 은 소 편 이 소개 한 Vue 가 type:ahead 구성 요소 기능(매우 믿 을 수 있 습 니 다)을 실현 하 는 것 입 니 다.여러분 에 게 도움 이 되 기 를 바 랍 니 다.궁금 한 점 이 있 으 시 면 메 시 지 를 남 겨 주세요.소 편 은 제때에 답 해 드 리 겠 습 니 다.여기 서도 저희 사이트 에 대한 여러분 의 지지 에 감 사 드 립 니 다!

    좋은 웹페이지 즐겨찾기