Vue 手寫組件之實現一個可顯示圖片的自定義遠程搜索框

背景

在實際的vue項目開發中,通常與element-ui搭配使用的比較多,每每能知足大部分的需求。
可是有時某些需求element-ui並不能知足,例如如今咱們有這樣一個場景:遠程搜索框的下拉列表項不單單能顯示文字內容,還要將搜索到的內容的圖片顯示出來css

首先,咱們來看一下官網上的這個組件的功能(el-autocomplete),以下圖,這個是element-ui官網爲咱們提供的遠程搜索組件,它可以在咱們輸入時,根據輸入內容實時從遠程服務器獲取內容並顯示在下拉框中。
element-ui提供的遠程搜索組件vue

Element-ui列表中對應的內容顯然 是搜索到的內容的value字段,是一個字符串,是沒法知足咱們放入圖片的需求的。ajax

image.png

這時就須要咱們本身去封裝一個自定義的能顯示圖片的遠程搜索框了。element-ui

實現

原理其實很簡單,主要有這麼幾點:segmentfault

  1. 監聽文本框的輸入事件,而且爲其添加防抖(性能上的優化,使得中止輸入一段時間後再去調用接口獲取內容)
  2. 使用v-for列表渲染獲取到的內容項,放在輸入框下方
  3. 爲列表設置最大高度(不然高度會根據搜索到的內容個數無限向下延長),超過這個高度加滾動條(max-height和overflow: auto)
  4. 點擊其餘區域或者選中某一項時列表框隱藏
  5. 其餘樣式:爲每項加邊框,與相鄰項隔離; 列表框的下方添加陰影效果; 每項鼠標移動進入時顏色置灰,鼠標移出時恢復

最終實現效果如圖:
遠程搜索框實現效果
代碼以下:數組

// 封了一個從服務器遠程獲取搜索內容的輸入框,比autocompleteuole多了在列表中顯示圖片的功能
<template>
  <div class="container">
    <!-- 輸入框 -->
    <el-input
      placeholder="輸入單詞或ID"
      v-model="obj[name]"
      @input="onInput"
      @blur="isShow=false"
      clearable
      style="width: 200px"
    >
    </el-input>
    <!-- 下拉列表框 -->
    <div class="selectLIst" v-if="isShow==true">
      <div v-for="item in resultList" :key="item.id" @click="handleSelect(item)" class="item">
        <span>{{item.value}}</span>  // 文字內容
        <img :src="item.picUrl" style="width: 20px; height: 20px; margin-left: 5px">   // 圖片
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'RemoteSearch',
  props: {
    obj: {
      type: Object
    },
    name: {
      type: String
    },
  },
  data() {
    return {
      timer: null,   // 這裏放外面其實不安全,容易被篡改
      resultList: [],   // 最終要渲染的內容數組
      isShow: false    // 控制下拉列表的顯示與隱藏
    }
  },
  methods: {
    onInput() {
      this.isShow = true
      // 防抖
      if(this.timer) {
        clearTimeout(this.timer)
      }
      this.timer = setTimeout(()=>{
        this.fetchContent()
      },800)
    },
    async fetchContent() {
      this.resultList = []
      let queryObj = {type:1}
      // 判斷 queryString是數字仍是單詞
      if(!isNaN(Number(this.obj[this.name]))) {
        queryObj.id = this.obj[this.name]
      } else {
        queryObj.word = this.obj[this.name]
      }
      // 這裏寫你要請求的地址
      const result = await this.$ajax.post(
        `/admin/incentive/queryRewardDetail`,queryObj
      )
      if (result.data.data) {
        const res = result.data.data
        res.forEach(e => {     // 這裏從返回的結果中選取須要用到的的內容放入resultList
          this.resultList.push({value: e.id+' '+e.word, picUrl: e.wordPic, id: e.id})
        });
      } else {
        this.$message.warning("請求出錯!");
      }
    },
    handleSelect(item) {
      this.obj[this.name] = item.id+''   // 選中後給對應字段賦值
      this.isShow = false   // 選中某一項後隱藏下拉列表框
    }
  }
}
</script>

<style lang="scss" scoped>
.container {
  position: absolute;
  .selectLIst {
    position: relative;
    z-index: 10;
    background-color: #FFFFFF;
    box-shadow: 0 4px 3px #C5C5C5;
    max-height: 200px;
    overflow: auto;
    .item {
      height: 30px;
      display: flex;
      align-items: center;
      justify-content: center;
    }
    .item:hover {
      background-color: #EFEFEF;
    }
  }
  ul {
    list-style: none   // 隱藏無序列表前面的小圓點
  }
}
</style>

直接在須要的頁面引入並使用:安全

// item: { word: '' }
// 假如你要將input框與item的word屬性綁定,則
<RemoteSearch :obj="item" name='word'/>
相關文章
相關標籤/搜索