8

背景

在实际的vue项目开发中,一般与element-ui搭配使用的比较多,往往能满足大部分的需求。
但是有时某些需求element-ui并不能满足,例如现在我们有这样一个场景:远程搜索框的下拉列表项不仅仅能显示文字内容,还要将搜索到的内容的图片显示出来

首先,我们来看一下官网上的这个组件的功能(el-autocomplete),如下图,这个是element-ui官网为我们提供的远程搜索组件,它能够在我们输入时,根据输入内容实时从远程服务器获取内容并显示在下拉框中。
element-ui提供的远程搜索组件

Element-ui列表中对应的内容显然 是搜索到的内容的value字段,是一个字符串,是无法满足我们放入图片的需求的。

image.png

这时就需要我们自己去封装一个自定义的能显示图片的远程搜索框了。

实现

原理其实很简单,主要有这么几点:

  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'/>

晚起的虫儿
545 声望48 粉丝

一起成长~