[搜索组件] VUE组件分页搜索

983 阅读3分钟

实现效果

接下来, 我们会围绕组件的几个点展开讨论:

如何让父组件v-model起效 搜索框自动锁定焦点 监听键盘按下触发搜索 组件按字搜索且搜索结果高亮条件

技术点讲解:

1.由于父级监听输入框的值,所以当组件输入值变化的时候,需要把值广播给父级.

// search-components / html
input(
  v-model="text"
)
// watch
/**
 * 检测传入的文本
 * @param {String} newValue 新值
 */
text (newValue) {
  if (!this.searchOnEnter) {
    this.$refs.input.blur()
    //  监听键盘按下触发搜索
    this.$emit('onSearch', newValue)
  }
	// 如何让父组件v-model起效
  this.$emit('input', newValue)
}
  1. 搜索框自动锁定焦点 实现思路有3种方式, 新页面通过mounted触发focus() / 通过$refs.input.focus()触发 / 通过自定义指令 v-focus触发 . 我们重点将用指令怎么实现. 首先在plugins写一个指令函数,其次在组件里面加上v-focus
// plugins/directive.js
/**
 * 设置将元素获得焦点
 */
vue.directive('focus', function (el, {value}) {
  if(value) {
    el.focus()
  }
})
  1. 搜索行为按字触发

     		//使用的时候,监听input值,有变动触发延时一秒的请求   
        toSearch () {
          // 如果搜索框内有值
          if (this.xxx) {
            setTimeout(() => {
              // 一秒内只执行一次
              if (this.lastDelay - event.timeStamp === 0) {
                // TODO
              }
            }, 1000)
          } else {
            // 移除输入框和搜索列表的内容
            this.removeInputValue()
          }
        },
    
  2. 搜索结果高亮条件 实现的思路是前端拿到搜索结果后,遍历结果,找到关键词然后加上样式,最后用v-html渲染.

// let state.clientItem 用户搜索内容

   if (state.clientItem) {
     	// 去除头尾空格
      let inputItem = state.clientItem.trim()
        .split(' ')
        .filter(item => {
          return item
        })
        .join('|')
      let clientReg = new RegExp(inputItem, 'g')
      let currentList = []
      state.clientList.forEach(element => {
        let unifiedCustName = element.unifiedCustName.replace(clientReg, function ($) {
          return `<span style='color : #FA6F5B'>${$}</span>`
        })}
    }

全部代码

组件

<template lang="pug">
  div._search(
    :class="isShowCancel ? '_search-shadow' : ''"
  )
    div._search-input
      i.iconfont.icon-search
      input(
        ref="input"
        type="text"
        placeholder="搜索"
        v-model="text"
        @focus="$emit('focus')"
        @keyup.enter="$emit('onSearch', $event)"
        @keydown="$emit('keydown')"
        @blur="$emit('blur')"
      )
      i.iconfont.icon-close(
        v-show="text"
        @click="onClear"
      )
    span(v-show="isShowCancel" @click="searchCancel") 取消
</template>

<script>
export default {
  name: 'SearchComponents',

  props: {
    // 查询默认值
    placeholder: {
      type: String,
      default: '搜索'
    },
    // 是否显示取消
    isShowCancel: {
      type: Boolean,
      default: false
    },
    // 自动锁定焦点
    autoFocus: {
      type: Boolean,
      default: false
    },
    // 获取焦点
    focus: {
      type: Function,
      default: () => {}
    },
    // 手指抬起
    keyup: {
      type: Function,
      default: () => {}
    },
    // 手指按下
    keydown: {
      type: Function,
      default: () => {}
    },
    // 失去焦点
    blur: {
      type: Function,
      default: () => {}
    },
    // 关闭事件
    onCancel: {
      type: Function,
      default: () => {}
    },
    // 查询事件
    onSearch: {
      type: Function,
      default: () => {}
    },
    // enter 输入事件
    searchOnEnter: {
      type: Boolean,
      default: true
    }
  },

  data () {
    return {
      text: this.value
    }
  },

  mounted () {
    if (this.autoFocus) this.$refs.input.focus()
  },

  methods: {
    searchCancel () {
      this.$emit('input', '')
      this.$emit('onCancel')
    },
    onClear () {
      this.text = ''
    }
  },

  watch: {
    text (newValue) {
      if (!this.searchOnEnter) {
        this.$refs.input.blur()
        this.$emit('onSearch', newValue)
      }
      this.$emit('input', newValue)
    },
    value (newValue) {
      this.text = newValue
    }
  }
}
</script>

<style lang="stylus" scoped>
._search
  position relative
  height 5rem
  flex-shrink 0
  display flex
  align-items center
  justify-content space-between
  padding 1rem 1.5rem
  box-sizing border-box
._search-shadow
  box-shadow 0 .2rem 1rem 0 rgba(0, 0, 0, .05)
  span
    font-size 1.5rem
    color #333333
    margin-left 1.5rem

._search-input
  height 100%
  width 100%
  background-color #fafafa
  border-radius 5rem
  display flex
  flex 1
  align-items center
  i
    font-size 1.3rem
    color #333333
    margin-left .5rem
    margin-right .6rem
  .icon-close
    font-size 1.8rem
    color #dddddd
  input
    outline none
    border none
    background-color transparent
    font-size 1.4rem
    width 100%
    height 100%
  input::-webkit-input-placeholder
    color #cccccc
</style>


使用组件

<template lang="pug">
//- 分页在mutation,页数1清空,其他就push.
.use-search
  common-search(
    v-model="searchStr"
    :is-show-cancel="showMode"
    @focus="toSearch"
    @onCancel="showMode=false"
    @onSearch="onSearch"
  )
  //- 内容
  .content
		span(v-html="item.materName)
</template>
<script>
import CommonSearch from '~/components/SearchComponents'
export default {
  components: {
    CommonSearch
  },
  data () {
    return {
      // 搜索内容
      searchStr: '',
      // search_switch
      showMode: true
    }
  },
  methods: {
    // search_点击input框触发
    toSearch () {
      // 如果搜索框内有值
      if (this.xxx) {
        setTimeout(() => {
          // 一秒内只执行一次
          if (this.lastDelay - event.timeStamp === 0) {
            // TODO
          }
        }, 1000)
      } else {
        // 移除输入框和搜索列表的内容
        this.removeInputValue()
      }
    },
    // search_键盘enter时候触发
    onSearch () {
    },
    // 取消搜索
    onCancel () {
    }
  }
}
</script>