Vue3+node.js网易云音乐实战项目 歌单页面详细代码

这篇博客展示了如何使用Vue.js实现一个音乐歌单页面,包括ListView.vue主页面、listviewTop.vue顶部组件、listviewIcon.vue导航栏组件和playlistView.vue播放列表组件。每个组件的功能和样式设计详细说明,例如传递参数、获取歌单详情、格式化显示数据等。通过这些组件的组合,实现了完整的歌单展示效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

其他页面实现可以看我的主页,如果对你有帮助请点一个赞或收藏

整体效果
image-20220627164222359

1、首先是路由跳转的主页面 ListView.vue

image-20220627163259098

<template>
    <div class="listView">
        <!-- 传递参数给子组件 -->
        <listviewtop :playlist = "state.playlist"></listviewtop>
        <listviewIcon :playlist = "state.playlist"></listviewIcon>
        <playlistView :playlist = "state.playlist"></playlistView>
    </div>
</template>

<script>
import listviewtop from '@/components/listView/listviewTop.vue'
import listviewIcon from '@/components/listView/listviewIcon.vue'
import playlistView from '@/components/listView/palylistView.vue'

import {postPlaylistDetail} from '@/api/index';
import { onMounted ,reactive} from 'vue';
import {useRoute} from 'vue-router'
export default {
    components:{
        listviewtop,
        listviewIcon,
        playlistView
    },
    setup(){
     const route = useRoute()
     let state = reactive ({
         list:[],
         playlist:{
             creator:{},
             tracks:{}  
         },
         })
         
     onMounted(async()=>{
        //  获取传入的id
         let id = route.query.id
         let result = await postPlaylistDetail(id)
         state.playlist = result.data.playlist
     })
     return{
       state
     }
 }
}
</script>

<style lang="less" scoped>
.listView{
    width: 7.5rem;
}


</style>

2、listviewTop.vue顶部

然后是三个子组件,listviewTop.vue顶部

image-20220627163618737

<template>
    <div class="listViewTop">
        <img class="bg" :src="playlist.coverImgUrl" alt="">
        <div class="listViewTopNav">
            <div class="left">
                <!-- 返回按钮 -->
                <div class="back" @click="$router.back()">
                    <svg class="icon" aria-hidden="true">
                        <use xlink:href="#icon-xitongfanhui"></use>
                    </svg>
                </div>
                <!-- 歌单 -->
                <div class="title">
                    歌单
                </div>
            </div>
            <!-- 搜索按钮 和 更多按钮-->
            <div class="right">
                <div class="search">
                    <svg class="icon" aria-hidden="true">
                        <use xlink:href="#icon-sousuo"></use>
                    </svg>
                </div>
                <div class="more">
                    <svg class="icon" aria-hidden="true">
                        <use xlink:href="#icon-gengduo-shuxiang"></use>
                    </svg>
                </div>
            </div>
        </div>
        <!-- 头像 简介部分 -->
        <div class="content">
            <div class="content_left">
                <!-- 头像 -->
                <div class="hp">
                    <img class="headPortrait" :src="playlist.coverImgUrl" alt="">
                     <div class="count">
                        <span>▷</span>
                        <!--播放量  -->
                        <span>{{formatNum(playlist.playCount)}}</span>
                    </div>
                </div>
                <!-- 头像下面的小方块 -->
                <div class="headPortraitBlock">
                    <img class="headPortraitBlockBG" :src="playlist.coverImgUrl" alt="">
                </div>
            </div>
            <div class="contnet_right">
                <div class="rightTop">
                <!-- 歌单用户姓名 -->
                    <div class="UserTitle">
                        <p>{{playlist.name}}</p>
                    </div>
                <!-- 更多图标 -->
                    <div class="contnet_right_more">
                        <svg class="icon" aria-hidden="true">
                            <use xlink:href="#icon-gengduo"></use>
                        </svg>
                    </div>
                </div>
                <!-- 用户头像和姓名 -->
                <div class="UserInfo">
                    <!-- 头像 -->
                    <div class="UserImg">
                        <img class="UserPortrait"  :src="playlist.creator.avatarUrl">
                    </div>
                    <!-- 名称 -->
                    <div class="UserName">
                        <p>{{playlist.creator.nickname}}</p>
                    </div>
                    <!-- 关注图标 -->
                    <div class="UserAttention">
                        <svg class="icon" aria-hidden="true">
                            <use xlink:href="#icon-plus"></use>
                        </svg>
                    </div>
                </div>
                <!-- 用户简介 -->
                <div class="UserAbout">
                    <div class="description">
                        <p>{{playlist.description}}</p>
                    </div>
                    <div class="UserAboutMore">
                        <svg class="icon" aria-hidden="true">
                            <use xlink:href="#icon-gengduo1"></use>
                        </svg>
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

<script>
import { computed } from 'vue'
export default {
    props:['playlist'],
    setup(){
     // 格式化输出听歌人数
        const formatNum = computed(()=>{
           return function(num){
                if(num>10000) return (num/10000).toFixed(2) + '万'
            }
        })
        return {
            formatNum
        }
    }
}
</script>

<style lang="less" scoped>
.listViewTop{
    width: 7.5rem;
    padding: 0 0.4rem;
    // border: 1px solid red;
    // height: 5rem;
    border-radius: 0 0 10% 10%;
    overflow: hidden;
    .bg{
        position:fixed;
        left:0;
        top: 0;
        width: 7.5rem;
        // height: auto;
        z-index: -1;
        // 设置滤镜模糊
        filter: blur(150px);
        overflow: hidden;
        height: 4rem;
        transform: scale(4);
    }
    .listViewTopNav{
        display: flex;
        justify-content: space-between;
        align-items: center;
        height: 1.2rem;
        font-size: 0.35rem;
        .icon{
            width: 0.5rem;
            height: 0.5rem;
            fill: #fff;
        }
        .title{
            margin-left: 0.4rem;
        }
        .search{
            margin-right: 0.5rem;
        }
    }
    .left,.right{
        display: flex;
        color: #fff;
    }


    // 标题 头像部分
    .content{
        display: flex;
        justify-content: space-between;
        margin-top: 10px;
    }
    // 左边头像部分
    .content_left{
        position: relative;
        // 头像
        .hp{
            position: absolute;
            z-index: 2;
            top: 8px;
        }
        .headPortrait{
                width: 2.5rem;
                height: 2.5rem;
                border-radius: 10%;
            }
        .headPortraitBlock{
            position: absolute;
            z-index: 1;
            left: 13px;
            // 隐藏超出的部分
            overflow: hidden;
            border-radius: 10px;
            .headPortraitBlockBG{
                width: 2rem;
                height: 2rem;
                background-size: cover;
                filter: blur(50px);
                // 局部放大取颜色
                transform: scale(3);
            }
        }
         .count{
            position: absolute;
            right: 0.1rem;
            top: 0.1rem;
            font-size: 0.24rem;
            color:rgb(246,247,250);
            border-radius:10px;
            background-color: rgba(19, 19, 19, 0.2);
        }
    }

    // 右边歌单名和用户简介部分
    .contnet_right{
         width: 4rem;
         height: 2.5rem;
        .rightTop{
            display: flex;
            justify-content: space-around;
            height: 1.25rem;
            .UserTitle{
                color: #fff;
                font-size: 15px;
            }
            .icon{
                fill: #fff;
                width: 0.5rem;
                height: 0.5rem;
            }
        }
        // 用户信息
        .UserInfo{
            display: flex;
            color: rgba(255, 255, 255, 0.5);
            .UserPortrait{
                width: 0.5rem;
                height: 0.5rem;
                border-radius: 100%;
            }
            .UserName{
                height: 26px;
                padding-left: 4px;
                font-size: 10px;
                line-height: 26px;
            }
            .UserAttention{
                width: 0.7rem;
                height: 0.5rem;
                background-color: rgba(207, 207, 207, 0.5);
                margin-left: 10px;
                border-radius: 12px;
                display: flex;
                .icon{
                    align-self: center;
                    margin:0 auto;
                    fill: rgba(255, 255, 255, 0.6);
                }
            }
        }
        // 用户简介
        .UserAbout{
                display: flex;
                justify-content: space-around;
                margin-top: 0.3rem;
                width: 3.5rem;
                color: rgba(255, 255, 255, 0.5);
               .description{
                    width: 3rem;
                    font-size: 14px;
                    display: -webkit-box;
                    -webkit-line-clamp: 1;
                    -webkit-box-orient: vertical;
                    overflow: hidden;
                }
                .icon{
                    align-self: center;
                    margin:0 auto;
                    fill: rgba(255, 255, 255, 0.6);
                }
        }
    }

}
</style>

3、listviewIcon.vue

然后就是导航栏的子组件部分 listviewIcon.vue

image-20220627164027870

<template>
    <div class="listviewIcon">
        <!-- 收藏 -->
        <div class="collectIcon">
            <svg class="icon" aria-hidden="true">
                <use xlink:href="#icon-shoucangjia"></use>
            </svg>
            <p>{{playlist.subscribedCount}}</p>
        </div>
        <!-- 评论 -->
        <div class="commentIcon">
            <svg class="icon" aria-hidden="true">
                <use xlink:href="#icon-pinglun-"></use>
            </svg>
            <p>{{playlist.commentCount}}</p>
        </div>
        <!-- 分享 -->
        <div class="shareIcon">
            <svg class="icon" aria-hidden="true">
                <use xlink:href="#icon-fenxiang"></use>
            </svg>
            <p>{{playlist.shareCount}}</p>
        </div>
    </div>
</template>

<script>
export default {
    props:['playlist'],
}
</script>

<style lang="less" scoped>
.listviewIcon{
    width: 6rem;
    display: flex;
    justify-content: space-around;
    // border: 1px solid red;
    border-radius: 20px;
    padding: 10px;
    // margin: 0.7rem auto -25px auto;
    background: #fff;
    // 阴影
    box-shadow: 0px 1px 10px -4px rgba(0, 0, 0, 0.2);
    font-size: 13px;
    // 让它在播放列表的上面
    position: absolute;
    z-index: 1;
    left: 0.7rem;
    top: 4.5rem;
    .icon{
        width: 0.5rem;
        height: 0.5rem;
        margin-right: 10px;
    }
}
    .listviewIcon div:nth-child(n){
        display: flex;
        justify-content: center;
        align-items: center;
    }
    .listviewIcon div:nth-child(n + 2) {		//n+2就是从第二个元素开始往后所有的元素
        border-left: 1px solid rgba(77, 77, 77, 0.5);
        padding-left: 20px;
    }

</style>

4、playlistView.vue

然后就是播放列表的子组件,playlistView.vue

image-20220627164206231

<template>
  <div class="playlistView">
      <!-- 图标栏 -->
      <div class="playlistIcon">
          <div class="leftIcon">
            <svg class="icon" aria-hidden="true">
                <use xlink:href="#icon-bofang"></use>
            </svg>
            <p  class="words">播放全部</p>
            <p class="count">({{playlist.tracks.length}})</p>
          </div>
          <div class="rightIcon">
            <svg class="icon" aria-hidden="true">
                <use xlink:href="#icon-xiazai"></use>
            </svg>
            <svg class="icon" aria-hidden="true">
                <use xlink:href="#icon-quanxuan"></use>
            </svg>
          </div>
      </div>
      <!-- 播放列表栏 -->
      <div class="playlists">
          <div class="playItem" v-for="(item,i) in playlist.tracks" :key="i">
            <!-- 列表左边区域 -->
              <div class="left">
                  <!-- 编号 -->
                  <div class="index">{{i+1}}</div>
                  <div class="content">
                      <!-- 音乐名 -->
                      <div class="title">{{item.name}}</div>
                      <!-- 作者名和专辑名 -->
                      <div class="anthor">{{item.ar[0].name}} - {{item.al.name}}</div>
                  </div>
              </div>
              <!-- 列表右边区域 -->
              <div class="right">
                  <!-- 图标部分 -->
                <svg class="icon" aria-hidden="true">
                    <use xlink:href="#icon-bofang"></use>
                </svg>
                <svg class="icon" aria-hidden="true">
                    <use xlink:href="#icon-gengduo-shuxiang"></use>
                </svg>
              </div>
          </div>
      </div>
  </div>
</template>

<script>
export default {
     props:['playlist'],
    setup(){

    }
}
</script>

<style lang="less" scoped>
.playlistView{
    width: 7.5rem;
    background-color: rgb(255, 255, 255);
    margin-top:1rem;
    padding: 1rem 0.4rem 0 0.4rem;
    .icon{
        width: 0.5rem;
        height: 0.5rem;
    }
    .words{
        margin-right: 5px;
        line-height: 0.5rem;
    }
    .count{
        font-size: 10px;
        line-height: 0.5rem;
    }
}
.playlistIcon{
    display: flex;
    justify-content: space-between;
    align-items: center;
}
.playlistIcon div:nth-child(n){
    display: flex;
    justify-content: space-between;
    align-items: center;
}
.rightIcon svg:nth-child(1){
    margin-right: 15px;
}
.leftIcon svg:nth-child(1){
    margin-right: 15px;
    fill: red;
}

// 播放列表栏
.playlists{
    margin-top: 0.5rem;
    .playItem{
        display: flex;
        justify-content: space-between;
        margin-top: 0.5rem;
        .left{
            display: flex;
            justify-content: space-between;
            // align-items: center;
            // 编号
            .index{
                width: 0.5rem;
                font-size: 20px;
                opacity: 0.5;
                display: flex;
                justify-content:center; 
                align-items:center; 
            }
            // 作者名和专辑名
            .content{
                margin-left: 0.4rem;
                .anthor{
                    font-size: 10px;
                    opacity: 0.5;
                }
            }  
        }
        .right{
            .icon{
                width: 0.5rem;
                height: 0.5rem;
                opacity: 0.5;
                margin-left: 15px;
            }
        }
    }
}


</style>

整体效果

image-20220627164222359

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

周粥粥ya

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值