新增我的列表内歌曲搜索
parent
ccd5c91283
commit
fbd2a57a35
|
@ -0,0 +1,362 @@
|
||||||
|
<template lang="pug">
|
||||||
|
div(:class="$style.container" ref="dom_container" v-show="isShow")
|
||||||
|
transition(enter-active-class="animated-fast zoomIn" leave-active-class="animated zoomOut" @after-leave="handleAnimated")
|
||||||
|
div(:class="$style.search" v-show="visible")
|
||||||
|
div(:class="$style.form")
|
||||||
|
input.key-bind.ignore-esc(:placeholder="placeholder" v-model.trim="text" ref="dom_input"
|
||||||
|
@input="handleDelaySearch"
|
||||||
|
@keyup.enter="handleTemplistClick(selectIndex)"
|
||||||
|
@keyup.40.prevent="handleKeyDown"
|
||||||
|
@keyup.38.prevent="handleKeyUp"
|
||||||
|
@keyup.27.prevent="handleKeyEsc"
|
||||||
|
@contextmenu="handleContextMenu")
|
||||||
|
button(type="button" @click="handleHide")
|
||||||
|
slot
|
||||||
|
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 212.982 212.982' space='preserve')
|
||||||
|
use(xlink:href='#icon-delete')
|
||||||
|
div.scroll(v-if="resultList" :class="$style.list" :style="listStyle" ref="dom_scrollContainer")
|
||||||
|
ul(ref="dom_list")
|
||||||
|
li(v-for="(item, index) in resultList" :key="item.songmid" :class="selectIndex === index ? $style.select : null" @mouseenter="selectIndex = index" @click="handleTemplistClick(index)")
|
||||||
|
div(:class="$style.img")
|
||||||
|
div(:class="$style.text")
|
||||||
|
h3(:class="$style.text") {{item.name}} - {{item.singer}}
|
||||||
|
h3(v-if="item.albumName" :class="[$style.text, $style.albumName]") {{item.albumName}}
|
||||||
|
div(:class="$style.source") {{item.source}}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { clipboardReadText, debounce, scrollTo } from '../../utils'
|
||||||
|
let canceleFn
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
placeholder: {
|
||||||
|
type: String,
|
||||||
|
default: 'Find for something...',
|
||||||
|
},
|
||||||
|
list: {
|
||||||
|
type: Array,
|
||||||
|
default() {
|
||||||
|
return []
|
||||||
|
},
|
||||||
|
},
|
||||||
|
visible: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
text: '',
|
||||||
|
selectIndex: -1,
|
||||||
|
listStyle: {
|
||||||
|
height: 0,
|
||||||
|
maxHeight: 0,
|
||||||
|
},
|
||||||
|
maxHeight: 0,
|
||||||
|
resultList: [],
|
||||||
|
isModDown: false,
|
||||||
|
isShow: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
resultList(n) {
|
||||||
|
if (this.selectIndex > -1) this.selectIndex = -1
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.listStyle.height = Math.min(this.$refs.dom_list.scrollHeight, this.maxHeight) + 'px'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
list(n) {
|
||||||
|
if (!this.visible) return
|
||||||
|
this.handleDelaySearch()
|
||||||
|
},
|
||||||
|
visible(n) {
|
||||||
|
if (!n) return
|
||||||
|
this.isShow = true
|
||||||
|
this.init()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.handleDelaySearch = debounce(() => {
|
||||||
|
this.handleSearch()
|
||||||
|
})
|
||||||
|
if (this.visible) this.isShow = true
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.init()
|
||||||
|
window.eventHub.$on('key_mod_down', this.handle_key_mod_down)
|
||||||
|
window.eventHub.$on('key_mod_up', this.handle_key_mod_up)
|
||||||
|
window.eventHub.$on('key_mod+f_down', this.handle_key_mod_f_down)
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
window.eventHub.$off('key_mod_down', this.handle_key_mod_down)
|
||||||
|
window.eventHub.$off('key_mod_up', this.handle_key_mod_up)
|
||||||
|
window.eventHub.$off('key_mod+f_down', this.handle_key_mod_f_down)
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
init() {
|
||||||
|
if (!this.visible) return
|
||||||
|
this.handleSearch()
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (!this.listStyle.maxHeight) {
|
||||||
|
this.maxHeight = this.$refs.dom_container.offsetParent.clientHeight - this.$refs.dom_list.offsetTop - 70
|
||||||
|
this.listStyle.maxHeight = this.maxHeight + 'px'
|
||||||
|
}
|
||||||
|
this.$refs.dom_input.focus()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleKeyEsc() {
|
||||||
|
if (this.text.length > 0) {
|
||||||
|
this.text = ''
|
||||||
|
this.resultList = []
|
||||||
|
} else {
|
||||||
|
this.handleHide()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handle_key_mod_down() {
|
||||||
|
this.isModDown = true
|
||||||
|
},
|
||||||
|
handle_key_mod_up() {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.isModDown = false
|
||||||
|
}, 100)
|
||||||
|
},
|
||||||
|
handle_key_mod_f_down() {
|
||||||
|
if (this.visible) this.$refs.dom_input.focus()
|
||||||
|
},
|
||||||
|
handleAnimated() {
|
||||||
|
if (this.visible) return
|
||||||
|
this.isShow = false
|
||||||
|
},
|
||||||
|
handleTemplistClick(index) {
|
||||||
|
if (index < 0) return
|
||||||
|
this.sendEvent('listClick', {
|
||||||
|
index: this.list.indexOf(this.resultList[index]),
|
||||||
|
isPlay: this.isModDown,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleHide() {
|
||||||
|
this.sendEvent('hide')
|
||||||
|
},
|
||||||
|
sendEvent(action, data) {
|
||||||
|
this.$emit('action', {
|
||||||
|
action,
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleKeyDown() {
|
||||||
|
if (!this.resultList.length) return
|
||||||
|
this.selectIndex = this.selectIndex + 1 < this.resultList.length ? this.selectIndex + 1 : 0
|
||||||
|
this.handleScrollList()
|
||||||
|
},
|
||||||
|
handleKeyUp() {
|
||||||
|
if (!this.resultList.length) return
|
||||||
|
this.selectIndex = this.selectIndex - 1 < -1 ? this.resultList.length - 1 : this.selectIndex - 1
|
||||||
|
this.handleScrollList()
|
||||||
|
},
|
||||||
|
handleScrollList() {
|
||||||
|
if (this.selectIndex < 0) return
|
||||||
|
let dom = this.$refs.dom_list.children[this.selectIndex]
|
||||||
|
let offsetTop = dom.offsetTop
|
||||||
|
let scrollTop = this.$refs.dom_scrollContainer.scrollTop
|
||||||
|
if (offsetTop < scrollTop) {
|
||||||
|
if (canceleFn) canceleFn()
|
||||||
|
canceleFn = scrollTo(this.$refs.dom_scrollContainer, offsetTop, 200, () => canceleFn = null)
|
||||||
|
} else if (offsetTop + dom.clientHeight > this.$refs.dom_scrollContainer.clientHeight + scrollTop) {
|
||||||
|
if (canceleFn) canceleFn()
|
||||||
|
canceleFn = scrollTo(this.$refs.dom_scrollContainer, offsetTop + dom.clientHeight - this.$refs.dom_scrollContainer.clientHeight, 200, () => canceleFn = null)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleContextMenu() {
|
||||||
|
let str = clipboardReadText()
|
||||||
|
str = str.replace(/\t|\r\n|\n|\r/g, ' ')
|
||||||
|
str = str.replace(/\s+/g, ' ')
|
||||||
|
let dom_input = this.$refs.dom_input
|
||||||
|
this.text = `${this.text.substring(0, dom_input.selectionStart)}${str}${this.text.substring(dom_input.selectionEnd, this.text.length)}`
|
||||||
|
},
|
||||||
|
handleSearch() {
|
||||||
|
if (!this.text.length) return this.resultList = []
|
||||||
|
let list = []
|
||||||
|
let rxp = new RegExp(this.text.split('').join('.*'), 'i')
|
||||||
|
for (const item of this.list) {
|
||||||
|
if (rxp.test(`${item.name}${item.singer}${item.albumName ? item.albumName : ''}`)) list.push(item)
|
||||||
|
}
|
||||||
|
this.resultList = list
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<style lang="less" module>
|
||||||
|
@import '../../assets/styles/layout.less';
|
||||||
|
|
||||||
|
.container {
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
top: 20px;
|
||||||
|
width: 45%;
|
||||||
|
height: @height-toolbar * 0.52;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: box-shadow .4s ease, background-color @transition-theme;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column nowrap;
|
||||||
|
background-color: @color-search-form-background;
|
||||||
|
box-shadow: 0 1px 2px rgba(0,0,0,0.07),
|
||||||
|
0 2px 4px rgba(0,0,0,0.07),
|
||||||
|
0 4px 8px rgba(0,0,0,0.07),
|
||||||
|
0 8px 16px rgba(0,0,0,0.07),
|
||||||
|
0 16px 32px rgba(0,0,0,0.07),
|
||||||
|
0 32px 64px rgba(0,0,0,0.07);
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
.form {
|
||||||
|
input {
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.form {
|
||||||
|
display: flex;
|
||||||
|
height: @height-toolbar * 0.52;
|
||||||
|
position: relative;
|
||||||
|
input {
|
||||||
|
flex: auto;
|
||||||
|
// border: 1px solid;
|
||||||
|
border-top-left-radius: 4px;
|
||||||
|
border-bottom-left-radius: 4px;
|
||||||
|
background-color: transparent;
|
||||||
|
// border-bottom: 2px solid @color-theme;
|
||||||
|
// border-color: @color-theme;
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
outline: none;
|
||||||
|
// height: @height-toolbar * .7;
|
||||||
|
padding: 0 5px;
|
||||||
|
overflow: hidden;
|
||||||
|
font-size: 13.5px;
|
||||||
|
line-height: @height-toolbar * 0.52 + 5px;
|
||||||
|
&::placeholder {
|
||||||
|
color: @color-btn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
flex: none;
|
||||||
|
border: none;
|
||||||
|
// background-color: @color-search-form-background;
|
||||||
|
background-color: transparent;
|
||||||
|
outline: none;
|
||||||
|
border-top-right-radius: 4px;
|
||||||
|
border-bottom-right-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
height: 100%;
|
||||||
|
padding: 6px 7px;
|
||||||
|
color: @color-btn;
|
||||||
|
transition: background-color .2s ease;
|
||||||
|
opacity: 0.8;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: @color-theme-hover;
|
||||||
|
}
|
||||||
|
&:active {
|
||||||
|
background-color: @color-theme-active;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.list {
|
||||||
|
// background-color: @color-search-form-background;
|
||||||
|
font-size: 13px;
|
||||||
|
transition: .3s ease;
|
||||||
|
height: 0;
|
||||||
|
transition-property: height;
|
||||||
|
position: relative;
|
||||||
|
li {
|
||||||
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 8px 5px;
|
||||||
|
transition: background-color .2s ease;
|
||||||
|
line-height: 1.3;
|
||||||
|
// overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row nowrap;
|
||||||
|
|
||||||
|
&.select {
|
||||||
|
background-color: @color-search-list-hover;
|
||||||
|
}
|
||||||
|
border-radius: 4px;
|
||||||
|
// &:last-child {
|
||||||
|
// border-bottom-left-radius: 4px;
|
||||||
|
// border-bottom-right-radius: 4px;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.img {
|
||||||
|
flex: none;
|
||||||
|
}
|
||||||
|
.text {
|
||||||
|
flex: auto;
|
||||||
|
.mixin-ellipsis-1;
|
||||||
|
}
|
||||||
|
.albumName {
|
||||||
|
font-size: 12px;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
.source {
|
||||||
|
flex: none;
|
||||||
|
font-size: 12px;
|
||||||
|
opacity: 0.5;
|
||||||
|
padding: 0 5px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
// transform: rotate(45deg);
|
||||||
|
// background-color:
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
each(@themes, {
|
||||||
|
:global(#container.@{value}) {
|
||||||
|
|
||||||
|
.search {
|
||||||
|
background-color: ~'@{color-@{value}-search-form-background}';
|
||||||
|
|
||||||
|
.form {
|
||||||
|
input {
|
||||||
|
&::placeholder {
|
||||||
|
color: ~'@{color-@{value}-btn}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
color: ~'@{color-@{value}-btn}';
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: ~'@{color-@{value}-theme-hover}';
|
||||||
|
}
|
||||||
|
&:active {
|
||||||
|
background-color: ~'@{color-@{value}-theme-active}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.list {
|
||||||
|
li {
|
||||||
|
&.select {
|
||||||
|
background-color: ~'@{color-@{value}-search-list-hover}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
</style>
|
|
@ -65,6 +65,7 @@
|
||||||
material-list-add-multiple-modal(:show="isShowListAddMultiple" :is-move="isMoveMultiple" :from-list-id="listData.id" :musicList="selectdListDetailData" :exclude-list-id="excludeListId" @close="handleListAddMultipleModalClose")
|
material-list-add-multiple-modal(:show="isShowListAddMultiple" :is-move="isMoveMultiple" :from-list-id="listData.id" :musicList="selectdListDetailData" :exclude-list-id="excludeListId" @close="handleListAddMultipleModalClose")
|
||||||
material-menu(:menus="listsItemMenu" :location="listsData.menuLocation" item-name="name" :isShow="listsData.isShowItemMenu" @menu-click="handleListsItemMenuClick")
|
material-menu(:menus="listsItemMenu" :location="listsData.menuLocation" item-name="name" :isShow="listsData.isShowItemMenu" @menu-click="handleListsItemMenuClick")
|
||||||
material-menu(:menus="listItemMenu" :location="listMenu.menuLocation" item-name="name" :isShow="listMenu.isShowItemMenu" @menu-click="handleListItemMenuClick")
|
material-menu(:menus="listItemMenu" :location="listMenu.menuLocation" item-name="name" :isShow="listMenu.isShowItemMenu" @menu-click="handleListItemMenuClick")
|
||||||
|
material-search-list(:list="list" @action="handleMusicSearchAction" :visible="isVisibleMusicSearch")
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -130,6 +131,7 @@ export default {
|
||||||
},
|
},
|
||||||
isMove: false,
|
isMove: false,
|
||||||
isMoveMultiple: false,
|
isMoveMultiple: false,
|
||||||
|
isVisibleMusicSearch: false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -251,7 +253,7 @@ export default {
|
||||||
},
|
},
|
||||||
'$route.query.scrollIndex'(n) {
|
'$route.query.scrollIndex'(n) {
|
||||||
if (n == null || this.isToggleList) return
|
if (n == null || this.isToggleList) return
|
||||||
this.restoreScroll(true)
|
this.restoreScroll(this.$route.query.scrollIndex, true)
|
||||||
this.isToggleList = true
|
this.isToggleList = true
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -322,6 +324,7 @@ export default {
|
||||||
window.eventHub.$on('key_mod_down', this.handle_key_mod_down)
|
window.eventHub.$on('key_mod_down', this.handle_key_mod_down)
|
||||||
window.eventHub.$on('key_mod_up', this.handle_key_mod_up)
|
window.eventHub.$on('key_mod_up', this.handle_key_mod_up)
|
||||||
window.eventHub.$on('key_mod+a_down', this.handle_key_mod_a_down)
|
window.eventHub.$on('key_mod+a_down', this.handle_key_mod_a_down)
|
||||||
|
window.eventHub.$on('key_mod+f_down', this.handle_key_mod_f_down)
|
||||||
},
|
},
|
||||||
unlistenEvent() {
|
unlistenEvent() {
|
||||||
window.eventHub.$off('key_shift_down', this.handle_key_shift_down)
|
window.eventHub.$off('key_shift_down', this.handle_key_shift_down)
|
||||||
|
@ -329,6 +332,10 @@ export default {
|
||||||
window.eventHub.$off('key_mod_down', this.handle_key_mod_down)
|
window.eventHub.$off('key_mod_down', this.handle_key_mod_down)
|
||||||
window.eventHub.$off('key_mod_up', this.handle_key_mod_up)
|
window.eventHub.$off('key_mod_up', this.handle_key_mod_up)
|
||||||
window.eventHub.$off('key_mod+a_down', this.handle_key_mod_a_down)
|
window.eventHub.$off('key_mod+a_down', this.handle_key_mod_a_down)
|
||||||
|
window.eventHub.$off('key_mod+f_down', this.handle_key_mod_f_down)
|
||||||
|
},
|
||||||
|
handle_key_mod_f_down() {
|
||||||
|
this.isVisibleMusicSearch = true
|
||||||
},
|
},
|
||||||
handle_key_shift_down() {
|
handle_key_shift_down() {
|
||||||
if (!this.keyEvent.isShiftDown) this.keyEvent.isShiftDown = true
|
if (!this.keyEvent.isShiftDown) this.keyEvent.isShiftDown = true
|
||||||
|
@ -355,11 +362,11 @@ export default {
|
||||||
this.delayTimeout = setTimeout(() => {
|
this.delayTimeout = setTimeout(() => {
|
||||||
this.delayTimeout = null
|
this.delayTimeout = null
|
||||||
this.delayShow = true
|
this.delayShow = true
|
||||||
this.restoreScroll()
|
this.restoreScroll(this.$route.query.scrollIndex, false)
|
||||||
}, 200)
|
}, 200)
|
||||||
} else {
|
} else {
|
||||||
this.delayShow = true
|
this.delayShow = true
|
||||||
this.restoreScroll()
|
this.restoreScroll(this.$route.query.scrollIndex, false)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
clearDelayTimeout() {
|
clearDelayTimeout() {
|
||||||
|
@ -368,9 +375,19 @@ export default {
|
||||||
this.delayTimeout = null
|
this.delayTimeout = null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
restoreScroll(isAnimation) {
|
handleScrollList(index, isAnimation, callback = () => {}) {
|
||||||
|
let location = this.getMusicLocation(index) - 150
|
||||||
|
if (location < 0) location = 0
|
||||||
|
if (isAnimation) {
|
||||||
|
scrollTo(this.$refs.dom_scrollContent, location, 300, callback)
|
||||||
|
} else {
|
||||||
|
this.$refs.dom_scrollContent.scrollTo(0, location)
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
restoreScroll(index, isAnimation) {
|
||||||
if (!this.list.length) return
|
if (!this.list.length) return
|
||||||
if (this.$route.query.scrollIndex == null) {
|
if (index == null) {
|
||||||
let location = this.listData.location || 0
|
let location = this.listData.location || 0
|
||||||
if (this.setting.list.isSaveScrollLocation && location) {
|
if (this.setting.list.isSaveScrollLocation && location) {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
|
@ -381,9 +398,7 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
let location = this.getMusicLocation(this.$route.query.scrollIndex) - 150
|
this.handleScrollList(index, isAnimation)
|
||||||
if (location < 0) location = 0
|
|
||||||
isAnimation ? scrollTo(this.$refs.dom_scrollContent, location) : this.$refs.dom_scrollContent.scrollTo(0, location)
|
|
||||||
this.$router.replace({
|
this.$router.replace({
|
||||||
path: 'list',
|
path: 'list',
|
||||||
query: {
|
query: {
|
||||||
|
@ -784,6 +799,22 @@ export default {
|
||||||
openUrl(url)
|
openUrl(url)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
handleMusicSearchAction({ action, data: { index, isPlay } = {} }) {
|
||||||
|
this.isVisibleMusicSearch = false
|
||||||
|
switch (action) {
|
||||||
|
case 'listClick':
|
||||||
|
if (index < 0) return
|
||||||
|
this.handleScrollList(index, true, () => {
|
||||||
|
let dom = document.getElementById('mid_' + this.list[index].songmid)
|
||||||
|
dom.classList.add('selected')
|
||||||
|
setTimeout(() => {
|
||||||
|
dom.classList.remove('selected')
|
||||||
|
if (isPlay) this.testPlay(index)
|
||||||
|
}, 600)
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
Loading…
Reference in New Issue