在线预览:点击访问
其他项目访问:点击访问
项目使用传统vue项目结构实现,前端采用element实现。
element官网:Element - The world's most popular Vue UI framework



5.歌曲在线实时搜索
7.扫码登录
http://www.codeman.ink:3000

- <template>
- <div class="musicListDetail" v-loading="!musicListDetail">
-
- <div class="listInfo">
-
- <div class="listAvatar">
- <img :src="musicListDetail?musicListDetail.coverImgUrl:''" alt="" />
- div>
- <div class="right">
-
- <div class="title">
- <div class="titleTag">歌单div>
- <div class="titleContent">{{ musicListDetail?musicListDetail.name:'' }}div>
- div>
-
- <div class="user">
- <div class="userAvatar">
- <img :src="musicListDetail&&musicListDetail.creator?musicListDetail.creator.avatarUrl:''" alt="" />
- div>
- <div
- class="userName">
- {{ musicListDetail&&musicListDetail.creator?musicListDetail.creator.nickname:'' }}
- div>
- <div class="createTime">
- {{ musicListDetail?musicListDetail.createTime:'' | showDate }}创建
- div>
- div>
-
- <div class="buttons">
- <div class="buttonItem playAll" @click="playAll">
- <i class="iconfont icon-bofang playAll">i>
- <span>播放全部span>
- div>
- <div class="buttonItem">
- <i class="iconfont icon-xihuan">i>
- <span>收藏span>
- div>
- <div class="buttonItem">
- <i class="iconfont icon-zhuanfa">i>
- <span>分享span>
- div>
- div>
-
- <div class="tags">
- 标签:
- <div
- class="tagItem"
- v-for="(item, index) in musicListDetail?musicListDetail.tags:[]"
- :key="index"
- >
- {{ item }}
- div>
- <div v-if="!musicListDetail||!musicListDetail.tags||musicListDetail.tags.length == 0">暂无标签div>
- div>
-
- <div class="otherInfo">
- <div class="musicNum">
- 歌曲 : {{ musicListDetail.trackCount | handleNum }}
- div>
- <div class="playCount">
- 播放 : {{ musicListDetail.playCount | handleNum }}
- div>
- div>
- <div class="desc">
- 简介 :
- {{
- musicListDetail.description
- ? musicListDetail.description
- : "暂无简介"
- }}
- div>
- div>
- div>
-
- <div class="musicList">
- <el-tabs value="first">
- <el-tab-pane label="歌曲列表" name="first">
-
- <el-table
- :data="musicListDetail.tracks"
- size="mini"
- style="width: 100%"
- @row-dblclick="clickRow"
- @cell-click="clickCell"
- highlight-current-row
- stripe
- lazy
- :row-key="
- (row) => {
- return row.id;
- }
- "
- v-infinite-scroll="this.$store.state.isLogin ? loadMore : ''"
- :infinite-scroll-disabled="scrollLoadDisabled"
- :infinite-scroll-distance="1500"
- :infinite-scroll-immediate="false"
- >
- <el-table-column
- label=""
- width="40"
- type="index"
- :index="handleIndex"
- >
- el-table-column>
- <el-table-column label="" width="23">
-
- <i class="iconfont icon-download">i>
- el-table-column>
- <el-table-column prop="name" label="音乐标题" min-width="350">
- el-table-column>
- <el-table-column prop="ar[0].name" label="歌手" min-width="120">
- el-table-column>
- <el-table-column prop="al.name" label="专辑" min-width="170">
- el-table-column>
- <el-table-column prop="dt" label="时长" min-width="100">
- el-table-column>
-
- el-table>
- <div class="loadMore" v-if="isMore && !this.$store.state.isLogin">
- 登陆后查看更多音乐
- div>
- <div class="placeholder" v-else>div>
-
- el-tab-pane>
- el-tabs>
- div>
-
-
- <go-top scrollObj=".musicListDetail">go-top>
- div>
- template>
-
- <script>
- import { formatDate, handleNum, handleMusicTime } from "@/plugins/utils";
- import Comment from "@/components/comment/Comment";
- import GoTop from "@/components/goTop/GoTop.vue";
- import UserListCard from "@/components/userListCard/UserListCard.vue";
-
- export default {
- name: "MusicListDetail",
- data() {
- return {
- musicListDetail: {
- trackIds:[],
- tracks:[]
- },
- comments: {},
- // 当前评论页数
- currentCommentPage: 1,
- // 是否还有更多音乐
- isMore: false,
- // 是否禁止滚动加载
- scrollLoadDisabled: false,
- };
- },
- components: {
- Comment,
- GoTop,
- UserListCard,
- },
- methods: {
- // 请求
- // 根据传来的 id 查询歌单
- async getMusicListDetail() {
- var timestamp = Date.parse(new Date());
- // console.log(this.$route.params.id);
- let result = await this.$request("/playlist/detail", {
- id: this.$route.params.id,
- timestamp,
- });
- // console.log(result);
- this.musicListDetail = result.data.playlist;
- // console.log(this.musicListDetail);
- // 判断是否还有更多音乐
- if (
- this.musicListDetail.tracks.length !=
- this.musicListDetail.trackIds.length
- ) {
- this.isMore = true;
- }
- // 处理播放时间
- this.musicListDetail.tracks.forEach((item, index) => {
- this.musicListDetail.tracks[index].dt = handleMusicTime(item.dt);
- });
- },
- // 获取歌曲详情
- async getMusicDetail(ids) {
- if (this.isMore == false) return;
- this.scrollLoadDisabled = true;
-
- let res = await this.$request("/song/detail", { ids });
- // 处理时间
- console.log(res);
- res.data.songs.forEach((item, index) => {
- res.data.songs[index].dt = handleMusicTime(item.dt);
- });
- this.musicListDetail.tracks.push(...res.data.songs);
- // 判断是否还有更多音乐
- if (
- this.musicListDetail.tracks.length <
- this.musicListDetail.trackIds.length
- ) {
- this.isMore = true;
- this.scrollLoadDisabled = false;
- } else {
- this.isMore = false;
- }
- },
-
- // 事件函数
- handleIndex(index) {
- // console.log(index);
- index += 1;
- if (index < 10) {
- return "0" + index;
- } else {
- return index;
- }
- },
- // 双击table的row的回调
- async clickRow(row) {
- console.log(row);
- // 将musicId提交到vuex中 供bottomControl查询歌曲url和其它操作
- this.$store.commit("updateMusicId", row.id);
- // 如果歌单发生变化,则提交歌单到vuex
- if (this.musicListDetail.id != this.$store.state.musicListId) {
- // 将歌单传到vuex
- this.$store.commit("updateMusicList", {
- musicList: this.musicListDetail.tracks,
- musicListId: this.musicListDetail.id,
- });
- }
-
- // let result = await this.$request("/song/url", { id: row.id, br: 320000 });
- // console.log(result.data.data[0].url);
- // this.$store.commit("updateMusicUrl", result.data.data[0].url);
- },
- // 点击播放全部按钮的回调
- playAll() {
- this.$store.commit("updateMusicId", this.musicListDetail.tracks[0].id);
- this.$store.commit("updateMusicList", {
- musicList: this.musicListDetail.tracks,
- musicListId: this.musicListDetail.id,
- });
- },
- handleDOM(current, last) {
- if (document.querySelector(".musicListDetail")) {
- let tableRows = document
- .querySelector(".musicListDetail")
- .querySelectorAll(".el-table__row");
- // 遍历当前musicList 找到当前播放的index的行进行渲染
- // console.log(tableRows);
- let index = this.musicListDetail.tracks.findIndex(
- (item) => item.id == current
- );
- // console.log(index);
- if (index != -1) {
- // 直接修改dom样式的颜色无效 可能是因为第三方组件的原故
- // 通过引入全局样式解决
- // 将正在播放的音乐前面的索引换成小喇叭
- tableRows[index].children[0].querySelector(
- ".cell"
- ).innerHTML = ``;
- tableRows[index].children[0]
- .querySelector(".iconfont")
- .classList.add("currentRow");
- tableRows[index].children[2]
- .querySelector(".cell")
- .classList.add("currentRow");
- }
- // 清除上一首的样式
- if (last != -1) {
- let lastIndex = this.musicListDetail.tracks.findIndex(
- (item) => item.id == last
- );
- if (lastIndex != -1) {
- // 将上一个播放的dom的小喇叭换回索引
- tableRows[lastIndex].children[0].querySelector(
- ".cell"
- ).innerHTML = `${
- lastIndex + 1 < 10 ? "0" + (lastIndex + 1) : lastIndex + 1
- }`;
-
- // 将上一首的类名删掉 小喇叭的html已经被替换了,不需要再还原
- tableRows[lastIndex].children[2]
- .querySelector(".cell")
- .classList.remove("currentRow");
- }
- }
- }
- },
- // 点击加载所有音乐的回调
- loadMore() {
- if (!this.$store.state.isLogin) {
- this.$message.error("请先进行登录操作!");
- return;
- }
- // console.log("加载所有音乐");
- // this.isMore = false;
-
- let arr = this.musicListDetail.trackIds.slice(
- this.musicListDetail.tracks.length
- );
- if (arr.length > 100) {
- arr = arr.slice(0, 100);
- }
- // console.log(arr.length);
- let ids = "";
- arr.forEach((item) => {
- ids += item.id + ",";
- });
- ids = ids.substr(0, ids.length - 1);
- // console.log(ids);
- this.getMusicDetail(ids);
- },
- async clickCell(row, column, cell) {
- // 判断点击的是下载按钮
- if (cell.querySelector(".icon-download")) {
- // 请求该歌曲的url
- console.log(row);
- let res = await this.$request("/song/url", { id: row.id });
- console.log(res.data.data[0].url);
- console.log(res);
- if (res.data.data[0].url == null) {
- this.$message.warning("暂时无法获取该资源哦!");
- return;
- }
-
- // 匹配资源的域名
- let url = res.data.data[0].url.match(/\http.*?\.net/);
- // 匹配域名名称,并匹配对应的代理
- let serve = url[0].match(/http:\/(\S*).music/)[1];
- if (
- serve != "/m7" &&
- serve != "/m701" &&
- serve != "/m8" &&
- serve != "/m801"
- ) {
- // 没有对应的代理
- this.$message.error("匹配不到对应的代理,下载失败!");
- return;
- }
- // 截取后面的参数
- let params = res.data.data[0].url.slice(url[0].length);
- // console.log(url[0], serve, params);
-
- let downloadMusicInfo = {
- url: serve + params,
- name:
- row.name +
- " - " +
- row.ar[0].name +
- "." +
- res.data.data[0].type.toLowerCase(),
- };
- console.log(downloadMusicInfo);
- this.$store.commit("updateDownloadMusicInfo", downloadMusicInfo);
- }
- },
-
- },
- computed: {},
- watch: {
- // "$store.state.currentIndex"(currentIndex, lastIndex) {
- // // 目前没什么好思路 直接操作原生DOM
- // console.log(currentIndex, lastIndex);
- // // this.handleTableDOM(currentIndex, lastIndex);
- // },
- "$store.state.musicId"(current, last) {
- this.handleDOM(current, last);
- },
- "$store.state.defaultPlay"(current, last) {
- console.info('defaultPlay2');
- this.playAll();
- },
- },
- filters: {
- showDate(value) {
- // 1、先将时间戳转成Date对象
- const date = new Date(value);
-
- // 2、将date进行格式化
- return formatDate(date, "yyyy-MM-dd");
- },
- handleNum,
- },
- created() {},
- async mounted() {
- await this.getMusicListDetail();
- this.$nextTick(() => {
- // 判断是否和上一次打开的歌单相同
- if (this.$route.params.id == this.$store.state.musicListId) {
- this.handleDOM(this.$store.state.musicId);
- }
- });
- },
- };
- script>
-
- <style scoped>
- .musicListDetail {
- overflow-y: scroll;
- }
-
- .listInfo {
- display: flex;
- padding: 25px 15px;
- align-items: center;
- }
-
- .listAvatar {
- width: 150px;
- height: 150px;
- overflow: hidden;
- border-radius: 10px;
- margin-right: 15px;
- position: relative;
- }
-
- .listAvatar::after {
- content: "";
- position: absolute;
- height: 100%;
- width: 100%;
- left: 0;
- top: 0;
- background: url("../../assets/img/imgLoading.png") no-repeat;
- background-size: contain;
- z-index: -1;
- }
-
- .listAvatar img {
- width: 100%;
- }
-
- .right {
- width: calc(100% - 200px);
- }
-
- .title {
- display: flex;
- align-items: center;
- }
-
- .titleTag {
- font-size: 12px;
- color: #ec4141;
- border: 1px solid #ec4141;
- padding: 1px 2px;
- border-radius: 2px;
- margin-right: 5px;
- transform: scale(0.8);
- }
-
- .titleContent {
- font-size: 20px;
- font-weight: 600;
- color: #373737;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- width: 90%;
- }
-
- .user {
- display: flex;
- align-items: center;
- margin-top: 8px;
- font-size: 12px;
- }
-
- .userAvatar {
- height: 25px;
- width: 25px;
- margin-right: 8px;
- }
-
- .userAvatar img {
- width: 100%;
- border-radius: 50%;
- }
-
- .userName {
- color: #6191c2;
- margin-right: 8px;
- cursor: pointer;
- }
-
- .createTime {
- transform: scale(0.9);
- }
-
- .buttons {
- margin: 8px 0 0 -5px;
- display: flex;
- }
-
- .buttonItem {
- font-size: 12px;
- padding: 8px 15px;
- border: 1px solid #ddd;
- border-radius: 20px;
- transform: scale(0.9);
- }
-
- .buttonItem i {
- font-size: 12px;
- margin-right: 3px;
- transform: scale(0.9);
- }
-
- .playAll {
- background-color: #ec4141;
- color: white;
- }
-
- .tags {
- margin: 8px 0 0 -30px;
- display: flex;
- font-size: 12px;
- transform: scale(0.9);
- }
-
- .tagItem {
- color: #6191c2;
- margin-right: 5px;
- }
-
- .otherInfo {
- margin: 5px 0 0 -30px;
- display: flex;
- font-size: 12px;
- transform: scale(0.9);
- }
-
- .musicNum {
- margin-right: 13px;
- }
-
- .desc {
- margin: 5px 0 0 -30px;
- font-size: 12px;
- transform: scale(0.9);
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
-
- .musicList {
- margin: -15px 15px 0;
- }
-
- .page {
- width: 100%;
- text-align: center;
- padding-bottom: 20px;
- }
-
- .placeholder {
- width: 100%;
- height: 50px;
- }
-
- .loadMore {
- width: 100%;
- height: 50px;
- font-size: 12px;
- color: #aaa;
- text-align: center;
- line-height: 50px;
- transform: scale(0.9);
- }
-
- .red {
- color: #ec4141;
- }
-
- .commentList /deep/ .el-loading-spinner {
- top: 40px;
- }
-
- .tips {
- font-size: 14px;
- margin: 30px 0;
- text-align: center;
- }
- style>
项目功能完整,后续可能将不断升级。
关注作者,及时了解更多好项目!
作者主页也有更多好项目分享!
获取源码或如需帮助,可通过博客后面名片+作者即可!
其他作品集合(主页更多):