我是靠谱客的博主 单薄唇膏,这篇文章主要介绍网易云音乐移动端项目实战(分解上),现在分享给大家,希望可以做个参考。

网易云音乐后台API服务搭建

步骤一
windows系统安装git

复制代码
1
2
//傻瓜式安装,可以一直next

步骤二
网易云音乐 NodeJS 版 API

下载好后,在vscode下新建一个文件夹
右击打开git bash here
在这里插入图片描述输入

复制代码
1
2
git clone https://github.com/Binaryify/NeteaseCloudMusicApi.git

github地址
在这里插入图片描述步骤三用
vscode打开下载好的文件夹
在这里插入图片描述

进入此文件夹后输入
cnpm install安装依赖
在这里插入图片描述完毕输入

复制代码
1
2
node app.js

运行完页面
在这里插入图片描述步骤四
在终端中输入命令,创建一个新项目

复制代码
1
2
vue create musicapp

在这里插入图片描述在public文件夹下创建一个js文件,js文件创建一个rem布局文件
实现自适应REM布局
在这里插入图片描述

准备工作

一、实现自适应REM布局

rem.js

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function remSize(){ //获取浏览器窗口文档显示区域的宽度,不包括滚动条。 var deviceWidth = document.documentElement.clientWidth || window.innerWidth if(deviceWidth > 750){ deviceWidth = 750 } if(deviceWidth <= 320){ deviceWidth = 320 } //设计稿是750 设置一半的宽度那么设计稿的宽度1rem等于设计稿的100像素 document.documentElement.style.fontSize = (deviceWidth / 7.5) + 'px'; document.querySelector('body').style.fontSize = 0.3 + 'rem'; } remSize() window.onresize = function(){//onreset 事件在表单被重置后触发。 remSize() }

index.html中导入rem.js

复制代码
1
2
<script src="<%= BASE_URL %>js/rem.js"></script>

index.html

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html> <html lang=""> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <link rel="icon" href="<%= BASE_URL %>favicon.ico"> <title><%= htmlWebpackPlugin.options.title %></title> </head> <body> <!-- <%= BASE_URL %>基础路径 --> <script src="<%= BASE_URL %>js/rem.js"></script> <noscript> <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong> </noscript> <div id="app"></div> <!-- built files will be auto injected --> </body> </html>

字体图标
阿里巴巴矢量图图标库
加好购物车后添加至项目,可在线获取链接
在这里插入图片描述引入到页面
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二、头部导航布局与样式

效果如下
在这里插入图片描述App.vue中添加topNav组件,设置全局字体图标样式
App.vue

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<template> <div id="nav"> <topNav/> </div> <!-- <router-view/> --> </template> <script> import topNav from '../src/components/topNav.vue' export default { components: { topNav } } </script> <style lang="less"> .icon { width: 1em; height: 1em; vertical-align: -0.15em; fill: currentColor; overflow: hidden; } *{ padding: 0; margin: 0; box-sizing: border-box; font-family: "微软雅黑"; } </style>

topNav.vue中设置topNav组件样式
topNav.vue

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<template> <div> <div class="topNav"> <div class="topleft"> <svg class="icon" aria-hidden="true"> <use xlink:href="#icon-caidan"></use> </svg> </div> <div class="topmain"> <span class="navBtn">我的</span> <span class="navBtn active">发现</span> <span class="navBtn">云村</span> <span class="navBtn">视频</span> </div> <div class="topright"> <svg class="icon" aria-hidden="true"> <use xlink:href="#icon-sousuo"></use> </svg> </div> </div> </div> </template> <style socoped lang="less"> .topNav { display: flex; width: 7.5rem; height: 1rem; justify-content: space-between; align-items: center; padding:0 0.2rem; //lang="less" .icon{ width: 0.5rem; height: 0.5rem; } .search{ width: 0.45rem; height: 0.45rem; } } .topmain{ width: 5rem; display: flex; justify-content: space-around; .active{ font-weight: 900; } } </style>

ps:

复制代码
1
2
3
4
5
/* space-between 最左、最右item贴合左侧或右侧边框,item与item之间间距相等。 space-around 每个item 左右方向的margin相等。两个item中间的间距会比较大 */

在这里插入图片描述

三、导入轮播组件

vue3- swiper组件

复制代码
1
2
npm install swiper@5.4.5
复制代码
1
2
npm install vue-awesome-swiper@4.1.1

swiperzujian.vue

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<template> <div> <div class="swiper-container"> <div class="swiper-wrapper"> <div class="swiper-slide" v-for="(item,i) in imgs" :key="i"><img :src="item" alt=""></div> </div> <!-- 如果需要分页器 --> <div class="swiper-pagination"></div> </div> </div> </template> <script> import Swiper from "swiper"; import "swiper/css/swiper.css"; import "swiper/js/swiper.min.js"; export default { data:function(){ return{ imgs:[ require('../assets/imag/adpage1.jpg'), require('../assets/imag/adpage2.jpg'), require('../assets/imag/adpage3.jpg'), ] } }, components: {}, mounted() { var mySwiper = new Swiper(".swiper-container", { loop: true, // 循环模式选项 // 如果需要分页器 pagination: { el: ".swiper-pagination" }, }); } }; </script> <style lang="less"> .swiper-container { width: 7.1rem; height: 3rem; border-radius: 0.1rem; } .swiper-slide img{ width: 100%; } .swiper-pagination-bullet-active{ background-color: rgb(179, 178, 177); } </style>
四、封装请求获取网易的banner图

安装axios

复制代码
1
2
npm install axios --save

banner

复制代码
1
2
/banner?type=2

启动搭建的服务器请求服务器地址加上接口地址
在这里插入图片描述

复制代码
1
2
http://localhost:3000/banner?type=2

swiperzujian.vue

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<template> <div> <div class="swiper-container" id="d1"> <div class="swiper-wrapper"> <div class="swiper-slide" v-for="(item,i) in imgs" :key="i"> <img :src="item.pic"> </div> </div> <!-- 如果需要分页器 --> <div class="swiper-pagination"></div> </div> </div> </template> <script> import Swiper from "swiper"; import "swiper/css/swiper.css"; import "swiper/js/swiper.min.js"; import axios from "axios"; export default { data: function() { return { imgs: [ //因为请求数据中banner是一个对象,img存放在pc属性上 { pic: require("../assets/imag/adpage1.jpg") }, { pic: require("../assets/imag/adpage2.jpg") }, { pic: require("../assets/imag/adpage3.jpg") }, { pic: require("../assets/imag/adpage3.jpg") } ] }; }, mounted() { var mySwiper = new Swiper(".swiper-container", { loop: true, // 循环模式选项 // 如果需要分页器 pagination: { el: ".swiper-pagination", clickable: true } }); async function ff() { let res = await axios.get("http://localhost:3000/banner?type=2"); return res; } ff().then(res => { this.imgs = res.data.banners; }); } }; </script> <style lang="less"> #d1.swiper-container { width: 7.1rem; height: 2.8rem; border-radius: 0.1rem; } .swiper-slide img { width: 100%; } .swiper-pagination-bullet-active { background-color: rgb(179, 178, 177); } </style>

封装一下
src下面的文件夹api下的index.js

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
import axios from 'axios'; //获取轮播图API /* 0: pc 1: android 2: iphone 3: ipad */ export async function ff(type = 2) { let res = await axios.get(`http://localhost:3000/banner?type=${type}`); return res; }
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<template> <div> <div class="swiper-container" id="d1"> <div class="swiper-wrapper"> <div class="swiper-slide" v-for="(item,i) in imgs" :key="i"> <img :src="item.pic"> </div> </div> <!-- 如果需要分页器 --> <div class="swiper-pagination"></div> </div> </div> </template> <script> import Swiper from "swiper"; import "swiper/css/swiper.css"; import "swiper/js/swiper.min.js"; import axios from "axios"; import {ff} from '../api/index.js' export default { data: function() { return { imgs: [ { pic: require("../assets/imag/adpage1.jpg") }, { pic: require("../assets/imag/adpage2.jpg") }, { pic: require("../assets/imag/adpage3.jpg") }, { pic: require("../assets/imag/adpage3.jpg") } ] }; }, mounted() { var mySwiper = new Swiper(".swiper-container", { loop: true, // 循环模式选项 // 如果需要分页器 pagination: { el: ".swiper-pagination", clickable: true } }); ff(2).then(res => { this.imgs = res.data.banners; }); } }; </script> <style lang="less"> #d1.swiper-container { width: 7.1rem; height: 2.8rem; border-radius: 0.1rem; } .swiper-slide img { width: 100%; } .swiper-pagination-bullet-active { background-color: rgb(179, 178, 177); } </style>

网易云音乐效果图

五、图标列表组件

新建一个模板名为iconList.vue加入到App.vue中
iconList.vue代码(基础布局)

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<template> <div class="iconList"> <div class="iconItem"> <svg class="icon" aria-hidden="true"> <use xlink:href="#icon-tuijian"></use> </svg> <span>每日推荐</span> </div> <div class="iconItem"> <svg class="icon" aria-hidden="true"> <use xlink:href="#icon-vipsirenzhuanxiangdingzhiyewukehu"></use> </svg> <span>私人FM</span> </div> <div class="iconItem"> <svg class="icon" aria-hidden="true"> <use xlink:href="#icon-gedan"></use> </svg> <span>歌单</span> </div> <div class="iconItem"> <svg class="icon" aria-hidden="true"> <use xlink:href="#icon-paihangbang"></use> </svg> <span>排行榜</span> </div> </div> </template> <style lang="less" scoped> .iconList{ display: flex; padding: 0.34rem; justify-content: space-between; .icon{ height: 0.8rem; width: 0.8rem; } .iconItem{ display: flex; flex-direction: column; text-align: center; align-items: center; font-size: 0.28rem; } } </style>
六、发现好歌单实现

新建模板musicList.vue并引入到App.vue
1.实现基本布局

2.设计好基本布局后做ajax请求

复制代码
1
2
3
4
5
6
getMusicList(10).then(res => { console.log(res); this.musicList = res.data.result; });

请求结果如下图
在这里插入图片描述
api中index.js

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import axios from 'axios'; //获取轮播图API /* 0: pc 1: android 2: iphone 3: ipad */ export async function ff(type = 2) { return await axios.get(`http://localhost:3000/banner?type=${type}`); } //获取推荐歌单默认十条数据 export async function getMusicList(limit = 10){ return await axios.get(`http://localhost:3000/personalized?limit=${limit}`) }

musicList.vue

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
<template> <div class="musicList"> <div class="musicList-Top"> <div class="title">发现好歌单</div> <div class="more">查看更多</div> </div> <div class="mlist"> <!-- swiper-container --> <div class="swiper-container" id="d2"> <div class="swiper-wrapper"> <div class="swiper-slide" v-for="(item,i) in musicList" :key="i"> <img :src="item.picUrl"> <div class="name">{{item.name}}</div> <div class="count"> <svg class="icon" aria-hidden="true"> <use xlink:href="#icon-whiteplayCircle"></use> </svg> <span>{{item.playCount}}</span> </div> </div> </div> </div> </div> </div> </template> <script scoped> import Swiper from "swiper"; import "swiper/css/swiper.css"; import "swiper/js/swiper.min.js"; import { getMusicList } from "@/api/index.js"; export default { data() { return { musicList: [] }; }, mounted() { var swiper = new Swiper("#d2", { slidesPerView: 3, spaceBetween: 10 }); getMusicList(10).then(res => { this.musicList = res.data.result; }); }, updated() { var swiper = new Swiper("#d2", { slidesPerView: 3, spaceBetween: 10 }); } }; </script> <style lang="less" scoped> .musicList { width: 7.5rem; padding: 0.4rem; .musicList-Top { display: flex; align-content: center; justify-content: space-between; height: 1rem; .title { font-size: 0.4rem; font-weight: 900; } .more { border: 1px solid #ccc; border-radius: 0.1rem; padding: 0.08rem; font-size: 0.24rem; text-align: center; height: 0.5rem; } } } .mlist { .swiper-container { width: 100%; height: 3rem; .swiper-slide { display: flex; flex-direction: column; position: relative; img { width: 100%; height: auto; border-radius: 0.1rem; } .name { height: 0.6rem; width: 100%; font-size: 0.24rem; line-height: 0.4rem; text-align: center; } .count { position: absolute; right: 0.1rem; top: 0.1rem; color: rgb(253, 251, 251); font-size: 0.2rem; display: flex; align-items: center; .icon { font-size: 0.2rem; } } } } } </style>

在这里插入图片描述
过滤函数(尝试把播放量数组改成亿或者万,调用方法直接在变量中引用方法)

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
changeValue: function(num) { let res = 0; if (num > 100000000) { res = num / 100000000; res = res.toFixed(2) + "亿"; } else if (num > 10000) { res = num / 10000; res = res.toFixed(2) + "万"; } return res; }
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
<template> <div class="musicList"> <div class="musicList-Top"> <div class="title">发现好歌单</div> <div class="more">查看更多</div> </div> <div class="mlist"> <!-- swiper-container --> <div class="swiper-container" id="d2"> <div class="swiper-wrapper"> <div class="swiper-slide" v-for="(item,i) in musicList" :key="i"> <img :src="item.picUrl"> <div class="name">{{item.name}}</div> <div class="count"> <svg class="icon" aria-hidden="true"> <use xlink:href="#icon-whiteplayCircle"></use> </svg> <span>{{changeValue(item.playCount)}}</span> </div> </div> </div> </div> </div> </div> </template> <script scoped> import Swiper from "swiper"; import "swiper/css/swiper.css"; import "swiper/js/swiper.min.js"; import { getMusicList } from "@/api/index.js"; export default { data() { return { musicList: [] }; }, mounted() { var swiper = new Swiper("#d2", { slidesPerView: 3, spaceBetween: 10 }); getMusicList(10).then(res => { this.musicList = res.data.result; }); }, updated() { var swiper = new Swiper("#d2", { slidesPerView: 3, spaceBetween: 10 }); }, methods: { //过滤函数 changeValue: function(num) { let res = 0; if (num > 100000000) { res = num / 100000000; res = res.toFixed(2) + "亿"; } else if (num > 10000) { res = num / 10000; res = res.toFixed(2) + "万"; } return res; } } }; </script> <style lang="less" scoped> .musicList { width: 7.5rem; padding: 0.4rem; .musicList-Top { display: flex; align-content: center; justify-content: space-between; height: 1rem; .title { font-size: 0.4rem; font-weight: 900; } .more { border: 1px solid #ccc; border-radius: 0.1rem; padding: 0.08rem; font-size: 0.24rem; text-align: center; height: 0.5rem; } } } .mlist { .swiper-container { width: 100%; height: 3rem; .swiper-slide { display: flex; flex-direction: column; position: relative; img { width: 100%; height: auto; border-radius: 0.1rem; } .name { height: 0.6rem; width: 100%; font-size: 0.24rem; line-height: 0.4rem; text-align: center; } .count { position: absolute; right: 0.1rem; top: 0.1rem; color: rgb(253, 251, 251); font-size: 0.2rem; display: flex; align-items: center; .icon { font-size: 0.2rem; //svg用fill设置颜色 } } } } } </style>

在这里插入图片描述使用vue3实现好歌单功能

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
<template> <div class="musicList"> <div class="musicList-Top"> <div class="title">发现好歌单</div> <div class="more">查看更多</div> </div> <div class="mlist"> <!-- swiper-container --> <div class="swiper-container" id="d2"> <div class="swiper-wrapper"> <div class="swiper-slide" v-for="(item,i) in musicList.musicLists" :key="i"> <img :src="item.picUrl"> <div class="name">{{item.name}}</div> <div class="count"> <svg class="icon" aria-hidden="true"> <use xlink:href="#icon-whiteplayCircle"></use> </svg> <span>{{changeValue(item.playCount)}}</span> </div> </div> </div> </div> </div> </div> </template> <script scoped> import Swiper from "swiper"; import "swiper/css/swiper.css"; import "swiper/js/swiper.min.js"; import { getMusicList } from "@/api/index.js"; import { reactive, onMounted, onUpdated } from "vue"; export default { setup() { let musicList = reactive({ musicLists: [] }); function changeValue(num) { let res = 0; if (num > 100000000) { res = num / 100000000; res = res.toFixed(2) + "亿"; } else if (num > 10000) { res = num / 10000; res = res.toFixed(2) + "万"; } return res; } onMounted(() => { getMusicList(10).then(res => { musicList.musicLists = res.data.result; }); }), onUpdated(() => { var swiper = new Swiper("#d2", { slidesPerView: 3, spaceBetween: 10 }); }); return { musicList, changeValue }; } }; </script> <style lang="less" scoped> .musicList { width: 7.5rem; padding: 0.4rem; .musicList-Top { display: flex; align-content: center; justify-content: space-between; height: 1rem; .title { font-size: 0.4rem; font-weight: 900; } .more { border: 1px solid #ccc; border-radius: 0.1rem; padding: 0.08rem; font-size: 0.24rem; text-align: center; height: 0.5rem; } } } .mlist { .swiper-container { width: 100%; height: 3rem; .swiper-slide { display: flex; flex-direction: column; position: relative; img { width: 100%; height: auto; border-radius: 0.1rem; } .name { height: 0.6rem; width: 100%; font-size: 0.24rem; line-height: 0.4rem; text-align: center; } .count { position: absolute; right: 0.1rem; top: 0.1rem; color: rgb(253, 251, 251); font-size: 0.2rem; display: flex; align-items: center; .icon { font-size: 0.2rem; //svg用fill设置颜色 } } } } } </style>
七、setup中获取路由信息

在这里插入图片描述
通过路由获取id值

在这里插入图片描述

复制代码
1
2
3
4
const router = useRouter() let id = router.currentRoute._value.query.id

在这里插入图片描述

在这里插入图片描述

复制代码
1
2
3
4
5
//获取歌单的详情 export async function getMusicContent(id){ return await axios.get(`${localhostUrl}/playlist/detail?id=${id}`) }

api 下的index.js

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import axios from 'axios'; //获取轮播图API /* 0: pc 1: android 2: iphone 3: ipad */ let localhostUrl = 'http://localhost:3000' export async function ff(type = 2) { return await axios.get(`${localhostUrl}/banner?type=${type}`); } //获取推荐歌单默认十条数据 export async function getMusicList(limit = 10){ return await axios.get(`${localhostUrl}/personalized?limit=${limit}`) } //获取歌单的详情 export async function getMusicContent(id){ return await axios.get(`${localhostUrl}/playlist/detail?id=${id}`) }

router 下的index.js

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { createRouter, createWebHistory } from 'vue-router' import Home from '../views/Home.vue' const routes = [ { path: '/', name: 'Home', component: Home }, { path: '/listview', name: 'listview', component: () => import('../views/listview.vue') }, ] const router = createRouter({ history: createWebHistory(process.env.BASE_URL), routes }) export default router

views下的listview.vue

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<template> <div> <h1>listview</h1> </div> </template> <script> import { getMusicContent } from "@/api/index.js"; import { reactive, onMounted, onUpdated } from "vue"; import { useRouter } from "vue-router"; //reactive响应式 export default { setup() { const router = useRouter(); let id = router.currentRoute._value.query.id; let state = reactive({ list: [] }); onMounted(() => { getMusicContent(id).then(res => { }); }); } }; </script> <style lang="less" scoped> </style>

views下的home

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<template> <div class="home"> <top-nav/> <swiperzujian/> <iconList/> <musicList/> </div> </template> <script> import topNav from "@/components/topNav.vue"; import swiperzujian from "@/components/swiperzujian.vue"; import iconList from "@/components/iconList.vue"; import musicList from "@/components/musicList.vue"; export default { name: "Home", components: { topNav, swiperzujian,iconList,musicList }, data() { return {}; } }; </script> <style lang="less"> </style>

App.vue

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<template> <div id="nav"> <router-view/><!-- router-view标签是指路由,其实就是指向的意思 --> </div> </template> <script> export default { components:{ } } </script> <style lang="less"> .icon { width: 1em; height: 1em; vertical-align: -0.15em; fill: currentColor; overflow: hidden; } *{ padding: 0; margin: 0; box-sizing: border-box; font-family: "微软雅黑"; } a { text-decoration: none; color: #333; } </style>

在这里插入图片描述

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
<template> <div class="listviewTop"> <img :src="playlist.coverImgUrl" class="bg"> <div class="listViewTopNav"> <div class="back"> <svg class="icon" aria-hidden="true"> <use xlink:href="#icon-zuojiantou"></use> </svg> <div class="title">歌单</div> </div> <div class="right"> <svg class="icon search" aria-hidden="true"> <use xlink:href="#icon-sousuo1"></use> </svg> <svg class="icon" aria-hidden="true"> <use xlink:href="#icon-diandiandianshu-copy"></use> </svg> </div> </div> </div> </template> <script> export default { props: ["playlist"] }; </script> <style lang="less" scoped> .listviewTop { .bg { position: fixed; left: 0; top: 0; width: 100%; height: auto; z-index: -1; filter: blur(40px); } width: 7.5rem; padding: 0 0.4rem; height: 6rem; font-size: 0.4rem; .listViewTopNav { line-height: 0.7rem; color: #fff; display: flex; justify-content: space-between; align-items: center; height: 1rem; .back, .right { display: flex; .icon { font-size: 0.6rem; color: #fff; } .search { margin-right: 0.4rem; } } .back { .icon { font-size: 0.7rem; } .title { margin-left: 0.4rem; } } } } </style>
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<template> <div class="listview" > <listviewTop :playlist="state.playlist"/> </div> </template> <script> import { getMusicContent } from "@/api/index.js"; import { reactive, onMounted, onUpdated } from "vue"; import { useRouter ,useRoute} from "vue-router"; import listviewTop from "@/components/listviewTop.vue" //reactive响应式 export default { setup() { const router = useRouter(); const route = useRoute(); //state是响应式对象,所以传它 let state = reactive({ list: [],playlist:{} }); onMounted(() => { let id = router.currentRoute._value.query.id; getMusicContent(id).then(res => { console.log(res) state.playlist = res.data.playlist; }); }); return{ state } }, components:{ listviewTop } }; </script> <style lang="less" scoped> </style>
八、歌单详情内容

从数据中获取
listviewTop

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
<template> <div class="listviewTop"> <img :src="playlist.coverImgUrl" class="bg"> <div class="listViewTopNav"> <div class="back"> <svg class="icon" aria-hidden="true"> <use xlink:href="#icon-zuojiantou"></use> </svg> <div class="title">歌单</div> </div> <div class="right"> <svg class="icon search" aria-hidden="true"> <use xlink:href="#icon-sousuo1"></use> </svg> <svg class="icon" aria-hidden="true"> <use xlink:href="#icon-diandiandianshu-copy"></use> </svg> </div> </div> <div class="content"> <div class="contentleft"> <img :src="playlist.coverImgUrl" class="imag"> <div class="count"> <svg class="icon" aria-hidden="true"> <use xlink:href="#icon-whiteplayCircle"></use> </svg> <span>{{changeValue(playlist.playCount)}}</span> </div> </div> <div class="contentrigh"> <h4>{{playlist.name}}</h4> <div class="author"> <div class="hearder"> <img :src="playlist.creator.avatarUrl"> <span>{{playlist.creator.nickname}}</span> </div> <div class="discription">{{playlist.description}}</div> </div> </div> </div> <div class="iconList"> <div class="iconitem"> <svg class="icon" aria-hidden="true"> <use xlink:href="#icon-IMliaotian-duihua"></use> </svg> <span>{{playlist.commentCount}}</span> </div> <div class="iconitem"> <svg class="icon" aria-hidden="true"> <use xlink:href="#icon-fenxiang"></use> </svg> <span>{{playlist.shareCount}}</span> </div> <div class="iconitem"> <svg class="icon" aria-hidden="true"> <use xlink:href="#icon-xiazai"></use> </svg> <span>下载</span> </div> <div class="iconitem"> <svg class="icon" aria-hidden="true"> <use xlink:href="#icon-checkbox"></use> </svg> <span>多选</span> </div> </div> </div> </template> <script> export default { props: ["playlist"], setup() { function changeValue(num) { let res = 0; if (num > 100000000) { res = num / 100000000; res = res.toFixed(2) + "亿"; } else if (num > 10000) { res = num / 10000; res = res.toFixed(2) + "万"; } return res; } return { changeValue }; } }; </script> <style lang="less" scoped> .listviewTop { .bg { position: fixed; left: 0; top: 0; width: 100%; height: auto; z-index: -1; filter: blur(40px); } overflow: hidden; width: 7.5rem; padding: 0 0.4rem; height: 6rem; font-size: 0.4rem; .listViewTopNav { padding-top: 0.2rem; line-height: 0.7rem; color: #fff; display: flex; justify-content: space-between; align-items: center; height: 1rem; .back, .right { display: flex; .icon { font-size: 0.6rem; color: #fff; } .search { margin-right: 0.4rem; } } .back { .icon { font-size: 0.7rem; } .title { margin-left: 0.4rem; } } } .content { padding-top: 0.5rem; display: flex; justify-content: space-between; .contentleft { position: relative; img { width: 3rem; height: 3rem; border-radius: 0.1rem; } .count { position: absolute; right: 0.1rem; top: 0.1rem; color: rgb(253, 251, 251); font-size: 0.2rem; display: flex; align-items: center; .icon { font-size: 0.2rem; //svg用fill设置颜色 } } } .contentrigh { position: relative; h4 { color: #fff; } width: 3.3rem; display: flex; flex-direction: column; .author { flex: 1; display: flex; flex-direction: column; align-content: center; justify-content: flex-start; height: 3rem; .hearder { padding-top: 0.1rem; display: flex; flex: 1; span { padding-left: 0.15rem; font-size: 0.24rem; color: rgb(240, 240, 240); line-height: 0.7rem; } img { width: 0.6rem; height: 0.6rem; border-radius: 0.3rem; } } .discription { position: absolute; bottom: 0.1rem; font-size: 0.24rem; color: rgb(209, 209, 209); overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; } } } } .iconList { display: flex; justify-content: space-between; padding: 0 0.3rem; padding-top: 0.3rem; align-items: center; .iconitem { display: flex; flex-direction: column; color: #fff; .icon { font-size: 0.6rem; } span { padding-top: 0.1rem; font-size: 0.24rem; line-height: 0.24rem; text-align: center; } } } } </style>

listview.vue

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<template> <div class="listview"> <listviewTop :playlist="state.playlist"/> </div> </template> <script> import { getMusicContent } from "@/api/index.js"; import { reactive, onMounted, onUpdated } from "vue"; import { useRouter, useRoute } from "vue-router"; import listviewTop from "@/components/listviewTop.vue"; //reactive响应式 export default { setup() { const router = useRouter(); const route = useRoute(); //state是响应式对象,所以传它 let state = reactive({ list: [], playlist: { creator: {} } }); onMounted(() => { let id = router.currentRoute._value.query.id; getMusicContent(id).then(res => { state.playlist = res.data.playlist; console.log(res); }); }); return { state }; }, components: { listviewTop } }; </script> <style lang="less" scoped> </style>

在这里插入图片描述

详情页的图标列表与返回页面

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
<template> <div class="listviewTop"> <img :src="playlist.coverImgUrl" class="bg"> <div class="listViewTopNav"> <div class="back" @click="$router.go(-1)"> <svg class="icon" aria-hidden="true"> <use xlink:href="#icon-zuojiantou"></use> </svg> <div class="title">歌单</div> </div> <div class="right"> <svg class="icon search" aria-hidden="true"> <use xlink:href="#icon-sousuo1"></use> </svg> <svg class="icon" aria-hidden="true"> <use xlink:href="#icon-diandiandianshu-copy"></use> </svg> </div> </div> <div class="content"> <div class="contentleft"> <img :src="playlist.coverImgUrl" class="imag"> <div class="count"> <svg class="icon" aria-hidden="true"> <use xlink:href="#icon-whiteplayCircle"></use> </svg> <span>{{changeValue(playlist.playCount)}}</span> </div> </div> <div class="contentrigh"> <h4>{{playlist.name}}</h4> <div class="author"> <div class="hearder"> <img :src="playlist.creator.avatarUrl"> <span>{{playlist.creator.nickname}}</span> </div> <div class="discription">{{playlist.description}}</div> </div> </div> </div> <div class="iconList"> <div class="iconitem"> <svg class="icon" aria-hidden="true"> <use xlink:href="#icon-IMliaotian-duihua"></use> </svg> <span>{{playlist.commentCount}}</span> </div> <div class="iconitem"> <svg class="icon" aria-hidden="true"> <use xlink:href="#icon-fenxiang"></use> </svg> <span>{{playlist.shareCount}}</span> </div> <div class="iconitem"> <svg class="icon" aria-hidden="true"> <use xlink:href="#icon-xiazai"></use> </svg> <span>下载</span> </div> <div class="iconitem"> <svg class="icon" aria-hidden="true"> <use xlink:href="#icon-checkbox"></use> </svg> <span>多选</span> </div> </div> </div> </template> <script> export default { props: ["playlist"], setup() { function changeValue(num) { let res = 0; if (num > 100000000) { res = num / 100000000; res = res.toFixed(2) + "亿"; } else if (num > 10000) { res = num / 10000; res = res.toFixed(2) + "万"; } return res; } return { changeValue }; } }; </script> <style lang="less" scoped> .listviewTop { .bg { position: fixed; left: 0; top: 0; width: 100%; height: auto; z-index: -1; filter: blur(40px); } overflow: hidden; width: 7.5rem; padding: 0 0.4rem; height: 6rem; font-size: 0.4rem; .listViewTopNav { padding-top: 0.2rem; line-height: 0.7rem; color: #fff; display: flex; justify-content: space-between; align-items: center; height: 1rem; .back, .right { display: flex; .icon { font-size: 0.6rem; color: #fff; } .search { margin-right: 0.4rem; } } .back { .icon { font-size: 0.7rem; } .title { margin-left: 0.4rem; } } } .content { padding-top: 0.5rem; display: flex; justify-content: space-between; .contentleft { position: relative; img { width: 3rem; height: 3rem; border-radius: 0.1rem; } .count { position: absolute; right: 0.1rem; top: 0.1rem; color: rgb(253, 251, 251); font-size: 0.2rem; display: flex; align-items: center; .icon { font-size: 0.2rem; //svg用fill设置颜色 } } } .contentrigh { position: relative; h4 { color: #fff; } width: 3.3rem; display: flex; flex-direction: column; .author { flex: 1; display: flex; flex-direction: column; align-content: center; justify-content: flex-start; height: 3rem; .hearder { padding-top: 0.1rem; display: flex; flex: 1; span { padding-left: 0.15rem; font-size: 0.24rem; color: rgb(240, 240, 240); line-height: 0.7rem; } img { width: 0.6rem; height: 0.6rem; border-radius: 0.3rem; } } .discription { position: absolute; bottom: 0.1rem; font-size: 0.24rem; color: rgb(209, 209, 209); overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; } } } } .iconList { display: flex; justify-content: space-between; padding: 0 0.3rem; padding-top: 0.3rem; align-items: center; .iconitem { display: flex; flex-direction: column; color: #fff; .icon { font-size: 0.6rem; } span { padding-top: 0.1rem; font-size: 0.24rem; line-height: 0.24rem; text-align: center; } } } } </style>
九、播放列表实现

还是基本布局以及循环拿到的数据
playlist.vue

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
<template> <div class="playlist"> <div class="playlist-Top"> <div class="left"> <svg class="icon search" aria-hidden="true"> <use xlink:href="#icon-bofang"></use> </svg> <div class="com1"> <div class="com2"> <div class="title">播放全部</div> <div class="num">(共{{playlist.tracks.length}}首)</div> </div> </div> </div> <div class="btn">+收藏({{playlist.subscribedCount}})</div> </div> <div class="list"> <div class="listitem" v-for="(item,i) in playlist.tracks" :key="i"> <div class="playCount">{{i+1}}</div> <div class="playcontent"> <div class="h4">{{item.name}}</div> <div class="author"> <span class="tag" v-for="(item,i) in playlist.tags" :key="i">{{item}}</span> <div class="discription">{{item.al.name}}</div> </div> </div> <div class="playicon"> <svg class="icon play" aria-hidden="true"> <use xlink:href="#icon-bofang2"></use> </svg> <svg class="icon" aria-hidden="true"> <use xlink:href="#icon-diandian"></use> </svg> </div> </div> </div> </div> </template> <script> export default { props: ["playlist"] }; </script> <style lang="less" scoped> .playlist { border-top-left-radius: 0.3rem; border-top-right-radius: 0.3rem; background-color: #fff; width: 7.5rem; .playlist-Top { position: relative; display: flex; height: 1.2rem; align-items: center; width: 7.5rem; justify-content: space-between; .left { width: 6.7rem; flex: 1; display: flex; font-size: 0.4rem; padding-left: 0.2rem; .icon { width: 0.5rem; height: 0.5rem; font-size: 0.5rem; } .com1 { width: 5.5rem; margin-left: 0.3rem; display: flex; font-size: 0.34rem; font-family: "微软雅黑"; color: #333; .com2 { display: flex; align-items: center; .num { line-height: 0.3rem; font-size: 0.3rem; color: rgba(187, 185, 185, 0.664); } } } } .btn { position: absolute; right: 0.15rem; font-size: 0.27rem; color: #fff; height: 0.85rem; line-height: 0.85rem; text-align: center; border-radius: 0.4rem; width: 2.4rem; background-color: #ff4935; } } .list { position: relative; width: 7.5rem; height: 1.2rem; .listitem { .playCount { height: 1.2rem; width: 1rem; text-align: center; line-height: 1.2rem; color: rgb(165, 164, 164); font-size: 0.36rem; } background-color: #fff; display: flex; position: relative; .playcontent { .h4 { padding-top: 0.1rem; display: flex; align-items: center; height: 0.85rem; font-size: 0.3rem; } .author { bottom: 0.1rem; position: absolute; height: 0.35rem; display: flex; align-items: center; span { width: 2.8em; text-align: center; height: 0.25rem; color: rgb(250, 43, 43); border-radius: 3px; font-size: 0.16rem; line-height: 0.2rem; border: 0.5px solid #ee8888; background-color: #ffd0c5a4; margin-right: 0.1rem; } .discription { color: #c2bdbd; height: 0.3rem; line-height: 0.3rem; font-size: 0.25rem; } } } .playicon { // z-index: -1; // position: fixed; position: absolute; right: 0.25rem; height: 1.2rem; line-height: 1.2rem; text-align: center; margin-top: 0.1rem; .icon { font-size: 0.5rem; } .play{ margin-right: 0.2rem; } } } } } </style>

public文件夹下的js文件夹的index.js

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html> <html lang=""> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <link rel="icon" href="<%= BASE_URL %>favicon.ico"> <title><%= htmlWebpackPlugin.options.title %></title> <script src="//at.alicdn.com/t/font_3042525_nvfyrtl9iw.js"></script> </head> <body> <script src="<%= BASE_URL %>js/rem.js"></script> </noscript> <div id="app"></div> </body> </html>

listview.vue

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<template> <div class="listview"> <listviewTop :playlist="state.playlist"/> <playlist :playlist="state.playlist"/> </div> </template> <script> import { getMusicContent } from "@/api/index.js"; import { reactive, onMounted, onUpdated } from "vue"; import { useRouter, useRoute } from "vue-router"; import listviewTop from "@/components/listviewTop.vue"; import playlist from "@/components/playlist.vue" //reactive响应式 export default { setup() { const router = useRouter(); const route = useRoute(); //state是响应式对象,所以传它 let state = reactive({ list: [], playlist: { creator: {},tracks:{} } }); onMounted(() => { let id = router.currentRoute._value.query.id; getMusicContent(id).then(res => { state.playlist = res.data.playlist; console.log(res); }); }); return { state }; }, components: { listviewTop,playlist } }; </script> <style lang="less" scoped> .listview{ display: flex; flex-direction: column; } </style>

listviewTop.vue

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
<template> <div class="listviewTop"> <img :src="playlist.coverImgUrl" class="bg"> <div class="listViewTopNav"> <div class="back" @click="$router.go(-1)"> <svg class="icon" aria-hidden="true"> <use xlink:href="#icon-zuojiantou"></use> </svg> <div class="title">歌单</div> </div> <div class="right"> <svg class="icon search" aria-hidden="true"> <use xlink:href="#icon-sousuo1"></use> </svg> <svg class="icon" aria-hidden="true"> <use xlink:href="#icon-diandiandianshu-copy"></use> </svg> </div> </div> <div class="content"> <div class="contentleft"> <img :src="playlist.coverImgUrl" class="imag"> <div class="count"> <svg class="icon" aria-hidden="true"> <use xlink:href="#icon-whiteplayCircle"></use> </svg> <span>{{changeValue(playlist.playCount)}}</span> </div> </div> <div class="contentrigh"> <h4>{{playlist.name}}</h4> <div class="author"> <div class="hearder"> <img :src="playlist.creator.avatarUrl"> <span>{{playlist.creator.nickname}}</span> </div> <div class="discription">{{playlist.description}}</div> </div> </div> </div> <div class="iconList"> <div class="iconitem"> <svg class="icon" aria-hidden="true"> <use xlink:href="#icon-IMliaotian-duihua"></use> </svg> <span>{{playlist.commentCount}}</span> </div> <div class="iconitem"> <svg class="icon" aria-hidden="true"> <use xlink:href="#icon-fenxiang"></use> </svg> <span>{{playlist.shareCount}}</span> </div> <div class="iconitem"> <svg class="icon" aria-hidden="true"> <use xlink:href="#icon-xiazai"></use> </svg> <span>下载</span> </div> <div class="iconitem"> <svg class="icon" aria-hidden="true"> <use xlink:href="#icon-checkbox"></use> </svg> <span>多选</span> </div> </div> </div> </template> <script> export default { props: ["playlist"], setup() { function changeValue(num) { let res = 0; if (num > 100000000) { res = num / 100000000; res = res.toFixed(2) + "亿"; } else if (num > 10000) { res = num / 10000; res = res.toFixed(2) + "万"; } return res; } return { changeValue }; } }; </script> <style lang="less" scoped> .listviewTop { .bg { position: fixed; left: 0; top: 0; width: 100%; height: auto; z-index: -1; filter: blur(40px); } overflow: hidden; width: 7.5rem; padding: 0 0.4rem; font-size: 0.4rem; .listViewTopNav { padding-top: 0.2rem; line-height: 0.7rem; color: #fff; display: flex; justify-content: space-between; align-items: center; height: 1rem; .back, .right { display: flex; .icon { font-size: 0.6rem; color: #fff; } .search { margin-right: 0.4rem; } } .back { .icon { font-size: 0.7rem; } .title { margin-left: 0.4rem; } } } .content { padding-top: 0.5rem; display: flex; justify-content: space-between; .contentleft { position: relative; img { width: 3rem; height: 3rem; border-radius: 0.1rem; } .count { position: absolute; right: 0.1rem; top: 0.1rem; color: rgb(253, 251, 251); font-size: 0.2rem; display: flex; align-items: center; .icon { font-size: 0.2rem; //svg用fill设置颜色 } } } .contentrigh { position: relative; h4 { color: #fff; } width: 3.3rem; display: flex; flex-direction: column; .author { flex: 1; display: flex; flex-direction: column; align-content: center; justify-content: flex-start; height: 3rem; .hearder { padding-top: 0.1rem; display: flex; flex: 1; span { padding-left: 0.15rem; font-size: 0.24rem; color: rgb(240, 240, 240); line-height: 0.7rem; } img { width: 0.6rem; height: 0.6rem; border-radius: 0.3rem; } } .discription { position: absolute; bottom: 0.1rem; font-size: 0.24rem; color: rgb(209, 209, 209); overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; } } } } .iconList { height: 1.5rem; display: flex; justify-content: space-between; padding: 0 0.3rem; padding-top: 0.3rem; align-items: center; .iconitem { display: flex; flex-direction: column; color: #fff; .icon { font-size: 0.6rem; } span { padding-top: 0.1rem; font-size: 0.24rem; line-height: 0.24rem; text-align: center; } } } } </style>

效果图
在这里插入图片描述

网易云音乐移动端项目实战下一篇【分解中】

最后

以上就是单薄唇膏最近收集整理的关于网易云音乐移动端项目实战(分解上)的全部内容,更多相关网易云音乐移动端项目实战(分解上)内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(76)

评论列表共有 0 条评论

立即
投稿
返回
顶部