完成歌单功能

pull/96/head
lyswhut 2019-09-04 00:03:29 +08:00
parent 9727601752
commit a035a2525a
9 changed files with 248 additions and 79 deletions

View File

@ -283,7 +283,7 @@ export default {
}
},
checkDelayNextTimeout() {
console.log(this.delayNextTimeout)
// console.log(this.delayNextTimeout)
if (this.delayNextTimeout) {
clearTimeout(this.delayNextTimeout)
this.delayNextTimeout = null

View File

@ -1,40 +1,41 @@
<template lang="pug">
div(:class="$style.songList")
div(v-if="list.length" :class="$style.list")
div(:class="$style.thead")
table
thead
tr
th.nobreak.center(style="width: 37px;")
material-checkbox(id="search_select_all" v-model="isSelectAll" @change="handleSelectAllData"
:indeterminate="isIndeterminate" :title="isSelectAll && !isIndeterminate ? '全不选' : '全选'")
th.nobreak(style="width: 25%;") 歌曲名
th.nobreak(style="width: 20%;") 歌手
th.nobreak(style="width: 22%;") 专辑
th.nobreak(style="width: 18%;") 操作
th.nobreak(style="width: 10%;") 时长
div.scroll(:class="$style.tbody" ref="dom_scrollContent")
table
tbody
tr(v-for='(item, index) in list' :key='item.songmid' @click="handleDoubleClick(index)")
td.nobreak.center(style="width: 37px;" @click.stop)
material-checkbox(:id="index.toString()" v-model="selectdList" @change="handleChangeSelect" :value="item")
td.break(style="width: 25%;")
| {{item.name}}
span.badge.badge-info(v-if="item._types['320k']") 高品质
span.badge.badge-success(v-if="item._types.ape || item._types.flac") 无损
td.break(style="width: 20%;") {{item.singer}}
td.break(style="width: 22%;") {{item.albumName}}
td(style="width: 18%;")
material-list-buttons(:index="index" :search-btn="true" :play-btn="item.source == 'kw' || (!isAPITemp && item.source != 'tx')" :download-btn="item.source == 'kw' || (!isAPITemp && item.source != 'tx')" :remove-btn="false" @btn-click="handleListBtnClick")
//- button.btn-info(type='button' v-if="item._types['128k'] || item._types['192k'] || item._types['320k'] || item._types.flac" @click.stop='openDownloadModal(index)')
//- button.btn-secondary(type='button' v-if="item._types['128k'] || item._types['192k'] || item._types['320k']" @click.stop='testPlay(index)')
//- button.btn-success(type='button' v-if="(item._types['128k'] || item._types['192k'] || item._types['320k']) && userInfo" @click.stop='showListModal(index)')
td(style="width: 10%;") {{item.interval || '--/--'}}
div(:class="$style.pagination")
material-pagination(:count="total" :limit="limit" :page="page" @btn-click="handleTogglePage")
div(v-else :class="$style.noitem")
p(v-html="noItem")
transition(enter-active-class="animated fadeIn" leave-active-class="animated fadeOut")
div(v-if="list.length" :class="$style.list")
div(:class="$style.thead")
table
thead
tr
th.nobreak.center(style="width: 37px;")
material-checkbox(id="search_select_all" v-model="isSelectAll" @change="handleSelectAllData"
:indeterminate="isIndeterminate" :title="isSelectAll && !isIndeterminate ? '全不选' : '全选'")
th.nobreak(style="width: 25%;") 歌曲名
th.nobreak(style="width: 20%;") 歌手
th.nobreak(style="width: 22%;") 专辑
th.nobreak(style="width: 18%;") 操作
th.nobreak(style="width: 10%;") 时长
div.scroll(:class="$style.tbody" ref="dom_scrollContent")
table
tbody
tr(v-for='(item, index) in list' :key='item.songmid' @click="handleDoubleClick(index)")
td.nobreak.center(style="width: 37px;" @click.stop)
material-checkbox(:id="index.toString()" v-model="selectdList" @change="handleChangeSelect" :value="item")
td.break(style="width: 25%;")
| {{item.name}}
span.badge.badge-info(v-if="item._types['320k']") 高品质
span.badge.badge-success(v-if="item._types.ape || item._types.flac") 无损
td.break(style="width: 20%;") {{item.singer}}
td.break(style="width: 22%;") {{item.albumName}}
td(style="width: 18%;")
material-list-buttons(:index="index" :search-btn="true" :play-btn="item.source == 'kw' || (!isAPITemp && item.source != 'tx')" :download-btn="item.source == 'kw' || (!isAPITemp && item.source != 'tx')" :remove-btn="false" @btn-click="handleListBtnClick")
//- button.btn-info(type='button' v-if="item._types['128k'] || item._types['192k'] || item._types['320k'] || item._types.flac" @click.stop='openDownloadModal(index)')
//- button.btn-secondary(type='button' v-if="item._types['128k'] || item._types['192k'] || item._types['320k']" @click.stop='testPlay(index)')
//- button.btn-success(type='button' v-if="(item._types['128k'] || item._types['192k'] || item._types['320k']) && userInfo" @click.stop='showListModal(index)')
td(style="width: 10%;") {{item.interval || '--/--'}}
div(:class="$style.pagination")
material-pagination(:count="total" :limit="limit" :page="page" @btn-click="handleTogglePage")
div(v-else :class="$style.noitem")
p(v-html="noItem")
material-flow-btn(:show="isShowEditBtn && (source == 'kw' || !isAPITemp)" :remove-btn="false" @btn-click="handleFlowBtnClick")
</template>
@ -111,6 +112,7 @@ export default {
},
list(n) {
this.resetSelect()
if (!this.list.length) return
this.$nextTick(() => scrollTo(this.$refs.dom_scrollContent, 0))
},
},
@ -143,6 +145,7 @@ export default {
},
handleSelectAllData(isSelect) {
this.selectdList = isSelect ? [...this.list] : []
this.$emit('input', [...this.selectdList])
},
resetSelect() {
this.selectdList = false
@ -183,6 +186,7 @@ export default {
}
.thead {
flex: none;
background-color: @color-theme_2;
}
.tbody {
flex: auto;
@ -220,4 +224,12 @@ export default {
}
}
each(@themes, {
:global(#container.@{value}) {
.thead {
background-color: ~'@color-@{value}-theme_2';
}
}
})
</style>

View File

@ -3,7 +3,7 @@
div(:class="$style.label" ref="dom_btn" @click="handleShow") {{value.name}}
div.scroll(:class="$style.list" @click.stop ref="dom_list" :style="listStyle")
div(:class="$style.tag" @click="handleClick(null)") 全部
dl(:class="$style.type" v-for="type in list")
dl(v-for="type in list")
dt(:class="$style.type") {{type.name}}
dd(:class="$style.tag" v-for="tag in type.list" @click="handleClick(tag)") {{tag.name}}
</template>
@ -114,7 +114,7 @@ export default {
.list {
position: absolute;
top: 100%;
width: 644px;
width: 646px;
left: 0;
border-bottom: 2px solid @color-tab-border-bottom;
border-right: 2px solid @color-tab-border-bottom;
@ -125,6 +125,8 @@ export default {
transition: .25s ease;
transition-property: height, opacity;
z-index: 10;
padding: 10px;
box-sizing: border-box;
li {
cursor: pointer;
@ -145,11 +147,18 @@ export default {
}
}
.type {
padding-top: 10px;
padding-bottom: 3px;
color: #999;
}
.tag {
display: inline-block;
margin: 5px;
background-color: @color-btn-background;
padding: 5px;
padding: 8px 10px;
border-radius: @radius-progress-border;
cursor: pointer;
&:hover {
background-color: @color-theme_2-hover;
@ -176,6 +185,7 @@ each(@themes, {
.list {
border-bottom-color: ~'@{color-@{value}-tab-border-bottom}';
border-right-color: ~'@{color-@{value}-tab-border-bottom}';
// border-left-color: ~'@{color-@{value}-tab-border-bottom}';
li {
// color: ~'@{color-@{value}-btn}';
@ -188,6 +198,16 @@ each(@themes, {
}
}
}
.tag {
background-color: ~'@{color-@{value}-btn-background}';
&:hover {
background-color: ~'@{color-@{value}-theme_2-hover}';
}
&:active {
background-color: ~'@{color-@{value}-theme_2-active}';
}
}
}
})

View File

@ -25,6 +25,7 @@ const state = {
limit: 30,
key: null,
},
selectListInfo: {},
isVisibleListDetail: false,
}
@ -38,6 +39,7 @@ const getters = {
sourceInfo: () => ({ sources, sortList }),
tags: state => state.tags,
isVisibleListDetail: state => state.isVisibleListDetail,
selectListInfo: state => state.selectListInfo,
listData(state) {
return state.list
},
@ -56,7 +58,7 @@ const actions = {
let source = rootState.setting.songList.source
let tabId = rootState.setting.songList.tagInfo.id
let sortId = rootState.setting.songList.sortId
console.log(sortId)
// console.log(sortId)
let key = `${source}${sortId}${tabId}${page}`
if (state.list.list.length && state.list.key == key) return true
return music[source].songList.getList(sortId, tabId, page).then(result => commit('setList', { result, key, page }))
@ -65,7 +67,7 @@ const actions = {
let source = rootState.setting.songList.source
let key = `${source}${id}${page}`
if (state.listDetail.list.length && state.listDetail.key == key) return true
console.log(id, page)
commit('clearListDetail')
return music[source].songList.getListDetail(id, page).then(result => commit('setListDetail', { result, key, page }))
},
}
@ -92,6 +94,12 @@ const mutations = {
setVisibleListDetail(state, bool) {
state.isVisibleListDetail = bool
},
setSelectListInfo(state, info) {
state.selectListInfo = info
},
clearListDetail(state) {
state.listDetail.list = []
},
}
export default {

View File

@ -184,8 +184,8 @@ export const updateSetting = setting => {
tabId: 'kwbiaosb',
},
songList: {
source: 'kw',
sortId: 'hot',
source: 'kg',
sortId: '5',
tagInfo: {
name: '全部',
id: null,

View File

@ -144,7 +144,7 @@ export default {
this.getListUrl(sortId, tagId, page)
)
return this._requestObj_list.promise.then(({ body }) => {
if (body.error_code !== this.successCode) return this.getSongList(sortId, tagId, page)
if (body.error_code !== this.successCode) return this.getList(sortId, tagId, page)
return {
list: this.filterList(body.diyInfo),
total: body.nums,

View File

@ -231,7 +231,7 @@ export default {
return info
})
)
if (!tagId) tasks.push(this.getSongListRecommend()) // 如果是所有类别,则顺便获取推荐列表
if (!tagId && page === 1) tasks.push(this.getSongListRecommend()) // 如果是所有类别,则顺便获取推荐列表
return Promise.all(tasks).then(([list, info, recommendList]) => {
if (recommendList) list.unshift(...recommendList)
return {

View File

@ -7,31 +7,37 @@ export default {
_requestObj_hotTags: null,
_requestObj_list: null,
_requestObj_listDetail: null,
limit_list: 100,
limit_song: 25,
limit_list: 25,
limit_song: 100,
successCode: 200,
sortList: [
{
name: '最热',
id: 'hot',
},
{
name: '最新',
id: 'new',
},
{
name: '最热',
id: 'hot',
},
],
tagsUrl: 'http://wapi.kuwo.cn/api/pc/classify/playlist/getTagList?cmd=rcm_keyword_playlist&user=0&prod=kwplayer_pc_9.0.5.0&vipver=9.0.5.0&source=kwplayer_pc_9.0.5.0&loginUid=0&loginSid=0&appUid=76039576',
hotTagUrl: 'http://wapi.kuwo.cn/api/pc/classify/playlist/getRcmTagList?loginUid=0&loginSid=0&appUid=76039576',
getListUrl({ sortId, id, page }) {
return id
? `http://wapi.kuwo.cn/api/pc/classify/playlist/getTagPlayList?loginUid=0&loginSid=0&appUid=76039576&id=${id}&pn=${page}&rn=${this.limit_list}`
: `http://wapi.kuwo.cn/api/pc/classify/playlist/getRcmPlayList?loginUid=0&loginSid=0&appUid=76039576&pn=${page}&rn=${this.limit_list}&order=${sortId}`
getListUrl({ sortId, id, type, page }) {
console.log(id, type)
if (!id) return `http://wapi.kuwo.cn/api/pc/classify/playlist/getRcmPlayList?loginUid=0&loginSid=0&appUid=76039576&&pn=${page}&rn=${this.limit_list}&order=${sortId}`
switch (type) {
case '10000': return `http://wapi.kuwo.cn/api/pc/classify/playlist/getTagPlayList?loginUid=0&loginSid=0&appUid=76039576&pn=${page}&id=${id}&rn=${this.limit_list}`
case '43': return `http://mobileinterfaces.kuwo.cn/er.s?type=get_pc_qz_data&f=web&id=${id}&prod=pc`
}
// http://wapi.kuwo.cn/api/pc/classify/playlist/getTagPlayList?loginUid=0&loginSid=0&appUid=76039576&id=173&pn=1&rn=100
},
getListDetailUrl(id, page) {
// http://nplserver.kuwo.cn/pl.svc?op=getlistinfo&pid=2858093057&pn=0&rn=100&encode=utf8&keyset=pl2012&identity=kuwo&pcmp4=1&vipver=MUSIC_9.0.5.0_W1&newver=1
return `http://nplserver.kuwo.cn/pl.svc?op=getlistinfo&pid=${id}&pn=${page - 1}&rn=${this.limit_song}&encode=utf8&keyset=pl2012&identity=kuwo&pcmp4=1&vipver=MUSIC_9.0.5.0_W1&newver=1`
// http://mobileinterfaces.kuwo.cn/er.s?type=get_pc_qz_data&f=web&id=140&prod=pc
},
// http://nplserver.kuwo.cn/pl.svc?op=getlistinfo&pid=2849349915&pn=0&rn=100&encode=utf8&keyset=pl2012&identity=kuwo&pcmp4=1&vipver=MUSIC_9.0.5.0_W1&newver=1
// 获取标签
getTag() {
if (this._requestObj_tags) this._requestObj_tags.cancelHttp()
@ -52,7 +58,7 @@ export default {
},
filterInfoHotTag(rawList) {
return rawList.map(item => ({
id: item.id,
id: `${item.id}-${item.digest}`,
name: item.name,
}))
},
@ -62,7 +68,7 @@ export default {
list: type.data.map(item => ({
parent_id: type.id,
parent_name: type.name,
id: item.id,
id: `${item.id}-${item.digest}`,
name: item.name,
})),
}))
@ -71,14 +77,34 @@ export default {
// 获取列表数据
getList(sortId, tagId, page) {
if (this._requestObj_list) this._requestObj_list.cancelHttp()
this._requestObj_list = httpFatch(this.getListUrl({ sortId, id: tagId, page }))
let id
let type
if (tagId) {
let arr = tagId.split('-')
id = arr[0]
type = arr[1]
} else {
id = null
}
console.log(id, type)
this._requestObj_list = httpFatch(this.getListUrl({ sortId, id, type, page }))
return this._requestObj_list.promise.then(({ body }) => {
if (body.code !== this.successCode) return this.getListUrl({ sortId, id: tagId, page })
if (!id || type == '10000') {
if (body.code !== this.successCode) return this.getListUrl({ sortId, id, type, page })
return {
list: this.filterList(body.data.data),
total: body.data.total,
page: body.data.pn,
limit: body.data.rn,
}
} else if (!body.length) {
return this.getListUrl({ sortId, id, type, page })
}
return {
list: this.filterList(body.data.data),
total: body.data.total,
page: body.data.pn,
limit: body.data.rn,
list: this.filterList2(body),
total: 1000,
page,
limit: 1000,
}
})
},
@ -105,6 +131,24 @@ export default {
desc: item.desc,
}))
},
filterList2(rawData) {
const list = []
rawData.forEach(item => {
if (!item.label) return
list.push(...item.list.map(item => ({
play_count: item.play_count === undefined ? null : this.formatPlayCount(item.listencnt),
id: item.id,
author: item.uname,
name: item.name,
// time: item.publish_time,
img: item.img,
grade: item.favorcnt / 10,
desc: item.desc,
})))
})
console.log(list)
return list
},
// 获取歌曲列表内的音乐
getListDetail(id, page) {

View File

@ -5,8 +5,18 @@
material-tab(:class="$style.tab" :list="sorts" item-key="id" item-name="name" v-model="sortId")
material-select(:class="$style.select" :list="sourceInfo.sources" item-key="id" item-name="name" v-model="source")
div(:class="$style.container")
div(:class="$style.materialSongList" v-show="isVisibleListDetail")
material-song-list(v-model="selectdData" @action="handleSongListAction" :source="source" :page="listDetail.page" :limit="listDetail.limit" :total="listDetail.total" :list="listDetail.list")
transition(enter-active-class="animated-fast fadeIn" leave-active-class="animated-fast fadeOut")
div(:class="$style.materialSongList" v-show="isVisibleListDetail")
div(:class="$style.songListHeader")
div(:class="$style.songListHeaderLeft")
img(:src="selectListInfo.img")
span(:class="$style.playNum" v-if="selectListInfo.play_count") {{selectListInfo.play_count}}
div(:class="$style.songListHeaderMiddle")
h3(:title="selectListInfo.name") {{selectListInfo.name}}
p(:title="selectListInfo.desc") {{selectListInfo.desc}}
div(:class="$style.songListHeaderRight")
material-btn(:class="$style.closeDetailButton" @click="hideListDetail") 返回
material-song-list(v-model="selectdData" @action="handleSongListAction" :source="source" :page="listDetail.page" :limit="listDetail.limit" :total="listDetail.total" :list="listDetail.list")
div.scroll(:class="$style.content" ref="dom_scrollContent" v-show="!isVisibleListDetail")
ul
li(:class="$style.item" v-for="(item, index) in listData.list" @click="handleItemClick(index)")
@ -15,6 +25,7 @@
div(:class="$style.right" :src="item.img")
h4(:title="item.name") {{item.name}}
p(:title="item.desc") {{item.desc}}
li(:class="$style.item" style="cursor: default;" v-if="listData.list && listData.list.length && listData.list.length % 3 == 2")
div(:class="$style.pagination")
material-pagination(:count="listData.total" :limit="listData.limit" :page="listData.page" @btn-click="handleTogglePage")
material-download-modal(:show="isShowDownload" :musicInfo="musicInfo" @select="handleAddDownload" @close="isShowDownload = false")
@ -44,7 +55,7 @@ export default {
},
computed: {
...mapGetters(['setting']),
...mapGetters('songList', ['sourceInfo', 'tags', 'listData', 'isVisibleListDetail', 'listDetail']),
...mapGetters('songList', ['sourceInfo', 'tags', 'listData', 'isVisibleListDetail', 'selectListInfo', 'listDetail']),
...mapGetters('list', ['defaultList']),
sorts() {
return this.source ? this.sourceInfo.sortList[this.source] : []
@ -60,11 +71,14 @@ export default {
sortId(n, o) {
this.setSongList({ sortId: n })
if (o === undefined && this.listData.page !== 1) return
this.getList(1).then(() => {
this.$nextTick(() => {
scrollTo(this.$refs.dom_scrollContent, 0)
this.$nextTick(() => {
this.getList(1).then(() => {
this.$nextTick(() => {
scrollTo(this.$refs.dom_scrollContent, 0)
})
})
})
// if (this.isVisibleListDetail) this.setVisibleListDetail(false)
},
tagInfo(n, o) {
this.setSongList({ tagInfo: n })
@ -73,11 +87,14 @@ export default {
this.isToggleSource = false
return
}
this.getList(1).then(() => {
this.$nextTick(() => {
scrollTo(this.$refs.dom_scrollContent, 0)
this.$nextTick(() => {
this.getList(1).then(() => {
this.$nextTick(() => {
scrollTo(this.$refs.dom_scrollContent, 0)
})
})
})
// if (this.isVisibleListDetail) this.setVisibleListDetail(false)
},
source(n, o) {
this.setSongList({ source: n })
@ -101,7 +118,7 @@ export default {
methods: {
...mapMutations(['setSongList']),
...mapActions('songList', ['getTags', 'getList', 'getListDetail']),
...mapMutations('songList', ['setVisibleListDetail']),
...mapMutations('songList', ['setVisibleListDetail', 'setSelectListInfo']),
...mapActions('download', ['createDownload', 'createDownloadMultiple']),
...mapMutations('list', ['defaultListAdd', 'defaultListAddMultiple']),
...mapMutations('player', ['setList']),
@ -175,8 +192,9 @@ export default {
this.isShowDownloadMultiple = false
},
handleItemClick(index) {
this.setSelectListInfo(this.listData.list[index])
this.setVisibleListDetail(true)
this.getListDetail({ id: this.listData.list[index].id, page: 1 })
this.getListDetail({ id: this.selectListInfo.id, page: 1 })
},
handleFlowBtnClick(action) {
switch (action) {
@ -209,6 +227,9 @@ export default {
resetSelect() {
this.selectdData = []
},
hideListDetail() {
setTimeout(() => this.setVisibleListDetail(false), 50)
},
},
}
</script>
@ -240,37 +261,101 @@ export default {
.container {
flex: auto;
overflow: hidden;
// position: relative;
}
.materialSongList {
.song-list-header {
background-color: @color-theme_2;
display: flex;
flex-flow: row nowrap;
height: 60px;
}
.song-list-header-left {
flex: none;
margin-left: 5px;
width: 60px;
position: relative;
img {
max-width: 100%;
max-height: 100%;
}
.play-num {
position: absolute;
bottom: 0;
left: 0;
right: 0;
padding: 2px;
background-color: rgba(0, 0, 0, 0.4);
color: #fff;
font-size: 11px;
text-align: right;
.mixin-ellipsis-1;
}
}
.song_list_header_middle {
flex: auto;
padding: 5px 7px;
h3 {
.mixin-ellipsis-1;
line-height: 1.2;
padding-bottom: 5px;
}
p {
.mixin-ellipsis-2;
font-size: 12px;
line-height: 1.2;
color: #888;
}
}
.song-list-header-right {
flex: none;
display: flex;
align-items: center;
padding-right: 15px;
}
.material-song-list {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
display: flex;
flex-flow: column nowrap;
}
.content {
height: 100%;
overflow-y: auto;
padding: 0 15px;
background-color: #fff;
ul {
display: flex;
flex-flow: row wrap;
justify-content: space-between;
}
}
.item {
width: 100% / 3;
width: 32%;
box-sizing: border-box;
display: flex;
padding-top: 15px;
margin-top: 15px;
cursor: pointer;
transition: opacity @transition-theme;
&:hover {
opacity: .7;
}
}
.left {
flex: none;
width: 30%;
width: 66px;
height: 66px;
display: flex;
img {
max-width: 100%;
max-height: 100%;
}
}
.right {