Vue 2.x折騰記 - (5) 寫一個不大靠譜的selectSearch組件

前言

這個功能在網站中也很常見,下拉選擇帶搜索,基於以前的typeahead組件的基礎上完善javascript

效果圖

實現邏輯css

  • typeahead的實現大同小異,就是選擇和傳遞數據的細節有所改善和重置默認值的思路有所變更
  • indexOf的比對稍微增強了,字母默認所有小寫,還有輸入的值去除空格

總體來講比typeahead組件更好理解和閱讀html

你能學到什麼?vue

  • css樣式
  • 組件臨時增長的狀態的處理
  • 數據過濾
  • 一些經常使用按鍵的監聽實現(ESC,up,down,Enter),失焦等..
  • 過濾數據的默認選中

代碼

selectSearch.vue

<template>
  <div class="select-search">
    <div class="select-header" @click="isExpand=!isExpand">
      <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">
      <input type="text" placeholder="關鍵字" v-model="searchVal" autocomplete="off" @blur="resetDefaultStatus" @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" v-show="typeaheadData">
          <transition-group tag="ul" name="el-fade-in-linear">
            <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:;">
                {{item.text}}
              </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: { typeaheadData () { let temp = []; if (this.searchVal === '') { return this.mapData; } else { this.currentIndex = -1; // 重置特殊狀況下的索引 this.mapData.forEach(item => { item.active = false; // 取消高亮 if (item.text.indexOf(this.searchVal.toLowerCase().trim()) !== -1) { temp.push(item) } }) return temp; } } }, props: { placeholder: { type: String, default: '--請選擇--' }, mapData: { type: Array, default: function () { return [ { text: 'wofsdf', value: 0 }, { text: '我是技術渣1', value: 1 }, { text: '我是技術渣2', value: 2 }, { text: '我是天坑', value: 3 }, { text: '我是天坑,分身乏術', value: 4 }, { text: '我是天坑2,分身乏術', value: 5 }, { text: '我是天坑3,分身乏術', value: 6 } ] } } }, methods: { resetDefaultStatus () { this.searchVal = ''; this.resultVal = ''; this.currentIndex = -1; this.typeaheadData.forEach(item => { this.$set(item, 'active', false); }) }, setActiveClass (index) { // 設置樣式活動類 this.mapData.forEach((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.forEach((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.forEach((item, index) => { this.currentIndex === index ? this.$set(item, 'active', true) : this.$set(item, 'active', false); }) } }, selectChildWidthEnter () { // 如果結果集只有一個,則默認選中 if (this.typeaheadData.length === 1) { this.searchVal = this.typeaheadData[0].text; this.resultVal = this.typeaheadData[0].value; this.placeholderValue = this.typeaheadData[0].text; this.isExpand = false; this.$emit('selectValue', { text: this.searchVal, value: this.resultVal }) } else { // 如果搜索的內容徹底匹配到項內的內容,則默認選中 this.typeaheadData.forEach(item => { if (this.searchVal === item.text || item.active === true) { this.searchVal = item.text; this.placeholderValue = item.text; this.resultVal = item.value; this.isExpand = false; this.$emit('selectValue', { text: this.searchVal, value: this.resultVal }) } }) } this.resetDefaultStatus(); }, selectChild (index) { // 鼠標點擊選擇子項 this.mapData.forEach((item, innerIndex) => { if (index === innerIndex || item.active) { this.searchVal = item.text; this.resultVal = item.value; this.placeholderValue = item.text; this.isExpand = false; } this.$set(item, 'active', false); }) this.$emit('selectValue', { text: this.searchVal, value: this.resultVal }); 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; } .select-search { a { color: #333; text-decoration: none; padding: 5px; } ul { list-style: none; padding: 6px 0; margin: 0; overflow: visible; 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 { z-index: 1000; border-radius: 2px; background-color: #fff; box-shadow: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04); box-sizing: border-box; margin: 5px 0; padding: 8px; >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; } } } </style>


複製代碼

總結

有更好的方案或者實現的能夠往下留言,謝謝閱讀java

相關文章
相關標籤/搜索