首先是环境的搭建
①安装node.js,安装步骤如下:
进入http://nodejs.cn/download/,我下载的是win64位的安装包,一路下一步,完成安装。进入cmd,输入node -v以及npm -v 查看安装版本。
由于使用npm原本的源下载可能会慢,这里我用的是淘宝的镜像
npm config set registry https://registry.npm.taobao.org
安装淘宝镜像
②安装vue,通过npm install vue安装vue。安装成功后,用vue-cli(脚手架)创建vue项目,通过cmd在想要创建的文件目录下输入 vue create 项目名即可创建项目,
然后回车,出现如下界面。
这里是选择项目预设的插件,第一个是默认,第二个是自己设置。这里我选择第二个,出现如下界面,选择插件,这里我只选择了Babel
以下为一部分解释
1 >( ) TypeScript // 支持使用 TypeScript 书写源码 2 ( ) Progressive Web App (PWA) Support // PWA 支持 3 ( *) Router // 支持 vue-router 4 ( ) Vuex // 支持 vuex 5 (* ) CSS Pre-processors // 支持 CSS 预处理器。 6 ( *) Linter / Formatter // 支持代码风格检查和格式化。 7 ( ) Unit Testing // 支持单元测试。 8 ( ) E2E Testing // 支持 E2E 测试。
然后都下一步,接着等待vue-cli配置完成。然后cd进入创建的项目文件夹,然后使用npm run serve就可以获得vue项目对应的页面。(我的理解就是编译)
由于我用的是idea,接下来在介绍一下通过idea打开创建的vue项目。首先要安装vue.js插件,通过settings->plugings安装,我是先下载了安装包,再安装的。
安装完成后,通过open可以打开创建的项目,以下是文件目录。
*组件是App.vue。组件放在components里,assets里可以放图片等资源。然后还需要安装axios和vue-router(分别用于获取数据和页面的切换)还有element-ui,通过idea终端输入命令npm install vue-router
安装路由,然后需要在main.js中执行。接着就可以使用路由了。通过终端输入命令npm install axios安装axios,在需要使用的页面导入一下即可使用。通过npm i element-ui -S 安装element-ui,然后导入,使用。
具体实现:
我实现了以下功能:
①发现音乐 ②推荐歌单 ③最新音乐 ④最新MV ⑤搜索歌曲、歌单、MV
概览
这是一个单页面应用SPA,主页面分为最上面的板块(包含名字,搜索栏)和下面的板块(选项卡+主要内容),其中各个模块都是由路由显示在主要内容里的,即让路由的出口设在那个页面里。
以下为组件图
*组件App.vue下是top.vue和main_wrap.vue组件,其他的都是路由设在main_wrap里的组件。
问题总结:
要熟悉一些ES6语法,可以提高代码效率;
需要注意vue组件中html写在<template>标签中,<script>中写js,<style>中写css。<template>中只能有一个顶层标签;
使用的插件如router,axios,element-ui都需要导入,在main.js中完成相应操作;
v-for组件的使用要有key;
在当前组件导入其他组件时,需要使用import导入,然后在components中注册组件;
在组件中传参数可以使用this.$router.push传参,this.$route接收;
created(){},watch:{},的使用;
通过this.$router.push到当前页面会出错,页面不会自动刷新,可以使用watch监听$route变化;
直接在for循环中使用axios会出错,即在for循环中进行异步操作会出问题,可以用递归或者async await解决;
在数据处理时将时间戳转化为年月日,可以使用date对象的方法,我这里通过重写toLocaleString方法获得想要的时间格式;
还有要注意接口获得的数据格式,自己定义的数据格式要与想获得的数据一样;
element-ui中的icon本质是字体,还有轮播图,分页、选项卡,表格的使用都需要注意;
还有一些动态出现的按钮可以通过overflow实现;
这次使用的css有很多重复,下次可以先注意一下,直接引入,而不用在每个组件里都写一遍;
多行文字溢出效果实现,https://blog.csdn.net/anwj1020/article/details/93481799
关于display:flex布局方式,可以查看http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html;
以下为代码:
App.vue
<template> <div id="app"> <top></top> <main_wrap></main_wrap> </div> </template> <script> import top from "./components/top.vue" import main_wrap from "./components/main_wrap" export default { name: \'App\', components: { top:top, main_wrap:main_wrap, } } </script> <style> *{ margin: 0; padding: 0; box-sizing: border-box; transition: 0.5s; } </style>
top.vue
<template> <div id="top"> <span>DougIger\'s MP3</span> <input type="text" v-model="inputValue" placeholder="搜索歌曲" @keyup.enter="searchMusic"> <el-button class="search_but" icon="el-icon-search" circle @click="searchMusic"></el-button> </div> </template> <script> export default { name: "top", data(){ return{ inputValue:\'\', } }, methods:{ searchMusic(){ if(this.inputValue==\'\'){ //为空则弹出提示框 this.$message({ message:\'请输入内容\', center:true, type:\'warning\', }) }else{ //有内容则跳转 this.$router.push(\'/result?keyword=\'+this.inputValue); } } } } </script> <style scoped> #top{ display: flex; position: fixed; align-items: center; width:100%; height: 60px; top:0; left: 0; background-color: #f9f9f9; z-index: 999; padding:20px; justify-content:flex-end; } #top>span{ position:absolute; left: 20px; font-size: 25px; font-weight: bold; color: #A6B0D9; } #top>img{ width: 85%; } .search_but{ margin-left: 10px; } </style>
main_wrap.vue
1 <template> 2 <div id="main_wrap"> 3 <div class="nav"> 4 <ul> 5 <li><router-link to="/discovery">发现音乐</router-link></li> 6 <li><router-link to="/recommend">推荐歌单</router-link></li> 7 <li><router-link to="/newMusic">最新音乐</router-link></li> 8 <li><router-link to="/newMv">最新MV</router-link></li> 9 </ul> 10 </div> 11 <div class="main"> 12 <!-- <discovery></discovery>--> 13 <!-- 路由的出口 地址命中之后,把组件显示的位置--> 14 <router-view :key="this.$route.path"></router-view> 15 </div> 16 <div class="player"> 17 <audio :src="musicUrl" controls="controls" autoplay></audio> 18 </div> 19 </div> 20 </template> 21 22 <script> 23 import discovery from \'./discovery.vue\' 24 export default { 25 name: "main_wrap", 26 data(){ 27 return{ 28 musicUrl:"", 29 } 30 }, 31 components:{ 32 discovery,//discovery:discovery 33 } 34 } 35 </script> 36 37 <style scoped> 38 #main_wrap{ 39 position: relative; 40 display: flex; 41 width: 100%; 42 /*设置宽度为当前设备的视窗*/ 43 height: calc(100vh); 44 padding-top: 60px; 45 padding-bottom: 60px; 46 } 47 .nav{ 48 width: 200px; 49 height: 100%; 50 background-color:#ededed; 51 } 52 ul>li{ 53 text-align: center; 54 margin:30px auto; 55 font-size: 20px; 56 } 57 .main{ 58 flex:1; 59 overflow-y: scroll; 60 } 61 a.router-link-exact-active{ 62 color:white; 63 background-color: orange; 64 } 65 a{ 66 color:black; 67 text-decoration: none; 68 } 69 .player{ 70 position: fixed; 71 bottom: 0; 72 left: 0; 73 height:60px; 74 width: 100%; 75 background-color:rgb(241,243,244); 76 } 77 .player>audio{ 78 display: block; 79 outline: none; 80 width: 100%; 81 } 82 83 </style>
discovery.vue(发现音乐)
1 <template> 2 <div> 3 <!--elementui的轮播图--> 4 <el-carousel :interval="4000" type="card" height="250px" class="banner"> 5 <el-carousel-item v-for="(item,index) in banner" :key="index"> 6 <img :src="item.imageUrl" alt=""> 7 </el-carousel-item> 8 </el-carousel> 9 <!--推荐歌单--> 10 <div class="recommend"> 11 <h3 class="title">推荐歌单</h3> 12 <div class="items"> 13 <div class="item" v-for="(item,index) in list" :key="index" @click="toPlayList(item.id)"> 14 <div class="img_wrap"> 15 <span class="copywriter">{{item.copywriter}}</span> 16 <img :src="item.picUrl" alt=""> 17 <span></span> 18 </div> 19 <p>{{item.name}}</p> 20 </div> 21 </div> 22 </div> 23 <!--最新歌曲--> 24 <div class="newsong"> 25 <h3>最新音乐</h3> 26 <div class="items"> 27 <div class="item" v-for="(item,index) in newsongs" :key="index"> 28 <div class="img_wrap"> 29 <img :src="item.picUrl" alt=""> 30 <span class="el-icon-video-play play-but" @click="playMusic(item.id)"></span> 31 </div> 32 <div class="song_wrap"> 33 <p class="song_name">{{item.name}}</p> 34 <p class="song_artist">{{item.song.artists[0].name}}</p> 35 </div> 36 </div> 37 </div> 38 </div> 39 <!--推荐mv--> 40 <div class="newMv"> 41 <h3>推荐MV</h3> 42 <div class="items"> 43 <div class="item" v-for="(item,index) in mvs" :key="index"> 44 <div class="img_wrap"> 45 <img :src="item.picUrl" alt="" @click="toMv(item.id)"> 46 <span class="play_count"><i class="el-icon-caret-right"></i>{{item.playCount}}</span> 47 </div> 48 <div class="info_wrap"> 49 <p>{{item.name}}</p> 50 <p class="song_artist">{{item.artistName}}</p> 51 </div> 52 </div> 53 </div> 54 </div> 55 56 </div> 57 </template> 58 59 <script> 60 //导入axios 61 import axios from \'axios\' 62 export default { 63 name: "discovery", 64 data(){ 65 return{ 66 //轮播图 67 banner:[], 68 list:[], 69 newsongs:[], 70 mvs:[], 71 } 72 }, 73 created() { 74 //调用轮播图接口 75 axios({ 76 url:\'https://autumnfish.cn/banner\', 77 method:\'get\', 78 }).then(response=>{ 79 this.banner=response.data.banners; 80 }) 81 //调用推荐歌单接口 82 axios({ 83 url:\'https://autumnfish.cn/personalized\', 84 method:\'get\', 85 params:{ 86 //获取的数据量 87 limit:10, 88 }, 89 }).then(response=>{ 90 //console.log(response); 91 this.list=response.data.result; 92 }) 93 axios({ 94 url:\'https://autumnfish.cn/personalized/newsong\', 95 method:\'get\', 96 }).then(response=>{ 97 //console.log(response); 98 this.newsongs=response.data.result; 99 100 }) 101 axios({ 102 url:\'https://autumnfish.cn/personalized/mv\', 103 method:\'get\', 104 }).then(response=>{ 105 //console.log(response); 106 this.mvs=response.data.result; 107 }) 108 }, 109 methods:{ 110 playMusic(id){ 111 axios({ 112 url:\'https://autumnfish.cn/song/url\', 113 method:\'get\', 114 params:{ 115 id:id, 116 }, 117 }).then(response=>{ 118 this.$parent.musicUrl=response.data.data[0].url; 119 }) 120 }, 121 toPlayList(id){ 122 //传参数到PlayList 123 this.$router.push(`/PlayList?id=${id}`); 124 }, 125 toMv(id){ 126 //传参数到MV 127 this.$router.push(`/MV?id=${id}`); 128 } 129 } 130 131 } 132 </script> 133 134 135 <style scoped> 136 img{ 137 width: 100%; 138 height: 100%; 139 border-radius:5px ; 140 } 141 .banner{ 142 width: 90%; 143 position: relative; 144 overflow: hidden; 145 top:5px; 146 left:5%; 147 } 148 .recommend{ 149 position: relative; 150 width:90%; 151 left: 5%; 152 } 153 .items{ 154 display: flex; 155 flex-wrap:wrap; 156 } 157 .item{ 158 position: relative; 159 width: 200px; 160 margin: 10px; 161 overflow: hidden; 162 } 163 .img_wrap{ 164 position: relative; 165 width: 200px; 166 height: 200px; 167 cursor: pointer; 168 } 169 .copywriter{ 170 position: absolute; 171 width: 200px; 172 top:-50px; 173 text-align: center; 174 } 175 .img_wrap:hover .copywriter{ 176 position: absolute; 177 width: 200px; 178 top:0; 179 color: white; 180 background-color: black; 181 opacity: 0.8; 182 text-align: center; 183 } 184 .newsong{ 185 position: relative; 186 top:20px; 187 width:90%; 188 left: 5%; 189 } 190 .newsong .item{ 191 display: flex; 192 position: relative; 193 width: 40%; 194 height: 100px; 195 margin: 10px 20px; 196 } 197 .newsong .item:hover{ 198 background-color:darkgray; 199 opacity: 0.8; 200 } 201 .newsong .img_wrap{ 202 width: 100px; 203 height: 100px; 204 } 205 .newsong .song_wrap{ 206 position: relative; 207 flex-direction: row; 208 flex: 1; 209 margin: 10px; 210 } 211 .song_artist{ 212 color: grey; 213 font-size:15px; 214 margin-top: 20px; 215 } 216 .play-but{ 217 display: block; 218 position: absolute; 219 width: 50px; 220 height: 50px; 221 bottom: -50px; 222 left: 28px; 223 font-size:50px; 224 color: sandybrown; 225 } 226 .img_wrap:hover .play-but{ 227 display: block; 228 position: absolute; 229 width: 50px; 230 height: 50px; 231 bottom: 20px; 232 left: 28px; 233 font-size:50px; 234 color: sandybrown; 235 } 236 .newMv{ 237 position: relative; 238 top:40px; 239 width:90%; 240 left: 5%; 241 } 242 .newMv .item{ 243 width: 22%; 244 } 245 .newMv .img_wrap{ 246 width: 250px; 247 height: 150px; 248 } 249 .newMv .info_wrap{ 250 text-align: center; 251 } 252 .newMv .song_artist{ 253 margin-top: 10px; 254 } 255 .newMv .play_count{ 256 background-image: linear-gradient(to right,rgba(255,0,0,0),rgba(58,58,10,1)); 257 position: absolute; 258 right: 0; 259 color: white; 260 } 261 h3{ 262 font-weight: normal; 263 } 264 265 266 267 </style>
recommendList.vue(推荐歌单)
1 <template> 2 <div> 3 <div class="top_card"> 4 <div class="img_wrap"> 5 <img :src="topList.coverImgUrl" alt=""> 6 </div> 7 <div class="info_wrap"> 8 <div class="tag">精品歌单</div> 9 <div class="title">{{topList.name}}</div> 10 <div class="content">{{topList.description}}</div> 11 </div> 12 <img :src="topList.coverImgUrl" class="bgimg"> 13 <div class="bg_mask"></div> 14 </div> 15 <div class="main_card"> 16 <div class="tab_bar"> 17 <span class="types" :class="{active:tag==\'全部\'}" @click="tag=\'全部\'">全部</span> 18 <span class="types" :class="{active:tag==\'欧美\'}" @click="tag=\'欧美\'">欧美</span> 19 <span class="types" :class="{active:tag==\'华语\'}" @click="tag=\'华语\'">华语</span> 20 <span class="types" :class="{active:tag==\'流行\'}" @click="tag=\'流行\'">流行</span> 21 <span class="types" :class="{active:tag==\'说唱\'}" @click="tag=\'说唱\'">说唱</span> 22 <span class="types" :class="{active:tag==\'摇滚\'}" @click="tag=\'摇滚\'">摇滚</span> 23 <span class="types" :class="{active:tag==\'民谣\'}" @click="tag=\'民谣\'">民谣</span> 24 <span class="types" :class="{active:tag==\'电子\'}" @click="tag=\'电子\'">电子</span> 25 <span class="types" :class="{active:tag==\'轻音乐\'}" @click="tag=\'轻音乐\'">轻音乐</span> 26 <span class="types" :class="{active:tag==\'影视原声\'}" @click="tag=\'影视原声\'">影视原声</span> 27 <span class="types" :class="{active:tag==\'ACG\'}" @click="tag=\'ACG\'">ACG</span> 28 <span class="types" :class="{active:tag==\'怀旧\'}" @click="tag=\'怀旧\'">怀旧</span> 29 <span class="types" :class="{active:tag==\'治愈\'}" @click="tag=\'治愈\'">治愈</span> 30 <span class="types" :class="{active:tag==\'旅行\'}" @click="tag=\'旅行\'">旅行</span> 31 </div> 32 <div class="items"> 33 <div class="item" v-for="(item,index) in list" :key="index" @click="toPlayList(item.id)"> 34 <div class="img_wrap"> 35 <span class="copywriter">播放量:{{item.playCount}}</span> 36 <img :src="item.coverImgUrl" alt=""> 37 <span class="el-icon-video-play play-but" ></span> 38 </div> 39 <div class="info_wrap"> 40 <p>{{item.name}}</p> 41 </div> 42 </div> 43 </div> 44 </div> 45 <div class="page_card"> 46 <el-pagination 47 @current-change="currentPage" 48 layout="prev, pager, next" 49 :current-page="page" 50 :page-size="15" 51 :total="total"> 52 </el-pagination> 53 </div> 54 55 </div> 56 </template> 57 58 <script> 59 //导入axios 60 import axios from \'axios\'; 61 export default { 62 name: "recommendList", 63 data(){ 64 return{ 65 topList:{}, 66 list:[], 67 tag:\'全部\', 68 //分页的总记录数 69 total:0, 70 //当前页数 71 page:1, 72 } 73 74 }, 75 //侦听器 76 watch:{ 77 tag(){ 78 if(this.tag!=\'旅行\')//接口中没有旅行 79 this.topData(); 80 this.page=1; 81 this.listData(); 82 } 83 }, 84 created(){ 85 this.topData(); 86 this.listData(); 87 88 }, 89 methods:{ 90 topData(){ 91 axios({ 92 url:\'https://autumnfish.cn/top/playlist/highquality\', 93 method:\'get\', 94 params:{ 95 limit:1, 96 cat:this.tag, 97 } 98 }).then(response=>{ 99 //console.log(response); 100 this.topList=response.data.playlists[0]; 101 }) 102 }, 103 listData(){ 104 axios({ 105 url:\'https://autumnfish.cn/top/playlist\', 106 method:\'get\', 107 params:{ 108 limit: 15, 109 offset:(this.page-1)*15, 110 cat: this.tag, 111 } 112 }).then(response=>{ 113 //console.log(response); 114 this.total=response.data.total; 115 this.list=response.data.playlists; 116 }) 117 }, 118 currentPage(tpage){ 119 //console.log("当前页"+tpage); 120 this.page=tpage; 121 this.listData(); 122 }, 123 toPlayList(id){ 124 this.$router.push(`/PlayList?id=${id}`); 125 }, 126 127 }, 128 129 } 130 </script> 131 132 <style scoped> 133 img{ 134 width: 100%; 135 height: 100%; 136 border-radius: 5px; 137 } 138 .top_card{ 139 display: flex; 140 position:relative; 141 width: 90%; 142 height: 200px; 143 left: 5%; 144 top:5px; 145 overflow: hidden; 146 padding: 10px; 147 border-radius: 5px; 148 } 149 .top_card .img_wrap{ 150 position: relative; 151 width: 180px; 152 height: 180px; 153 z-index: 1; 154 flex:0 0 auto; 155 margin-right: 20px; 156 } 157 .top_card .info_wrap{ 158 z-index: 1; 159 width: 80%; 160 height: 200px; 161 flex:0 0 auto; 162 } 163 .top_card .info_wrap .tag{ 164 position: relative; 165 width: 100px; 166 height: 30px; 167 border: 1px solid orange; 168 text-align: center; 169 color: orange; 170 line-height: 30px; 171 } 172 .top_card .info_wrap .title{ 173 position: relative; 174 width: 100%; 175 top:5px; 176 color: white; 177 } 178 .top_card .info_wrap .content{ 179 position: relative; 180 color: grey; 181 width: 100%; 182 top:10px; 183 text-overflow: ellipsis; 184 /*多行文字溢出效果*/ 185 display: -webkit-box; 186 -webkit-box-orient: vertical; 187 -webkit-line-clamp:6; 188 } 189 .top_card .bgimg{ 190 position: absolute; 191 top: 0; 192 left: 0; 193 right: 0; 194 bottom: 0; 195 z-index: 0; 196 width: 100%; 197 filter: blur(20px); 198 } 199 .top_card .bg_mask{ 200 position: absolute; 201 width: 100%; 202 left: 0; 203 top: 0; 204 right: 0; 205 bottom: 0; 206 background-color: rgba(0,0,0,0.5); 207 } 208 .main_card{ 209 position: relative; 210 width: 90%; 211 padding-top: 30px; 212 left: 5%; 213 } 214 .main_card .tab_bar{ 215 display: flex; 216 justify-content: flex-end; 217 } 218 .tab_bar .types{ 219 color: grey; 220 font-size: 16px; 221 margin-right: 20px; 222 cursor:pointer; 223 } 224 .tab_bar .types.active{ 225 color: crimson; 226 } 227 .items .img_wrap{ 228 position: relative; 229 width: 200px; 230 height: 200px; 231 } 232 .items{ 233 display: flex; 234 flex-wrap:wrap; 235 } 236 .item{ 237 position: relative; 238 width: 210px; 239 margin: 10px; 240 overflow: hidden; 241 cursor: pointer; 242 } 243 .copywriter{ 244 position: absolute; 245 width: 200px; 246 top:-50px; 247 } 248 .img_wrap:hover .copywriter{ 249 position: absolute; 250 width: 200px; 251 top:0; 252 color: white; 253 background-color: black; 254 opacity: 0.8; 255 } 256 .play-but{ 257 display: block; 258 position: absolute; 259 width: 50px; 260 height: 50px; 261 bottom: -200px; 262 right: 0; 263 font-size:50px; 264 color: sandybrown; 265 } 266 .img_wrap:hover .play-but{ 267 display: block; 268 position: absolute; 269 width: 50px; 270 height: 50px; 271 bottom: 0; 272 right: 0; 273 font-size:50px; 274 color: sandybrown; 275 cursor:pointer; 276 } 277 .page_card{ 278 position: relative; 279 width: 90%; 280 left: 5%; 281 text-align: center; 282 } 283 284 285 </style>
newMusic.vue(最新音乐)
1 <template> 2 <div> 3 <div class="tab_bar"> 4 <span class="types" :class="{active:tag==\'0\'}" @click="tag=\'0\'">全部</span> 5 <span class="types" :class="{active:tag==\'7\'}" @click="tag=\'7\'">华语</span> 6 <span class="types" :class="{active:tag==\'96\'}" @click="tag=\'96\'">欧美</span> 7 <span class="types" :class="{active:tag==\'8\'}" @click="tag=\'8\'">日本</span> 8 <span class="types" :class="{active:tag==\'16\'}" @click="tag=\'16\'">韩国</span> 9 </div> 10 <div class="table_card"> 11 <table class="el-table"> 12 <thead> 13 <th width="5%"></th> 14 <th width="15%"></th> 15 <th width="20%">音乐标题</th> 16 <th width="20%">歌手</th> 17 <th width="30%">专辑</th> 18 <th width="10%">时长</th> 19 </thead> 20 <tbody> 21 <tr v-for="(item,index) in tableData" :key="index"> 22 <td>{{index+1}}</td> 23 <td class="img_column"> 24 <img :src="item.album.picUrl" alt=""> 25 <span class="el-icon-video-play play-but" @click="playMusic(item.id)"></span> 26 </td> 27 <td>{{item.name}}</td> 28 <td>{{item.artists[0].name}}</td> 29 <td>{{item.album.name}}</td> 30 <td>{{item.duration}}</td> 31 </tr> 32 </tbody> 33 </table> 34 </div> 35 36 </div> 37 </template> 38 39 <script> 40 //导入axios 41 import axios from \'axios\'; 42 export default { 43 name: "newMusic", 44 data(){ 45 return{ 46 tag:\'0\', 47 tableData:[], 48 } 49 }, 50 watch:{ 51 tag(){ 52 this.getTable(); 53 } 54 55 }, 56 created() { 57 this.getTable(); 58 59 }, 60 methods:{ 61 getTable(){ 62 axios({ 63 url:\'https://autumnfish.cn/top/song\', 64 method:\'get\', 65 params:{ 66 type:this.tag, 67 } 68 }).then(response=>{ 69 // console.log(response); 70 this.tableData=response.data.data; 71 for(let i=0;i<this.tableData.length;i++) 72 { //将获取到的毫秒转化为分+秒 73 let duration=this.tableData[i].duration; 74 let min=parseInt(duration/1000/60); 75 let second=parseInt(duration/1000%60); 76 if(min<10) 77 min=\'0\'+min; 78 if(second<10) 79 second=\'0\'+second; 80 this.tableData[i].duration=`${min}:${second}`; 81 } 82 }) 83 }, 84 playMusic(id){ 85 axios({ 86 url:\'https://autumnfish.cn/song/url\', 87 method:\'get\', 88 params:{ 89 id:id, 90 } 91 }).then(response=>{ 92 //console.log(response); 93 this.$parent.musicUrl=response.data.data[0].url; 94 }) 95 96 } 97 }, 98 } 99 </script> 100 101 <style scoped> 102 img{ 103 width: 100%; 104 height: 100%; 105 } 106 .tab_bar{ 107 position: relative; 108 width: 90%; 109 height: 30px; 110 left: 5%; 111 top:10px; 112 display: flex; 113 justify-content: flex-end; 114 } 115 .tab_bar .types{ 116 color: grey; 117 font-size: 16px; 118 margin-right: 20px; 119 cursor:pointer; 120 } 121 .tab_bar .types.active{ 122 color: crimson; 123 } 124 .table_card{ 125 position: relative; 126 width: 90%; 127 left: 5%; 128 } 129 .img_column>img{ 130 width: 100px; 131 height: 100px; 132 } 133 .play-but{ 134 display: block; 135 position: absolute; 136 width: 50px; 137 height: 50px; 138 left: 30px; 139 top:40px; 140 font-size:40px; 141 color: sandybrown; 142 cursor: pointer; 143 } 144 .el-table td, .el-table th{ 145 padding: 12px 10px; 146 } 147 148 </style>
newMv.vue(最新MV)
1 <template> 2 <div> 3 <div class="type_card"> 4 <div class="atype"> 5 <span class="atitle">地区:</span> 6 <span class="types" :class="{active:area==\'全部\'}" @click="area=\'全部\'">全部</span> 7 <span class="types" :class="{active:area==\'内地\'}" @click="area=\'内地\'">内地</span> 8 <span class="types" :class="{active:area==\'港台\'}" @click="area=\'港台\'">港台</span> 9 <span class="types" :class="{active:area==\'欧美\'}" @click="area=\'欧美\'">欧美</span> 10 <span class="types" :class="{active:area==\'日本\'}" @click="area=\'日本\'">日本</span> 11 <span class="types" :class="{active:area==\'韩国\'}" @click="area=\'韩国\'">韩国</span> 12 </div> 13 <div class="atype"> 14 <span class="atitle">类型:</span> 15 <span class="types" :class="{active:type==\'全部\'}" @click="type=\'全部\'">全部</span> 16 <span class="types" :class="{active:type==\'官方版\'}" @click="type=\'官方版\'">官方版</span> 17 <span class="types" :class="{active:type==\'原声\'}" @click="type=\'原声\'">原声</span> 18 <span class="types" :class="{active:type==\'现场版\'}" @click="type=\'现场版\'">现场版</span> 19 <span class="types" :class="{active:type==\'网易出品\'}" @click="type=\'网易出品\'">网易出品</span> 20 </div> 21 <div class="atype"> 22 <span class="atitle">排序:</span> 23 <span class="types" :class="{active:order==\'排序上升\'}" @click="order=\'排序上升\'">排序上升</span> 24 <span class="types" :class="{active:order==\'最热\'}" @click="order=\'最热\'">最热</span> 25 <span class="types" :class="{active:order==\'最新\'}" @click="order=\'最新\'">最新</span> 26 </div> 27 </div> 28 <div class="newMv"> 29 <div class="items"> 30 <div class="item" v-for="(item,index) in mvList" :key="index"> 31 <div class="img_wrap" @click="toMv(item.id)"> 32 <img :src="item.cover" alt=""> 33 <span class="play_count"><i class="el-icon-caret-right"></i>{{item.playCount}}</span> 34 </div> 35 <div class="info_wrap"> 36 <p>{{item.name}}</p> 37 <p class="song_artist">{{item.artistName}}</p> 38 </div> 39 </div> 40 </div> 41 </div> 42 <div class="page_card"> 43 <el-pagination 44 @current-change="currentPage" 45 layout="prev, pager, next" 46 :current-page="page" 47 :page-size="16" 48 :total="total"> 49 </el-pagination> 50 </div> 51 52 </div> 53 </template> 54 55 <script> 56 import axios from \'axios\' 57 export default { 58 name: "newMv", 59 data(){ 60 return{ 61 mvList:[], 62 area:\'\', 63 type:\'\', 64 order:\'\', 65 page:1, 66 total:0, 67 } 68 }, 69 methods:{ 70 getMv(){ 71 axios({ 72 url:\'https://autumnfish.cn/mv/all\', 73 method:\'get\', 74 params:{ 75 area:this.area, 76 type:this.type, 77 order:this.order, 78 limit:16, 79 offset:(this.page-1)*16, 80 }, 81 }).then(response=>{ 82 //console.log(response); 83 //接口问题,只有第一页有数据总数 84 if(this.page==1) 85 this.total=response.data.count; 86 this.mvList=response.data.data; 87 }) 88 }, 89 currentPage(tPage){ 90 this.page=tPage; 91 this.getMv(); 92 }, 93 toMv(id){ 94 //传参数到MV 95 this.$router.push(`/MV?id=${id}`); 96 }, 97 98 }, 99 created() { 100 this.getMv(); 101 }, 102 watch:{ 103 area(){ 104 this.page=1; 105 this.getMv(); 106 }, 107 type(){ 108 this.page=1; 109 this.getMv(); 110 }, 111 order(){ 112 this.page=1; 113 this.getMv(); 114 } 115 } 116 } 117 </script> 118 119 <style scoped> 120 img{ 121 width: 100%; 122 height: 100%; 123 border-radius: 5px; 124 } 125 .type_card{ 126 position: relative; 127 width: 90%; 128 left: 5%; 129 top:10px; 130 } 131 .atitle{ 132 width: 8%; 133 } 134 .atype{ 135 position: relative; 136 display: flex; 137 height: 30px; 138 margin: 15px; 139 } 140 .types{ 141 color: grey; 142 font-size: 16px; 143 margin-right: 80px; 144 cursor:pointer; 145 } 146 .types.active{ 147 color: crimson; 148 } 149 .newMv{ 150 position: relative; 151 width:90%; 152 left: 5%; 153 } 154 .newMv .items{ 155 display: flex; 156 flex-wrap: wrap; 157 } 158 .newMv .item{ 159 width: 22%; 160 margin: 10px; 161 } 162 .newMv .img_wrap{ 163 position: relative; 164 width: 250px; 165 height: 150px; 166 cursor: pointer; 167 } 168 .newMv .info_wrap{ 169 text-align: center; 170 } 171 .newMv .play_count{ 172 background-image: linear-gradient(to right,rgba(255,0,0,0),rgba(58,58,10,1)); 173 position: absolute; 174 right: 0; 175 color: white; 176 } 177 .song_artist{ 178 color: grey; 179 font-size:15px; 180 margin-top: 5px; 181 } 182 .page_card{ 183 width: 90%; 184 position: relative; 185 left: 5%; 186 text-align: center; 187 } 188 189 </style>
result.vue(搜索结果)
1 <template> 2 <div> 3 <div class="title_wrap"> 4 <h2>{{this.$route.query.keyword}}</h2> 5 <span>找到{{this.count}}个结果</span> 6 </div> 7 <div class="tab_wrap"> 8 <el-tabs v-model="activeName"> 9 <el-tab-pane label="歌曲" name="songs"> 10 <table class="el-table"> 11 <thead> 12 <th width="5%"></th> 13 <th width="30%">音乐标题</th> 14 <th width="20%">歌手</th> 15 <th width="35%">专辑</th> 16 <th width="10%">时长</th> 17 </thead> 18 <tbody> 19 <tr v-for="(item,index) in songList" :key="index" class="el-table--enable-row-hover" 20 @dblclick="playMusic(item.id)"> 21 <td>{{index+1}}</td> 22 <td> 23 <div> 24 <span>{{item.name}}</span> 25 <span class="el-icon-video-play" v-if="item.mvid!=0"></span> 26 </div> 27 <div> 28 <p v-if="item.alias.length!=0">{{item.alias[0]}}</p> 29 </div> 30 </td> 31 <td><span>{{item.artists[0].name}}</span></td> 32 <td><span>{{item.album.name}}</span></td> 33 <td><span>{{item.duration}}</span></td> 34 </tr> 35 </tbody> 36 </table> 37 </el-tab-pane> 38 <el-tab-pane label="歌单" name="playLists" class="playLists"> 39 <div class="items"> 40 <div class="item" v-for="(item,index) in playList" :key="index" @click="toPlayList(item.id)"> 41 <div class="img_wrap"> 42 <span class="copywriter">播放量:{{item.playCount}}</span> 43 <img :src="item.coverImgUrl" alt=""> 44 <span class="el-icon-video-play play-but" ></span> 45 </div> 46 <div class="info_wrap"> 47 <p>{{item.name}}</p> 48 </div> 49 </div> 50 </div> 51 </el-tab-pane> 52 <el-tab-pane label="MV" name="mvs" class="mvs"> 53 <div class="items"> 54 <div class="item" v-for="(item,index) in mvs" :key="index"> 55 <div class="img_wrap" @click="toMv(item.id)"> 56 <img :src="item.cover" alt=""> 57 <span class="play_count"><i class="el-icon-caret-right"></i>{{item.playCount}}</span> 58 </div> 59 <div class="info_wrap"> 60 <p>{{item.name}}</p> 61 <p class="song_artist">{{item.artistName}}</p> 62 </div> 63 </div> 64 </div> 65 </el-tab-pane> 66 </el-tabs> 67 </div> 68 <div class="page_card"> 69 <el-pagination 70 @current-change="currentPage" 71 layout="prev, pager, next" 72 :current-page="page" 73 :page-size="20" 74 :total="total"> 75 </el-pagination> 76 </div> 77 </div> 78 </template> 79 80 <script> 81 import axios from \'axios\' 82 export default { 83 name: "result", 84 data(){ 85 return{ 86 //标签名 87 activeName:\'songs\', 88 songList:[], 89 count:0, 90 playList:[], 91 mvs:[], 92 page:1, 93 total:0, 94 } 95 }, 96 watch:{ 97 activeName(){ 98 if(this.activeName==\'songs\') 99 { 100 this.getSongs(); 101 this.page=1; 102 } 103 if(this.activeName==\'playLists\') 104 { 105 this.getLists(); 106 this.page=1; 107 } 108 if(this.activeName==\'mvs\') 109 { 110 this.getMvs(); 111 this.page=1; 112 } 113 }, 114 /*因为vue的路由传参传到当前的页面是不会刷新的,所以需要监听路由的变化, 115 变化后再执行一次获取各数据的函数即可。 116 */ 117 $route(){ 118 if(this.activeName==\'songs\') 119 { 120 this.getSongs(); 121 this.page=1; 122 } 123 if(this.activeName==\'playLists\') 124 { 125 this.getLists(); 126 this.page=1; 127 } 128 if(this.activeName==\'mvs\') 129 { 130 this.getMvs(); 131 this.page=1; 132 } 133 134 }, 135 }, 136 //生命周期钩子 created 137 //回调函数,添加之后自动执行 138 //内部可以通过this访问vue实例 139 created() { 140 console.log(this.$route); 141 if(this.activeName==\'songs\') 142 { 143 this.getSongs(); 144 this.page=1; 145 } 146 if(this.activeName==\'playLists\') 147 { 148 this.getLists(); 149 this.page=1; 150 } 151 if(this.activeName==\'mvs\') 152 { 153 this.getMvs(); 154 this.page=1; 155 } 156 157 }, 158 methods:{ 159 getSongs(){ 160 axios({ 161 url:\'https://autumnfish.cn/search\', 162 method:\'get\', 163 params:{ 164 keywords:this.$route.query.keyword, 165 limit:20, 166 offset:(this.page-1)*20, 167 type:1, 168 } 169 }).then(response=>{ 170 //console.log(response); 171 this.songList=response.data.result.songs; 172 this.count=response.data.result.songCount; 173 this.total=this.count; 174 for(let i=0;i<this.songList.length;i++) 175 { //将获取到的毫秒转化为分+秒 176 let duration=this.songList[i].duration; 177 let min=parseInt(duration/1000/60); 178 let second=parseInt(duration/1000%60); 179 if(min<10) 180 min=\'0\'+min; 181 if(second<10) 182 second=\'0\'+second; 183 this.songList[i].duration=`${min}:${second}`; 184 } 185 }) 186 }, 187 getLists(){ 188 axios({ 189 url:\'https://autumnfish.cn/search\', 190 method:\'get\', 191 params:{ 192 keywords:this.$route.query.keyword, 193 limit:20, 194 offset:(this.page-1)*20, 195 type:1000, 196 } 197 }).then(response=>{ 198 //console.log(response); 199 this.playList=response.data.result.playlists; 200 this.count=response.data.result.playlistCount; 201 this.total=this.count; 202 203 }) 204 }, 205 getMvs(){ 206 axios({ 207 url:\'https://autumnfish.cn/search\', 208 method:\'get\', 209 params:{ 210 keywords:this.$route.query.keyword, 211 limit:20, 212 offset:(this.page-1)*20, 213 type:1004, 214 } 215 }).then(response=>{ 216 //console.log(response); 217 this.mvs=response.data.result.mvs; 218 this.count=response.data.result.mvCount; 219 this.total=this.count; 220 }) 221 }, 222 223 playMusic(id){ 224 axios({ 225 url:\'https://autumnfish.cn/song/url\', 226 method:\'get\', 227 params:{ 228 id:id, 229 } 230 }).then(response=>{ 231 //console.log(response); 232 this.$parent.musicUrl=response.data.data[0].url; 233 }) 234 }, 235 currentPage(tPage){ 236 this.page=tPage; 237 if(this.activeName==\'songs\') 238 { 239 this.getSongs(); 240 } 241 if(this.activeName==\'playLists\') 242 { 243 this.getLists(); 244 } 245 if(this.activeName==\'mvs\') 246 { 247 this.getMvs(); 248 } 249 }, 250 toPlayList(id){ 251 //传参数到PlayList 252 this.$router.push(`/PlayList?id=${id}`); 253 }, 254 toMv(id){ 255 //传参数到MV 256 this.$router.push(`/MV?id=${id}`); 257 }, 258 }, 259 } 260 </script> 261 262 <style scoped> 263 img{ 264 width: 100%; 265 height: 100%; 266 border-radius: 5px; 267 } 268 .title_wrap{ 269 position: relative; 270 width: 90%; 271 left: 5%; 272 top:5px; 273 } 274 .tab_wrap{ 275 position: relative; 276 width: 90%; 277 left: 5%; 278 } 279 .items .img_wrap{ 280 position: relative; 281 width: 200px; 282 height: 200px; 283 } 284 .playLists .items{ 285 display: flex; 286 flex-wrap:wrap; 287 } 288 .playLists .item{ 289 position: relative; 290 width: 210px; 291 margin: 10px; 292 overflow: hidden; 293 cursor: pointer; 294 } 295 .playLists .copywriter{ 296 position: absolute; 297 width: 200px; 298 top:-50px; 299 } 300 .playLists .img_wrap:hover .copywriter{ 301 position: absolute; 302 width: 200px; 303 top:0; 304 color: white; 305 background-color: black; 306 opacity: 0.8; 307 } 308 .playLists .play-but{ 309 display: block; 310 position: absolute; 311 width: 50px; 312 height: 50px; 313 bottom: -200px; 314 right: 0; 315 font-size:50px; 316 color: sandybrown; 317 } 318 .playLists .img_wrap:hover .play-but{ 319 display: block; 320 position: absolute; 321 width: 50px; 322 height: 50px; 323 bottom: 0; 324 right: 0; 325 font-size:50px; 326 color: sandybrown; 327 cursor:pointer; 328 } 329 .mvs .items{ 330 display: flex; 331 flex-wrap: wrap; 332 } 333 .mvs .item{ 334 width: 22%; 335 margin: 10px; 336 337 } 338 .mvs .img_wrap{ 339 position: relative; 340 width: 250px; 341 height: 150px; 342 cursor: pointer; 343 } 344 .mvs .info_wrap{ 345 text-align: center; 346 } 347 .mvs .play_count{ 348 background-image: linear-gradient(to right,rgba(255,0,0,0),rgba(58,58,10,1)); 349 position: absolute; 350 right: 0; 351 color: white; 352 } 353 .song_artist{ 354 color: grey; 355 font-size:15px; 356 margin-top: 5px; 357 } 358 .page_card{ 359 width: 90%; 360 position: relative; 361 left: 5%; 362 text-align: center; 363 } 364 365 366 367 </style>
MV.vue(MV详情页面)
1 <template> 2 <div> 3 <div class="main_wrap"> 4 <div class="mv_info"> 5 <h3>MV详情</h3> 6 <div class="video"> 7 <video :src="mvUrl" controls="controls"></video> 8 </div> 9 <div class="artist"> 10 <img :src="imgUrl" alt=""> 11 <span>{{mvInfo.artistName}}</span> 12 </div> 13 <div class="mv_name"> 14 <span>{{mvInfo.name}}</span> 15 </div> 16 <div class="mv_time"> 17 <span>发布:{{mvInfo.publishTime}}</span> 18 <span>播放:{{mvInfo.playCount}}次</span> 19 </div> 20 <div class="description"> 21 <span>{{mvInfo.desc}}</span> 22 </div> 23 24 </div> 25 <div class="mv_about"> 26 <h3>相关推荐</h3> 27 <div class="item" v-for="(item,index) in mvs" :key="index"> 28 <div class="img_wrap"> 29 <img :src="item.cover" alt="" @click="toMv(item.id)"> 30 <span class="play_count"><i class="el-icon-caret-right"></i>{{item.playCount}}</span> 31 </div> 32 <div class="info_wrap"> 33 <p>{{item.name}}</p> 34 <p class="song_artist">{{item.artistName}}</p> 35 </div> 36 </div> 37 </div> 38 39 </div> 40 <div class="comments"> 41 <div class="comment_wrap" v-if="hotCommentCount!=0"> 42 <h3>热门评论({{hotCommentCount}})</h3> 43 <div class="item" v-for="(item,index) in hotcomment" :key="index"> 44 <div class="icon_wrap"> 45 <img :src="item.user.avatarUrl" alt=""> 46 </div> 47 <div class="info_wrap"> 48 <div class="user_id"> 49 <span class="text_blue">{{item.user.nickname}}:</span> 50 {{item.content}} 51 </div> 52 <div class="re_content" v-if="item.beReplied.length!=0"> 53 <span class="text_blue">{{item.beReplied[0].user.nickname}}:</span> 54 {{item.beReplied[0].content}} 55 </div> 56 <div class="user_time"> 57 <span>{{item.time}}</span> 58 </div> 59 </div> 60 </div> 61 </div> 62 <div class="comment_wrap"> 63 <h3>最新评论({{commentCount}})</h3> 64 <div class="item" v-for="(item,index) in comment" :key="index"> 65 <div class="icon_wrap"> 66 <img :src="item.user.avatarUrl" alt=""> 67 </div> 68 <div class="info_wrap"> 69 <div class="user_id"> 70 <span class="text_blue">{{item.user.nickname}}:</span> 71 {{item.content}} 72 </div> 73 <div class="re_content" v-if="item.beReplied.length!=0"> 74 <span class="text_blue">{{item.beReplied[0].user.nickname}}:</span> 75 {{item.beReplied[0].content}} 76 </div> 77 <div class="user_time"> 78 <span>{{item.time}}</span> 79 </div> 80 </div> 81 </div> 82 </div> 83 <div class="page_card"> 84 <el-pagination 85 @current-change="currentPage" 86 layout="prev, pager, next" 87 :current-page="page" 88 :page-size="20" 89 :total="commentCount"> 90 </el-pagination> 91 </div> 92 93 </div> 94 </div> 95 </template> 96 97 <script> 98 import axios from \'axios\'; 99 export default { 100 name: "MV", 101 data(){ 102 return{ 103 mvUrl:\'\', 104 mvInfo:[], 105 imgUrl:\'\', 106 mvs:[], 107 hotcomment:[], 108 hotCommentCount:0, 109 comment:[], 110 commentCount:0, 111 page:1, 112 } 113 }, 114 methods:{ 115 playMv(){ 116 axios({ 117 url:\'https://autumnfish.cn/mv/url\', 118 method:\'get\', 119 params:{ 120 id:this.$route.query.id, 121 } 122 }).then(response=>{ 123 //console.log(response); 124 this.mvUrl=response.data.data.url; 125 }) 126 }, 127 getMvAbout(){ 128 axios({ 129 url:\'https://autumnfish.cn/simi/mv\', 130 method:\'get\', 131 params:{ 132 mvid:this.$route.query.id, 133 } 134 }).then(response=>{ 135 //console.log(response); 136 this.mvs=response.data.mvs; 137 }) 138 }, 139 getMvInfo(){ 140 axios({ 141 url:\'https://autumnfish.cn/mv/detail\', 142 method:\'get\', 143 params:{ 144 mvid:this.$route.query.id, 145 } 146 }).then(response=>{ 147 //console.log(response); 148 this.mvInfo=response.data.data; 149 150 axios({ 151 url:\'https://autumnfish.cn/artists\', 152 method:\'get\', 153 params:{ 154 id:this.mvInfo.artistId, 155 } 156 }).then(res=>{ 157 // console.log(res); 158 this.imgUrl=res.data.artist.picUrl; 159 160 }) 161 }) 162 }, 163 getMvComment(){ 164 axios({ 165 url:\'https://autumnfish.cn/comment/mv\', 166 method:\'get\', 167 params:{ 168 id:this.$route.query.id, 169 limit:20, 170 offset:(this.page-1)*20, 171 } 172 }).then(response=>{ 173 //console.log(response); 174 this.comment=response.data.comments; 175 this.commentCount=response.data.total; 176 this.hotcomment=response.data.hotComments; 177 if(response.data.hotComments) 178 this.hotCommentCount=response.data.hotComments.length; 179 else 180 this.hotCommentCount=0; 181 }) 182 }, 183 currentPage(tPage){ 184 this.page=tPage; 185 this.getMvComment(); 186 }, 187 toMv(id){ 188 //传参数到MV 189 this.$router.push(`/MV?id=${id}`); 190 }, 191 }, 192 created() { 193 this.playMv(); 194 this.getMvInfo(); 195 this.getMvAbout(); 196 this.getMvComment(); 197 }, 198 watch:{ 199 $route(){ 200 this.page=1; 201 this.playMv(); 202 this.getMvInfo(); 203 this.getMvAbout(); 204 this.getMvComment(); 205 } 206 }, 207 } 208 </script> 209 210 <style scoped> 211 img{ 212 width: 100%; 213 height: 100%; 214 border-radius: 5px; 215 } 216 .main_wrap{ 217 position: relative; 218 display: flex; 219 width: 90%; 220 left: 5%; 221 top:5px; 222 } 223 .main_wrap .mv_info{ 224 flex:6.5; 225 } 226 .main_wrap .mv_info .video{ 227 width: 93%; 228 height: 400px; 229 margin:10px 0; 230 } 231 .main_wrap .mv_info .video>video{ 232 width: 100%; 233 height: 100%; 234 outline: none; 235 } 236 .main_wrap .mv_info .artist{ 237 width: 100%; 238 margin:10px 0; 239 display: flex; 240 align-items: center; 241 font-size: 18px; 242 } 243 .main_wrap .mv_info .artist>img{ 244 width: 70px; 245 height: 70px; 246 border-radius: 50%; 247 margin-right: 10px; 248 } 249 .main_wrap .mv_info .mv_name{ 250 width: 100%; 251 font-size: 25px; 252 font-weight: bold; 253 margin:10px 0; 254 } 255 256 .main_wrap .mv_info .mv_time{ 257 width: 100%; 258 font-size: 14px; 259 color: grey; 260 margin:10px 0; 261 } 262 .main_wrap .mv_info .description{ 263 margin:10px 0; 264 } 265 .main_wrap .mv_info .mv_time>span{ 266 margin-right: 30px; 267 } 268 .main_wrap .mv_about{ 269 position: relative; 270 flex:3.5; 271 } 272 .main_wrap .mv_about .item{ 273 width: 100%; 274 display: flex; 275 margin-top: 10px; 276 align-items: center; 277 } 278 .main_wrap .mv_about .img_wrap{ 279 width: 200px; 280 height: 110px; 281 position: relative; 282 cursor: pointer; 283 } 284 .main_wrap .mv_about .info_wrap{ 285 flex: 1; 286 margin-left: 6px; 287 } 288 .main_wrap .mv_about .song_artist{ 289 margin-top: 10px; 290 color: grey; 291 font-size: 15px; 292 } 293 .main_wrap .mv_about .play_count{ 294 background-image: linear-gradient(to right,rgba(255,0,0,0),rgba(58,58,10,1)); 295 position: absolute; 296 right: 0; 297 color: white; 298 } 299 300 .comments{ 301 position: relative; 302 width: 90%; 303 left: 5%; 304 margin-top: 20px; 305 } 306 .comments .comment_wrap{ 307 width: 100%; 308 } 309 .comments .comment_wrap .item{ 310 width: 100%; 311 padding: 5px; 312 margin: 15px; 313 display: flex; 314 border-bottom: 1px solid grey; 315 } 316 .comments .comment_wrap .icon_wrap{ 317 width: 50px; 318 height: 50px; 319 border-radius: 50%; 320 background: white; 321 } 322 .comments .comment_wrap .icon_wrap>img{ 323 border-radius: 50%; 324 } 325 .comments .comment_wrap .info_wrap{ 326 flex:1; 327 margin-left: 10px; 328 } 329 .comments .comment_wrap .info_wrap .user_id{ 330 position: relative; 331 } 332 .text_blue{ 333 color: #517EAF; 334 } 335 .comments .comment_wrap .info_wrap .re_content{ 336 position: relative; 337 background:#E6E5E6; 338 border-radius: 5px; 339 line-height: 30px; 340 } 341 .comments .comment_wrap .info_wrap .user_time{ 342 position: relative; 343 top:5px; 344 color: grey; 345 font-size: 14px; 346 height: 30px; 347 } 348 .page_card{ 349 width: 90%; 350 position: relative; 351 left: 5%; 352 text-align: center; 353 } 354 355 </style>
PlayList.vue(歌单详情页)
1 <template> 2 <div> 3 <div class="top_wrap"> 4 <div class="img_wrap"> 5 <img :src="playList.coverImgUrl"> 6 </div> 7 <div class="info_wrap"> 8 <div class="description"> 9 <span>{{playList.name}}</span> 10 </div> 11 <div class="description user"> 12 <img :src="playList.creator.avatarUrl" class="user_icon"> 13 <span>{{playList.creator.nickname}}</span> 14 <span class="Date">{{playList.createTime}}创建</span> 15 </div> 16 <div class="description"> 17 <span class="title">标签:</span> 18 <span>{{playList.tags[0]}}/{{playList.tags[1]}}/{{playList.tags[2]}}</span> 19 </div> 20 <div class="description text_overflow"> 21 <span class="title">简介:</span> 22 <span>{{playList.description}}</span> 23 </div> 24 </div> 25 </div> 26 <div class="main_wrap"> 27 <el-tabs v-model="activeName"> 28 <el-tab-pane label="歌曲列表" name="songs"> 29 <table class="el-table"> 30 <thead> 31 <th width="5%"></th> 32 <th width="15%"></th> 33 <th width="25%">音乐标题</th> 34 <th width="15%">歌手</th> 35 <th width="30%">专辑</th> 36 <th width="10%">时长</th> 37 </thead> 38 <tbody> 39 <tr v-for="(item,index) in songList" :key="index"> 40 <td>{{index+1}}</td> 41 <td class="img_column"> 42 <img :src="item.al.picUrl" alt=""> 43 <span class="el-icon-video-play play-but" @click="playMusic(item.id)"></span> 44 </td> 45 <td>{{item.name}}</td> 46 <td>{{item.ar[0].name}}</td> 47 <td>{{item.al.name}}</td> 48 <td>{{item.dt}}</td> 49 </tr> 50 </tbody> 51 </table> 52 </el-tab-pane> 53 <el-tab-pane label="评论" name="comments"> 54 <div class="comment_wrap" v-if="hotCommentCount!=0"> 55 <h3>热门评论({{hotCommentCount}})</h3> 56 <div class="item" v-for="(item,index) in hotcomment" :key="index"> 57 <div class="icon_wrap"> 58 <img :src="item.user.avatarUrl" alt=""> 59 </div> 60 <div class="info_wrap"> 61 <div class="user_id"> 62 <span class="text_blue">{{item.user.nickname}}:</span> 63 {{item.content}} 64 </div> 65 <div class="re_content" v-if="item.beReplied.length!=0"> 66 <span class="text_blue">{{item.beReplied[0].user.nickname}}:</span> 67 {{item.beReplied[0].content}} 68 </div> 69 <div class="user_time"> 70 <span>{{item.time}}</span> 71 </div> 72 </div> 73 </div> 74 </div> 75 <div class="comment_wrap"> 76 <h3>最新评论({{commentCount}})</h3> 77 <div class="item" v-for="(item,index) in comment" :key="index"> 78 <div class="icon_wrap"> 79 <img :src="item.user.avatarUrl" alt=""> 80 </div> 81 <div class="info_wrap"> 82 <div class="user_id"> 83 <span class="text_blue">{{item.user.nickname}}:</span> 84 {{item.content}} 85 </div> 86 <div class="re_content" v-if="item.beReplied.length!=0"> 87 <span class="text_blue">{{item.beReplied[0].user.nickname}}:</span> 88 {{item.beReplied[0].content}} 89 </div> 90 <div class="user_time"> 91 <span>{{item.time}}</span> 92 </div> 93 </div> 94 </div> 95 </div> 96 <div class="page_card"> 97 <el-pagination 98 @current-change="currentPage" 99 layout="prev, pager, next" 100 :current-page="page" 101 :page-size="20" 102 :total="commentCount"> 103 </el-pagination> 104 </div> 105 106 </el-tab-pane> 107 </el-tabs> 108 109 </div> 110 </div> 111 </template> 112 113 <script> 114 import axios from \'axios\' 115 export default { 116 name: "PlayList", 117 data(){ 118 return{ 119 playList:{ 120 creator:{}, 121 tags:[], 122 trackIds:[], 123 }, 124 songList:[], 125 activeName:\'songs\', 126 hotcomment:[], 127 hotCommentCount:0, 128 comment:[], 129 commentCount:0, 130 page:1, 131 } 132 }, 133 methods:{ 134 getSongs(){ 135 //获取vue容器 136 var that=this; 137 axios({ 138 url:\'https://autumnfish.cn/playlist/detail\', 139 method:\'get\', 140 params:{ 141 id:this.$route.query.id, 142 }, 143 }).then(response=>{ 144 //console.log(response); 145 this.playList=response.data.playlist; 146 let app=this.playList; 147 var date=new Date(this.playList.createTime); 148 //将获取到的创建时间由毫秒转化为想要的格式 149 Date.prototype.toLocaleString=function(){ 150 //自定义时间格式 151 function addZero(num) { 152 if(num<10) 153 { 154 return \'0\'+num; 155 } 156 else 157 return num; 158 } 159 return this.getFullYear()+\'-\'+addZero((this.getMonth()+1))+\'-\'+addZero(this.getDate()); 160 }; 161 this.playList.createTime=date.toLocaleString(); 162 getSongList(0,app.trackIds.length); 163 //尝试一下递归 164 /*递归可以解决顺序问题,暂且先用递归*/ 165 function getSongList(j,length) { 166 axios({ 167 url:\'https://autumnfish.cn/song/detail\', 168 method:\'get\', 169 params:{ 170 ids:app.trackIds[j].id, 171 }, 172 }).then(response=>{ 173 that.songList.push(response.data.songs[0]); 174 //将获取到的毫秒转化为分+秒 175 let duration=that.songList[j].dt; 176 let min=parseInt(duration/1000/60); 177 let second=parseInt(duration/1000%60); 178 if(min<10) 179 min=\'0\'+min; 180 if(second<10) 181 second=\'0\'+second; 182 that.songList[j].dt=`${min}:${second}`; 183 184 if(++j<length){ 185 getSongList(j,length); 186 } 187 }) 188 } 189 //另一个思路:像播放音乐那样,给tr绑定一个函数,可以传id,用什么事件绑定是问题,然后可以用songList.值来代替 190 //item 191 //通过track的id获取歌曲的详细信息,在for循环中异步会出问题 192 /****************** 193 for(let i=0;i<this.playList.trackIds.length;i++) 194 { 195 axios({ 196 url:\'https://autumnfish.cn/song/detail\', 197 method:\'get\', 198 params:{ 199 ids:this.playList.trackIds[i].id, 200 }, 201 }).then(response=>{ 202 //this.songList[i]=response.data.songs[0]; 203 //这个会导致每次产生的结果不同,原因即为在当前循环中axios还没获得接口传过来的数据, 204 //then后面已经执行了.因此根据i赋值会错位。而用push会每次结果不同。 205 this.songList.push(response.data.songs[0]); 206 207 if(i==this.playList.trackIds.length-1) 208 { 209 for(let j=0;j<this.songList.length;j++) 210 { //将获取到的毫秒转化为分+秒 211 let duration=this.songList[j].dt; 212 let min=parseInt(duration/1000/60); 213 let second=parseInt(duration/1000%60); 214 if(min<10) 215 min=\'0\'+min; 216 if(second<10) 217 second=\'0\'+second; 218 this.songList[j].dt=`${min}:${second}`; 219 } 220 } 221 }) 222 } 223 *************************/ 224 // console.log(this.songList); 225 }) 226 }, 227 playMusic(id){ 228 axios({ 229 url:\'https://autumnfish.cn/song/url\', 230 method:\'get\', 231 params:{ 232 id:id, 233 }, 234 }).then(response=>{ 235 this.$parent.musicUrl=response.data.data[0].url; 236 }) 237 }, 238 gethotComments(){ 239 axios({ 240 url:\'https://autumnfish.cn/comment/hot\', 241 method:\'get\', 242 params:{ 243 id:this.$route.query.id, 244 type:2, 245 limit:20, 246 }, 247 }).then(response=>{ 248 //console.log(response); 249 this.hotcomment=response.data.hotComments; 250 this.hotCommentCount=response.data.total; 251 252 Date.prototype.toLocaleString=function(){ 253 //自定义时间格式 254 function addZero(num) { 255 //加0 256 if(num<10) 257 { 258 return \'0\'+num; 259 } 260 else 261 return num; 262 } 263 return this.getFullYear()+\'-\'+addZero((this.getMonth()+1))+\'-\'+ addZero(this.getDate())+\' \'+ 264 addZero(this.getHours())+\':\'+addZero(this.getMinutes()); 265 }; 266 for(let i=0;i<this.hotcomment.length;i++) 267 { 268 var date=new Date(this.hotcomment[i].time); 269 this.hotcomment[i].time=date.toLocaleString(); 270 } 271 }) 272 }, 273 getComments(){ 274 axios({ 275 url:\'https://autumnfish.cn/comment/playlist\', 276 method:\'get\', 277 params:{ 278 id:this.$route.query.id, 279 limit:20, 280 offset:(this.page-1)*20, 281 }, 282 }).then(response=>{ 283 //console.log(response); 284 this.comment=response.data.comments; 285 this.commentCount=response.data.total; 286 287 Date.prototype.toLocaleString=function(){ 288 //自定义时间格式 289 function addZero(num) { 290 //加0 291 if(num<10) 292 { 293 return \'0\'+num; 294 } 295 else 296 return num; 297 } 298 return this.getFullYear()+\'-\'+addZero((this.getMonth()+1))+\'-\'+ addZero(this.getDate())+\' \'+ 299 addZero(this.getHours())+\':\'+addZero(this.getMinutes()); 300 }; 301 for(let i=0;i<this.comment.length;i++) 302 { 303 var date=new Date(this.comment[i].time); 304 this.comment[i].time=date.toLocaleString(); 305 } 306 }) 307 }, 308 currentPage(tPage){ 309 this.page=tPage; 310 this.getComments(); 311 }, 312 }, 313 created() { 314 this.getSongs(); 315 this.gethotComments(); 316 this.getComments(); 317 } 318 } 319 </script> 320 321 <style scoped> 322 img{ 323 width:100%; 324 height: 100%; 325 border-radius: 5px; 326 } 327 .top_wrap{ 328 position: relative; 329 display: flex; 330 width: 90%; 331 left: 5%; 332 top:5px; 333 padding: 10px; 334 } 335 .main_wrap{ 336 position: relative; 337 width: 90%; 338 left: 5%; 339 top:5px; 340 padding: 10px; 341 } 342 .top_wrap .img_wrap{ 343 width: 200px; 344 height: 200px; 345 } 346 .top_wrap .info_wrap{ 347 flex: 1; 348 padding-left: 20px; 349 } 350 .top_wrap .info_wrap .description{ 351 height: 30px; 352 margin: 10px; 353 line-height: 30px; 354 } 355 .top_wrap .info_wrap .text_overflow{ 356 display: -webkit-box; 357 -webkit-box-orient: vertical; 358 -webkit-line-clamp: 3; 359 height: 90px; 360 overflow: hidden; 361 } 362 .top_wrap .info_wrap .user>span{ 363 position: relative; 364 display: inline-block; 365 margin-left: 10px; 366 bottom: 5px; 367 } 368 .top_wrap .info_wrap .user .Date{ 369 font-size: 15px; 370 color: grey; 371 } 372 .top_wrap .info_wrap .description .user_icon{ 373 display: inline-block; 374 width: 30px; 375 height: 30px; 376 border-radius: 50%; 377 } 378 .top_wrap .info_wrap .title{ 379 font-size: 18px; 380 } 381 .main_wrap .img_column>img{ 382 width: 100px; 383 height: 100px; 384 } 385 .main_wrap .play-but{ 386 display: block; 387 position: absolute; 388 width: 50px; 389 height: 50px; 390 left: 40px; 391 top:40px; 392 font-size:40px; 393 color: sandybrown; 394 cursor: pointer; 395 } 396 .el-table td, .el-table th{ 397 padding: 12px 10px; 398 } 399 .main_wrap .comment_wrap{ 400 width: 100%; 401 } 402 .main_wrap .comment_wrap .item{ 403 width: 100%; 404 padding: 5px; 405 margin: 15px; 406 display: flex; 407 border-bottom: 1px solid grey; 408 } 409 .main_wrap .comment_wrap .icon_wrap{ 410 width: 50px; 411 height: 50px; 412 border-radius: 50%; 413 background: white; 414 } 415 .main_wrap .comment_wrap .icon_wrap>img{ 416 border-radius: 50%; 417 } 418 .main_wrap .comment_wrap .info_wrap{ 419 flex:1; 420 margin-left: 10px; 421 } 422 .main_wrap .comment_wrap .info_wrap .user_id{ 423 position: relative; 424 } 425 .text_blue{ 426 color: #517EAF; 427 } 428 .main_wrap .comment_wrap .info_wrap .re_content{ 429 position: relative; 430 background:#E6E5E6; 431 border-radius: 5px; 432 line-height: 30px; 433 } 434 .main_wrap .comment_wrap .info_wrap .user_time{ 435 position: relative; 436 top:5px; 437 color: grey; 438 font-size: 14px; 439 height: 30px; 440 } 441 .page_card{ 442 width: 90%; 443 position: relative; 444 left: 5%; 445 text-align: center; 446 } 447 448 449 450 </style>
main.js
import Vue from \'vue\' import App from \'./App.vue\' Vue.config.productionTip = false //导入 import VueRouter from \'vue-router\' //use一下 Vue.use(VueRouter) //导入 需要通过路由管理的组件 import discovery from "./components/discovery"; import recommendList from "./components/recommendList"; import newMusic from "./components/newMusic"; import newMv from "./components/newMv"; import result from "./components/result"; import PlayList from "./components/PlayList"; import MV from "./components/MV"; //创建路由 let router=new VueRouter({ routes:[ //配置地址 和 组件的对应关系 { //地址 path:\'/discovery\', //组件 component:discovery, }, { path:\'\', component: discovery, }, { //地址 path:\'/recommend\', component:recommendList, }, { //地址 path: \'/newMusic\', component: newMusic, }, { //地址 path: \'/newMv\', component: newMv, }, { //地址 path: \'/result\', component: result, }, { path: \'/playlist\', component:PlayList, } , { path: \'/mv\', component: MV, } ] }) //导入element-ui import ElementUI from \'element-ui\' //导入element-ui的样式 import \'element-ui/lib/theme-chalk/index.css\'; //使用一下 Vue.use(ElementUI); new Vue({ render: h => h(App), //挂载到vue实例上 router,//router:router }).$mount(\'#app\')