由于 QQ音乐的歌曲文件时不时更新一下,我在尝试了获取vkey后仍然是不能播放歌曲,所以我把歌单的数据换成了酷狗的API,酷狗API请求歌曲简单多了,在推荐组件下请求歌单,返回歌单的id,然后根据某一歌单的id可以请求回这个歌单下的所有歌曲的一些基本信息,根据基本信息下包含的hash值,可以请求回来这首歌曲的完整信息,包括可以直接播放的play_url 地址
页面结构还是和之前差不多,不过数据属性名有点差异
请求函数换成酷狗的
引入请求酷狗歌单的函数
import axios from 'axios';
let commonParams = {
inCharset: 'utf-8',
outCharset: 'utf-8'
}
export function getKugouDiscList() {
const url = 'KugouMusicAPI/plist/index&json=true'
const data = Object.assign({}, commonParams, {
})
return axios.get(url, {
params: data
}).then((res) => { //成功的回调
return Promise.resolve(res.data.plist.list.info)
})
}
由于这个请求只是单纯的跨域了,所以使用proxyTable 进行代理请求即可跨域访问
把请求回来的数据进行处理,然后转存在组件的data中
.
完成酷狗音乐歌单的请求,效果页
然后给每个歌单的li项,绑定点击事件,通过路由参数来传递数据(尽量不用vuex),实现路由跳转到歌单详情页
路由配置信息
跳转到歌单详情页 disc.vue,其中有用到musicList和songList组件,由于之前是QQ音乐API,现在是酷狗,所以我把这两个组件重新弄了一份kugouMusicList.vue和kugouSongList.vue,大部分结构保持不变,只是为了保证页面组件的可阅读性。在disc组件中,通过路由参数id,使用proxyTable 进行代理请求即可跨域访问。
disc.vue
<template>
<div class="disc">
<KugoumusicList
:bgImage="this.$route.params.imgurl"
:songs="disc.info"
:title="this.$route.params.specialname"
></KugoumusicList>
</div>
</template>
<script>
import KugoumusicList from "../../components/music-list/KugouMusic-list";
import { getKugouDiscList } from "../../api/kugouDisc"; //获取歌单下的歌曲
import { mapMutations } from "vuex";
import { mapGetters } from "vuex";
export default {
data() {
return {
specialid: this.$route.params.id, //传入的歌单id,以发送axios请求,获取该歌单下的所有歌曲
kugouDiscList: []
};
},
created() {
this._getKugouDiscList();
},
methods: {
async _getKugouDiscList() {
if (!this.$route.params.imgurl && !this.$route.params.specialname) {
//刷新页面导致params数据丢失,返回到推荐页
this.$router.push("/home/recommend");
}
this.kugouDiscList = await getKugouDiscList(this.specialid);
this.setDisc(this.kugouDiscList); //vuex设置歌单数据
},
//设置歌单
...mapMutations({
setDisc: "set_disc" //设置歌单信息
})
},
components: {
KugoumusicList
},
computed: {
...mapGetters(["disc"])
},
};
</script>
<style>
.disc {
position: absolute;
width: 100%;
height: 100vh;
top: 0;
color: black;
background-color: white;
}
</style>
kugouMusicList.vue
<template>
<div class="music-list">
<div class="imgDiv">
<!-- 顶部返回和标题 -->
<header class="backgroundDiv">
<div @click="goBack" class="el-icon-arrow-left goBack"></div>
<span class="title">{{title}}</span>
</header>
<!-- <img v-lazy="bgImage" class="bgImage" ref="bgImage" /> -->
<div class="bgImage" ref="bgImage" :style="bgStyle"></div>
</div>
<div
ref="list"
class="songList flexUl"
v-loading="songs&&!songs.length"
element-loading-text="拼命加载中"
element-loading-spinner="el-icon-loading"
element-loading-background="rgba(255,255,255, 0.8)"
element-loading-customClass="loadingCustomClass"
style="position:relative;min-height:50vh;font-size:1rem"
>
<KugouSongList :songs="songs" @select="selectItem"></KugouSongList>
<!-- <player></player> -->
</div>
</div>
</template>
<script>
import scroll from "../base/scroll";
import KugouSongList from "../../components/base/KugouSongList";
import { mapActions } from "vuex";
import player from "../../components/player/player";
export default {
props: {
bgImage: {
type: String,
default: ""
},
songs: {
type: Array,
default: ()=>{}
},
title: {
type: String,
default: ""
}
},
data() {
return {};
},
methods: {
goBack() {
this.$router.go(-1);
},
selectItem(item, index) {
//使用mapActions 暴露出来的 selectPlay函数,来设置播放器的播放内容
this.selectPlay({
list: this.songs, //为何不直接使用 item ,item在当前组件表示点击的某首歌曲,
//此处传入的list将整个songs列表都传递进去,可以通过index查找,比起单首歌曲的播放列表,
//显然完整的歌曲列表更加方便后续的使用
index:index
});
},
...mapActions(["selectPlay"])
},
components: {
scroll,
KugouSongList,
player
},
computed: {
bgStyle() {
return `background-image:url(${this.bgImage});background-size:cover;`;
}
}
};
</script>
<style lang="less" scoped>
kugouSongList.vue
<template>
<div class="songList">
<ul v-if="songs" :style="isPaddingBottom">
<li @click="selectItem(item,index)" v-for="(item,index) in songs" :key="'info-'+index">
<div class="container">
<h2 class="name">
<span>{{index+1 | indexFilter}}、</span>
{{item.filename}}
</h2>
</div>
</li>
</ul>
</div>
</template>
<script>
import { mapGetters } from "vuex";
export default {
props: {
songs: {
type: Array,
default: () => {}
}
},
data() {
return {
};
},
methods: {
selectItem(item, index) {
this.$emit("select", item, index); //让父组件去处理
}
},
filters: {
indexFilter: function(val) {
if (val.toString().length == 1) {
return "0" + val;
} else {
return val;
}
}
},
watch: {
// 使用路由参数时,例如从 /user/foo 导航到 /user/bar,
// 原来的组件实例会被复用。因为两个路由都渲染同个组件,
// 比起销毁再创建,复用则显得更加高效。
// 不过,这也意味着组件的生命周期钩子不会再被调用。
fullScreen: function(newVal) {
newVal == true
? (this.isPaddingBottom = "")
: (this.isPaddingBottom = "padding-bottom:4.5rem");
}
},
computed: {
...mapGetters(["playList", "fullScreen"]),
isPaddingBottom: {
get: function() {
if (this.playList) {
if (this.playList.length) {
return this.fullScreen ? "" : "padding-bottom:4.5rem";
}
} else {
return ""
}
},
set: function(newValue) {}
}
}
};
</script>
<style lang="less" scoped>
其中涉及到的对vuex的数据的判断,是因为在player.vue播放器组件里有正常全屏播放器和迷你底部栏播放器,由于底部栏播放器固定在底部,会对歌单列表和歌单歌曲列表产生遮盖,因此使用playList数据来判断是否处于播放状态,和fullScreen数据判断是否处于全屏播放状态,以此来调整涉及到的组件的底部留空和better-scroll组件的height属性。
给歌单中的歌曲li项绑定点击事件,并将其抛给父组件处理
在父组件kugouMusicList中接收
并通过vuex来改变相关的数据(list和index)
其中mapActions和mapMutations和mapGetters一样,是vuex的辅助函数,通过在action.js文件中定义的selectPlay函数,可以对state多个数据进行操作(该函数集中了对所需数据的操作),但是action.js的主要作用是处理异步操作(mutation只能处理同步操作),具体请看vuex官方文档,此时我们便可以通过mapActions辅助函数引入selectPlay函数,通过传入我们的数据,来实现对state数据的更新。
效果页:
下一篇将详细介绍 player.vue 播放器组件的页面结构和数据请求