vue实战 ---- 一个简单的音乐搜索网站(start)
vue实战 ---- 一个简单的音乐搜索网站(一)
vue实战 ---- 一个简单的音乐搜索网站(二)
1. songHeader.vue代码
挂件,没什么意义,
<template>
<ul class="song-header">
<div class="song-name">歌曲名</div>
<div class="author">歌手</div>
<div class="song-play">播放</div>
</ul>
</template>
<script type="text/javascript">
export default {
name: 'songHeader',
data() {
return {
}
}
}
</script>
<style type="text/css" scoped>
.song-header {
display: flex;
width: 600px;
margin: auto;
padding: 0.5em 1em;
font-size: 16px;
background-color: #e4c6d0;
border-radius: 20px 20px 0 0;
box-sizing: border-box;
}
.song-header > div {
box-sizing: border-box;
}
.song-name {
width: 40%;
padding-left: 1em;
border-right: 1px solid #aaa;
}
.author {
width: 30%;
text-align: center;
border-right: 1px solid #aaa;
}
.song-play {
width: 30%;
text-align: center;
}
</style>
2. searchContent.vue代码
主要的代码,有点长
<template>
<div class="search-content">
<ul class="song-list">
<input type="hidden" v-model='slist'>
<li v-for='(song, index) in showPageList' :class="[index%2 == 0 ? 'even' : '']">
<div class="song-name">{{song.name}}</div>
<div class="author">{{song.ar[0].name}}</div>
<div class="song-play">
<div class="play" :songID="song.id" @click='play_url'></div>
<div class="like"></div>
<div class="add"></div>
</div>
</li>
</ul>
<ul v-show='showPageList' class="pagination">
<input type="hidden" v-model='curPage'>
<li class="prev-page" @click='prevPage'>上一页</li>
<li v-for='num in pageNum' :class="['page-num', num==curPage?'active':'']">{{num}}</li>
<li class="next-page" @click='nextPage'>下一页</li>
</ul>
<div class="set-footer">
<audio class="player" :src="songUrl" controls="controls" loop="loop" autoplay="true"></audio>
</div>
</div>
</template>
<script type="text/javascript">
export default {
props: ['slist'],
name: 'searchContent',
data(){
return {
songNum: 0, //多少条数据
pageNum: [], //总页数转化成列表
pageSize: 10, //每一页的数量
totalPage: 1, //总页数
curPage: 1, //当前页数
showPageList: 0, //当前显示的页面
songUrl: '',
idList: [],
}
},
watch: {
slist(newData){
this.pageNum = []; // 每一次更新页码前先清零
this.songNum = newData.length;
this.totalPage = Math.ceil(this.songNum / this.pageSize);
this.totalPage = this.totalPage == 0 ? 1 : this.totalPage;
this.changeList();
this.getShowPageList(newData, this.curPage)
},
curPage(newPage){
this.getShowPageList(this.slist, newPage)
}
},
methods: {
changeList(){
for (var i = 1; i <= this.totalPage; i++) {
this.pageNum.push(i)
}
},
getShowPageList(songlist, index){ // 获取当前应展示的页面数据
let begin = (index - 1) * this.pageSize;
let end = index * this.pageSize;
this.showPageList = songlist.slice(begin, end);
},
prevPage(e){
if(this.curPage != 1){
this.curPage --;
}
},
nextPage(e){
if(this.curPage != this.totalPage){
this.curPage ++;
}
console.info(this.curPage);
},
play_url(e){
console.info(e.target.attributes.songID.nodeValue);
var id = e.target.attributes.songID.nodeValue
this.songUrl = 'https://v1.itooi.cn/netease/url?id=' + id + '&quality=flac'
},
},
}
</script>
<style type="text/css" scoped>
.search-content {
/*border: 1px solid red;*/
width: 600px;
height: 100%;
margin: auto;
margin-bottom: 100px;
}
.song-list {
/*border: 1px solid red;*/
list-style: none;
padding: 0;
margin: 0;
background-color: #CCCCFF;
}
.song-list > li {
display: flex;
/*border: 1px solid red;*/
margin: 0;
padding: 0.3em 1em;
font-size: 14px;
}
/*偶数行*/
.even {
background-color: #FFFFCC;
}
.song-list > li > div {
box-sizing: border-box;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
.song-list > li > div:hover {
white-space: normal;
overflow: auto;
}
.song-list > li .song-name {
width: 40%;
}
.song-list > li .author {
width: 35%;
padding-left: 3em;
}
.song-list > li .song-play {
width: 25%;
}
.song-play {
display: flex;
}
.song-play > div {
background-size: 100%;
margin-left: 1em;
width: 1.4em;
height: 1.4em;
}
.song-play > div:hover {
background-size: 100%;
}
.play {
background: url("~@/assets/images/play.png") no-repeat;
}
.like {
background: url('~@/assets/images/like.png') no-repeat;
}
.add {
background: url('~@/assets/images/add.png') no-repeat;
}
.play:hover {
background: url("~@/assets/images/play_active.png") no-repeat;
}
.like:hover {
background: url('~@/assets/images/like_active.png') no-repeat;
}
.add:hover {
background: url('~@/assets/images/add_active.png') no-repeat;
}
/*播放器*/
.set-footer {
width: 100%;
margin: auto;
position: fixed;
bottom: 0;
left: 0;
}
.player {
width: 100%;
}
/*-----分页------*/
.pagination {
margin: 0;
padding: 0;
}
.pagination:hover{
cursor: pointer;
}
.pagination > li{
list-style: none;
width: 1.5em;
float: left;
text-align: center;
border: 1px solid #48beef;
/*border: 1px solid red;*/
}
.pagination > li.prev-page, .pagination > li.next-page {
width: 4em;
background: #ccff99;
}
.page-num {
}
.active {
background: #e43d38;
}
</style>
3. 代码详解
(1)加载歌曲数据
-
props: ['slist']
这里props定义了类属性可以从父亲组件接收歌曲数据然后用v-for循环导入 -
class="[index%2 == 0 ? 'even' : '']"
css中我写了一个even的样式,就是偶数行颜色和奇数行颜色不同,让界面好看一些
(2)分页功能
这里耗费了我半天时间才解决,思路看起来也不难,歌曲总数songNum,定义一页展示
pageSize: 10条数据,那么总页数就是totalPage = songNum / pageSize
-
getShowPageList方法
获取当前应展示的数据,原理也不难,使用slice函数对数组切片就行 -
prevPage方法
点击的时候展示上一页数据 -
nextPage方法
点击的时候展示下一页数据 -
changeList方法
页码转换成数组,使得可以v-for导入
分页的思路看似不难,但还是花费了我蛮久时间才弄好,因为在加载数据时就要分页,那么想当然就是在created或mounted方法中调用上面分页的函数(涉及dom操作最好放到mounted方法中),一开始点击搜索的时候确实实现了分页,但是再点击一次就不会执行了,并且数据也没有刷新。数据是从父亲组件传来的,点击搜索事件也不是在本组件进行的,所以当歌曲数据改变的时候,当前组件并不知情,为了解决这个问题,就要用到watch方法监控数据的变动,用两个‘hidden’属性的input标签v-model绑定歌曲列表数据slist,当这个数据变动时,所有分页相关的方法也执行一次,子组件的数据也就随父亲组件的数据刷新了。
(3)播放
直接引用了html5原生的audio标签,设置了controls属性显示播放控件,设置了autoplay属性使得就绪状态就可以播放,实现方法就是直接绑定src属性,点击播放按钮时设置歌曲链接。