分离搜索框业务逻辑,优化搜索框搜索体验
							parent
							
								
									45fea5726b
								
							
						
					
					
						commit
						59627e048f
					
				| 
						 | 
				
			
			@ -3,6 +3,10 @@
 | 
			
		|||
- 新增网易云源歌曲搜索
 | 
			
		||||
- 新增网易云源歌单
 | 
			
		||||
 | 
			
		||||
#### 优化
 | 
			
		||||
 | 
			
		||||
- 优化搜索框搜索体验
 | 
			
		||||
 | 
			
		||||
#### 修复
 | 
			
		||||
 | 
			
		||||
- 修复QQ源歌单无法翻页Bug
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,7 +2,9 @@
 | 
			
		|||
  div(:class="$style.toolbar")
 | 
			
		||||
    //- img(v-if="icon")
 | 
			
		||||
    //- h1 {{title}}
 | 
			
		||||
    material-search-input(:class="$style.input")
 | 
			
		||||
    material-search-input(:class="$style.input"
 | 
			
		||||
      @event="handleEvent" :list="tipList" :visibleList="visibleList"
 | 
			
		||||
      v-model="searchText")
 | 
			
		||||
    div(:class="$style.control")
 | 
			
		||||
      button(type="button" :class="$style.min" title="最小化" @click="min")
 | 
			
		||||
      //- button(type="button" :class="$style.max" @click="max")
 | 
			
		||||
| 
						 | 
				
			
			@ -11,22 +13,93 @@
 | 
			
		|||
 | 
			
		||||
<script>
 | 
			
		||||
import { rendererSend } from 'common/icp'
 | 
			
		||||
// import { mapGetters } from 'vuex'
 | 
			
		||||
import { mapGetters } from 'vuex'
 | 
			
		||||
import music from '../../utils/music'
 | 
			
		||||
import { debounce } from '../../utils'
 | 
			
		||||
export default {
 | 
			
		||||
  // props: {
 | 
			
		||||
  //   color: {
 | 
			
		||||
  //     type: String,
 | 
			
		||||
  //     default: color,
 | 
			
		||||
  //   },
 | 
			
		||||
  //   icon: {
 | 
			
		||||
  //     type: Boolean,
 | 
			
		||||
  //     default: false,
 | 
			
		||||
  //   },
 | 
			
		||||
  // },
 | 
			
		||||
  data() {
 | 
			
		||||
    return {
 | 
			
		||||
      searchText: '',
 | 
			
		||||
      visibleList: false,
 | 
			
		||||
      tipList: [],
 | 
			
		||||
      tipSearch: null,
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  computed: {
 | 
			
		||||
    // ...mapGetters(['theme']),
 | 
			
		||||
    ...mapGetters(['route', 'setting']),
 | 
			
		||||
    ...mapGetters('search', {
 | 
			
		||||
      storeSearchText: 'searchText',
 | 
			
		||||
    }),
 | 
			
		||||
    source() {
 | 
			
		||||
      return this.setting.search.tempSearchSource
 | 
			
		||||
    },
 | 
			
		||||
    isAutoClearInput() {
 | 
			
		||||
      return this.setting.odc.isAutoClearSearchInput
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  watch: {
 | 
			
		||||
    route(n) {
 | 
			
		||||
      if (this.isAutoClearInput && n.name != 'search' && this.searchText) this.searchText = ''
 | 
			
		||||
    },
 | 
			
		||||
    'storeSearchText'(n) {
 | 
			
		||||
      if (n !== this.searchText) this.searchText = n
 | 
			
		||||
    },
 | 
			
		||||
    searchText(n) {
 | 
			
		||||
      this.handleTipSearch()
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  mounted() {
 | 
			
		||||
    this.tipSearch = debounce(() => {
 | 
			
		||||
      if (this.searchText === '') {
 | 
			
		||||
        this.tipList.splice(0, this.tipList.length)
 | 
			
		||||
        music[this.source].tempSearch.cancelTempSearch()
 | 
			
		||||
        return
 | 
			
		||||
      }
 | 
			
		||||
      music[this.source].tempSearch.search(this.searchText).then(list => {
 | 
			
		||||
        this.tipList = list
 | 
			
		||||
      }).catch(() => {})
 | 
			
		||||
    }, 50)
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    handleEvent({ action, data }) {
 | 
			
		||||
      switch (action) {
 | 
			
		||||
        case 'focus':
 | 
			
		||||
          if (!this.visibleList) this.visibleList = true
 | 
			
		||||
          if (this.searchText) this.handleTipSearch()
 | 
			
		||||
          break
 | 
			
		||||
        case 'blur':
 | 
			
		||||
          setTimeout(() => {
 | 
			
		||||
            if (this.visibleList) this.visibleList = false
 | 
			
		||||
          }, 50)
 | 
			
		||||
          break
 | 
			
		||||
        case 'submit':
 | 
			
		||||
          this.handleSearch()
 | 
			
		||||
          break
 | 
			
		||||
        case 'listClick':
 | 
			
		||||
          this.searchText = this.tipList[data]
 | 
			
		||||
          this.$nextTick(() => {
 | 
			
		||||
            this.handleSearch()
 | 
			
		||||
          })
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    handleTipSearch() {
 | 
			
		||||
      if (!this.visibleList) this.visibleList = true
 | 
			
		||||
      this.tipSearch()
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    handleSearch() {
 | 
			
		||||
      if (this.visibleList) this.visibleList = false
 | 
			
		||||
      setTimeout(() => {
 | 
			
		||||
        this.$router.push({
 | 
			
		||||
          path: 'search',
 | 
			
		||||
          query: {
 | 
			
		||||
            text: this.searchText,
 | 
			
		||||
          },
 | 
			
		||||
        }).catch(_ => _)
 | 
			
		||||
      }, 200)
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    min() {
 | 
			
		||||
      rendererSend('min')
 | 
			
		||||
    },
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,31 +1,46 @@
 | 
			
		|||
<template lang="pug">
 | 
			
		||||
div(:class="[$style.search, focus ? $style.active : '']")
 | 
			
		||||
  div(:class="$style.form")
 | 
			
		||||
    input(placeholder="Search for something..." v-model.trim="text"
 | 
			
		||||
          @focus="handleFocus" @blur="handleBlur" @input="handleInput"
 | 
			
		||||
    input(:placeholder="placeholder" v-model.trim="text"
 | 
			
		||||
          @focus="handleFocus" @blur="handleBlur" @input="$emit('input', text)"
 | 
			
		||||
          @change="sendEvent('change')"
 | 
			
		||||
          @keyup.enter="handleSearch")
 | 
			
		||||
    button(type="button" @click="handleSearch")
 | 
			
		||||
      slot
 | 
			
		||||
        svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 30.239 30.239' space='preserve')
 | 
			
		||||
          use(xlink:href='#icon-search')
 | 
			
		||||
  //- transition(name="custom-classes-transition"
 | 
			
		||||
  //-             enter-active-class="animated flipInX"
 | 
			
		||||
  //-             leave-active-class="animated flipOutX")
 | 
			
		||||
  div(:class="$style.list" :style="listStyle")
 | 
			
		||||
  div(v-if="list" :class="$style.list" :style="listStyle")
 | 
			
		||||
    ul(ref="dom_list")
 | 
			
		||||
      li(v-for="(item, index) in list" :key="item" @click="handleTemplistClick(index)")
 | 
			
		||||
        span {{item}}
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import { mapGetters } from 'vuex'
 | 
			
		||||
import music from '../../utils/music'
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  props: {
 | 
			
		||||
    placeholder: {
 | 
			
		||||
      type: String,
 | 
			
		||||
      default: 'Search for something...',
 | 
			
		||||
    },
 | 
			
		||||
    list: {
 | 
			
		||||
      type: Array,
 | 
			
		||||
    },
 | 
			
		||||
    visibleList: {
 | 
			
		||||
      type: Boolean,
 | 
			
		||||
      default: false,
 | 
			
		||||
    },
 | 
			
		||||
    value: {
 | 
			
		||||
      type: String,
 | 
			
		||||
      default: '',
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  data() {
 | 
			
		||||
    return {
 | 
			
		||||
      isShow: false,
 | 
			
		||||
      text: '',
 | 
			
		||||
      list: [],
 | 
			
		||||
      index: null,
 | 
			
		||||
      focus: false,
 | 
			
		||||
      listStyle: {
 | 
			
		||||
| 
						 | 
				
			
			@ -33,64 +48,35 @@ export default {
 | 
			
		|||
      },
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  computed: {
 | 
			
		||||
    ...mapGetters(['route', 'setting']),
 | 
			
		||||
    ...mapGetters('search', ['searchText']),
 | 
			
		||||
    isAutoClearInput() {
 | 
			
		||||
      return this.setting.odc.isAutoClearSearchInput
 | 
			
		||||
    },
 | 
			
		||||
    source() {
 | 
			
		||||
      return this.setting.search.tempSearchSource
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  watch: {
 | 
			
		||||
    list(n) {
 | 
			
		||||
      if (!this.visibleList) return
 | 
			
		||||
      this.$nextTick(() => {
 | 
			
		||||
        this.listStyle.height = this.$refs.dom_list.scrollHeight + 'px'
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
    'searchText'(n) {
 | 
			
		||||
      if (n !== this.text) this.text = n
 | 
			
		||||
    value(n) {
 | 
			
		||||
      this.text = n
 | 
			
		||||
    },
 | 
			
		||||
    route(n) {
 | 
			
		||||
      if (this.isAutoClearInput && n.name != 'search' && this.text) this.text = ''
 | 
			
		||||
    visibleList(n) {
 | 
			
		||||
      n ? this.showList() : this.hideList()
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    handleTemplistClick(index) {
 | 
			
		||||
      this.text = this.list[index]
 | 
			
		||||
      this.handleSearch()
 | 
			
		||||
      this.sendEvent('listClick', index)
 | 
			
		||||
    },
 | 
			
		||||
    handleFocus() {
 | 
			
		||||
      this.focus = true
 | 
			
		||||
      if (this.text) this.handleInput()
 | 
			
		||||
      this.showList()
 | 
			
		||||
      this.sendEvent('focus')
 | 
			
		||||
    },
 | 
			
		||||
    handleBlur() {
 | 
			
		||||
      setTimeout(() => {
 | 
			
		||||
      this.focus = false
 | 
			
		||||
        this.hideList()
 | 
			
		||||
      }, 200)
 | 
			
		||||
      this.sendEvent('blur')
 | 
			
		||||
    },
 | 
			
		||||
    handleSearch() {
 | 
			
		||||
      this.hideList()
 | 
			
		||||
      this.$router.push({
 | 
			
		||||
        path: 'search',
 | 
			
		||||
        query: {
 | 
			
		||||
          text: this.text,
 | 
			
		||||
        },
 | 
			
		||||
      }).catch(_ => _)
 | 
			
		||||
    },
 | 
			
		||||
    handleInput() {
 | 
			
		||||
      if (this.text === '') {
 | 
			
		||||
        this.list.splice(0, this.list.length)
 | 
			
		||||
        music[this.source].tempSearch.cancelTempSearch()
 | 
			
		||||
        return
 | 
			
		||||
      }
 | 
			
		||||
      if (!this.isShow) this.showList()
 | 
			
		||||
      music[this.source].tempSearch.search(this.text).then(list => {
 | 
			
		||||
        this.list = list
 | 
			
		||||
      }).catch(() => {})
 | 
			
		||||
      this.sendEvent('submit')
 | 
			
		||||
    },
 | 
			
		||||
    showList() {
 | 
			
		||||
      this.isShow = true
 | 
			
		||||
| 
						 | 
				
			
			@ -100,6 +86,12 @@ export default {
 | 
			
		|||
      this.isShow = false
 | 
			
		||||
      this.listStyle.height = 0
 | 
			
		||||
    },
 | 
			
		||||
    sendEvent(action, data) {
 | 
			
		||||
      this.$emit('event', {
 | 
			
		||||
        action,
 | 
			
		||||
        data,
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -305,6 +305,24 @@ export const throttle = (fn, delay = 100) => {
 | 
			
		|||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 生成防抖函数
 | 
			
		||||
 * @param {*} fn
 | 
			
		||||
 * @param {*} delay
 | 
			
		||||
 */
 | 
			
		||||
export const debounce = (fn, delay = 100) => {
 | 
			
		||||
  let timer = null
 | 
			
		||||
  let _args = null
 | 
			
		||||
  return function(...args) {
 | 
			
		||||
    _args = args
 | 
			
		||||
    if (timer) clearTimeout(timer)
 | 
			
		||||
    timer = setTimeout(() => {
 | 
			
		||||
      timer = null
 | 
			
		||||
      fn.apply(this, _args)
 | 
			
		||||
    }, delay)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const async_removeItem = (arr, num, callback) => window.requestAnimationFrame(() => {
 | 
			
		||||
  let len = arr.length
 | 
			
		||||
  if (len > num) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue