分离搜索框业务逻辑,优化搜索框搜索体验
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")
|
||||
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')
|
||||
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.focus = false
|
||||
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