分离搜索框业务逻辑,优化搜索框搜索体验
parent
45fea5726b
commit
59627e048f
|
@ -3,6 +3,10 @@
|
||||||
- 新增网易云源歌曲搜索
|
- 新增网易云源歌曲搜索
|
||||||
- 新增网易云源歌单
|
- 新增网易云源歌单
|
||||||
|
|
||||||
|
#### 优化
|
||||||
|
|
||||||
|
- 优化搜索框搜索体验
|
||||||
|
|
||||||
#### 修复
|
#### 修复
|
||||||
|
|
||||||
- 修复QQ源歌单无法翻页Bug
|
- 修复QQ源歌单无法翻页Bug
|
||||||
|
|
|
@ -2,7 +2,9 @@
|
||||||
div(:class="$style.toolbar")
|
div(:class="$style.toolbar")
|
||||||
//- img(v-if="icon")
|
//- img(v-if="icon")
|
||||||
//- h1 {{title}}
|
//- 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")
|
div(:class="$style.control")
|
||||||
button(type="button" :class="$style.min" title="最小化" @click="min")
|
button(type="button" :class="$style.min" title="最小化" @click="min")
|
||||||
//- button(type="button" :class="$style.max" @click="max")
|
//- button(type="button" :class="$style.max" @click="max")
|
||||||
|
@ -11,22 +13,93 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { rendererSend } from 'common/icp'
|
import { rendererSend } from 'common/icp'
|
||||||
// import { mapGetters } from 'vuex'
|
import { mapGetters } from 'vuex'
|
||||||
|
import music from '../../utils/music'
|
||||||
|
import { debounce } from '../../utils'
|
||||||
export default {
|
export default {
|
||||||
// props: {
|
data() {
|
||||||
// color: {
|
return {
|
||||||
// type: String,
|
searchText: '',
|
||||||
// default: color,
|
visibleList: false,
|
||||||
// },
|
tipList: [],
|
||||||
// icon: {
|
tipSearch: null,
|
||||||
// type: Boolean,
|
}
|
||||||
// default: false,
|
},
|
||||||
// },
|
|
||||||
// },
|
|
||||||
computed: {
|
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: {
|
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() {
|
min() {
|
||||||
rendererSend('min')
|
rendererSend('min')
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,31 +1,46 @@
|
||||||
<template lang="pug">
|
<template lang="pug">
|
||||||
div(:class="[$style.search, focus ? $style.active : '']")
|
div(:class="[$style.search, focus ? $style.active : '']")
|
||||||
div(:class="$style.form")
|
div(:class="$style.form")
|
||||||
input(placeholder="Search for something..." v-model.trim="text"
|
input(:placeholder="placeholder" v-model.trim="text"
|
||||||
@focus="handleFocus" @blur="handleBlur" @input="handleInput"
|
@focus="handleFocus" @blur="handleBlur" @input="$emit('input', text)"
|
||||||
|
@change="sendEvent('change')"
|
||||||
@keyup.enter="handleSearch")
|
@keyup.enter="handleSearch")
|
||||||
button(type="button" @click="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')
|
slot
|
||||||
use(xlink:href='#icon-search')
|
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"
|
//- transition(name="custom-classes-transition"
|
||||||
//- enter-active-class="animated flipInX"
|
//- enter-active-class="animated flipInX"
|
||||||
//- leave-active-class="animated flipOutX")
|
//- leave-active-class="animated flipOutX")
|
||||||
div(:class="$style.list" :style="listStyle")
|
div(v-if="list" :class="$style.list" :style="listStyle")
|
||||||
ul(ref="dom_list")
|
ul(ref="dom_list")
|
||||||
li(v-for="(item, index) in list" :key="item" @click="handleTemplistClick(index)")
|
li(v-for="(item, index) in list" :key="item" @click="handleTemplistClick(index)")
|
||||||
span {{item}}
|
span {{item}}
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters } from 'vuex'
|
|
||||||
import music from '../../utils/music'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
props: {
|
||||||
|
placeholder: {
|
||||||
|
type: String,
|
||||||
|
default: 'Search for something...',
|
||||||
|
},
|
||||||
|
list: {
|
||||||
|
type: Array,
|
||||||
|
},
|
||||||
|
visibleList: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
isShow: false,
|
isShow: false,
|
||||||
text: '',
|
text: '',
|
||||||
list: [],
|
|
||||||
index: null,
|
index: null,
|
||||||
focus: false,
|
focus: false,
|
||||||
listStyle: {
|
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: {
|
watch: {
|
||||||
list(n) {
|
list(n) {
|
||||||
|
if (!this.visibleList) return
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.listStyle.height = this.$refs.dom_list.scrollHeight + 'px'
|
this.listStyle.height = this.$refs.dom_list.scrollHeight + 'px'
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
'searchText'(n) {
|
value(n) {
|
||||||
if (n !== this.text) this.text = n
|
this.text = n
|
||||||
},
|
},
|
||||||
route(n) {
|
visibleList(n) {
|
||||||
if (this.isAutoClearInput && n.name != 'search' && this.text) this.text = ''
|
n ? this.showList() : this.hideList()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
handleTemplistClick(index) {
|
handleTemplistClick(index) {
|
||||||
this.text = this.list[index]
|
this.sendEvent('listClick', index)
|
||||||
this.handleSearch()
|
|
||||||
},
|
},
|
||||||
handleFocus() {
|
handleFocus() {
|
||||||
this.focus = true
|
this.focus = true
|
||||||
if (this.text) this.handleInput()
|
this.sendEvent('focus')
|
||||||
this.showList()
|
|
||||||
},
|
},
|
||||||
handleBlur() {
|
handleBlur() {
|
||||||
setTimeout(() => {
|
this.focus = false
|
||||||
this.focus = false
|
this.sendEvent('blur')
|
||||||
this.hideList()
|
|
||||||
}, 200)
|
|
||||||
},
|
},
|
||||||
handleSearch() {
|
handleSearch() {
|
||||||
this.hideList()
|
this.hideList()
|
||||||
this.$router.push({
|
this.sendEvent('submit')
|
||||||
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(() => {})
|
|
||||||
},
|
},
|
||||||
showList() {
|
showList() {
|
||||||
this.isShow = true
|
this.isShow = true
|
||||||
|
@ -100,6 +86,12 @@ export default {
|
||||||
this.isShow = false
|
this.isShow = false
|
||||||
this.listStyle.height = 0
|
this.listStyle.height = 0
|
||||||
},
|
},
|
||||||
|
sendEvent(action, data) {
|
||||||
|
this.$emit('event', {
|
||||||
|
action,
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</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(() => {
|
const async_removeItem = (arr, num, callback) => window.requestAnimationFrame(() => {
|
||||||
let len = arr.length
|
let len = arr.length
|
||||||
if (len > num) {
|
if (len > num) {
|
||||||
|
|
Loading…
Reference in New Issue