vue仿写音乐App项目移动端(部分)

在这一个月期间跟着逆战班级做的一个基于vue的音乐App项目

项目目录结构如下:

在这里插入图片描述
public文件夹用于存放公用文件、src用于存放项目源代码
1、App组件代码

<template>
  <div id="app">
    <Header></Header>
    <Nav></Nav>
    <keep-alive>
      <router-view v-if="$route.meta.keepAlive"></router-view>
    </keep-alive>
  </div>
</template>

<script>
import Header from "./components/Header/Header";
import Nav from "./components/Nav/Nav";
export default {
  data() {
    return {};
  },
  components: {
    Header,
    Nav
  }
};
</script>
<style lang="less" scoped>
@import "./style/reset.css";
</style>

3、路由结构

import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)
import Recommend from '../components/Recommend';
import Singer from '../components/Singer/index.vue';
import Ranking from '../components/Ranking'
import Search from '../components/Search'
const router = new VueRouter({
    routes: [
        {
            path: '/recommend',
            component: Recommend,
            meta: {
                keepAlive: true
            }
        },
        {
            path: '/singer',
            component: Singer,
            meta: {
                keepAlive: true
            }
        },
        {
            path: '/ranking',
            component: Ranking,
            meta: {
                keepAlive: true
            }
        },
        {
            path: '/search',
            component: Search,
            meta: {
                keepAlive: true
            }
        },
        {
            path: '/',
            redirect: '/recommend',
            meta: {
                keepAlive: true
            }
        }
    ]
})

export default router


4、推荐页面(Recommend)代码如下(包含Content 、Swiper两个组件)
Recommend页面

<!--  -->
<template>
  <div class="wrapper">
    <div class="test">
      <Swiper></Swiper>
      <Content></Content>
    </div>
  </div>
</template>

<script>
import Swiper from "./Swiper";
import Content from "./recommend-cont";

import BScorll from "better-scroll";
export default {
  //import引入的组件需要注入到对象中才能使用
  components: {
    Swiper,
    Content
  },
  data() {
    //这里存放数据
    return {};
  },
  //监听属性 类似于data概念
  computed: {},
  //监控data中的数据变化
  watch: {},
  //方法集合
  methods: {
    init() {
      new BScorll(".wrapper", {});
    }
  },
  mounted() {
    this.init();
  }
};
</script>
<style scoped>
.wrapper {
  position: fixed;
  top: 120px;
  bottom: 0;
  left: 0;
  right: 0;
  overflow: hidden;
}
.wrapper .test {
  height: auto;
}
</style>

Content 组件

<template>
  <div class="cont_box">
    <h5>热门歌单推荐</h5>
    <div class="cont">
      <ul>
        <li v-for="item in list" :key="item.dissid">
          <div class="cont-left">
            <img v-lazy="item.imgurl" alt />
          </div>
          <div class="cont-right">
            <span class="con_title">{{item.creator.name}}</span>
            <p class="con_all">{{item.dissname}}</p>
          </div>
        </li>
      </ul>
    </div>
  </div>
</template>

<script>
export default {
  //import引入的组件需要注入到对象中才能使用
  components: {},
  data() {
    //这里存放数据
    return {
      list: []
    };
  },
  //监听属性 类似于data概念
  computed: {},
  //监控data中的数据变化
  watch: {},
  //方法集合
  methods: {},
  //生命周期 - 创建完成(可以访问当前this实例)
  created() {
    let url =
      "/api/getDiscList?g_tk=1928093487&inCharset=utf-8&outCharset=utf-8&notice=0&format=json&platform=yqq&hostUin=0&sin=0&ein=29&sortId=5&needNewCode=0&categoryId=10000000&rnd=0.7841041983308066";
    this.$axios.get(url).then(res => {
      this.list = res.data.list;
    });
  },

  mounted() {}
};
</script>
<style lang="less" scoped>
@import "../../style/mixin.less";
ul li {
  .w(375);
  height: 60px;
  padding: 13px;
  list-style: none;
}
.cont_box h5 {
  .w(375);
  height: 65px;
  text-align: center;
  line-height: 65px;
  color: #ffcd32;
  font-size: 14px;
}
.cont_box h3 {
  color: #fff;
}
.cont-left {
  float: left;
  height: 60px;
}
.cont_box img {
  width: 60px;
  height: 60px;
}
.cont-right {
  float: left;
  height: 60px;
}
.con_title {
  display: block;
  color: #fff;
  font-size: 14px;
  margin-top: 10px;
  margin-left: 20px;
}
.con_all {
  color: #888;
  font-size: 13px;
  margin-left: 20px;
  margin-top: 10px;
}
</style>

Swiper(轮播图组件),这里采用了swiper插件来实现(具体使用方法请访问https://www.swiper.com.cn/)

<template>
  <div id="swiper">
    <div class="swiper-container">
      <div class="swiper-wrapper">
        <div class="swiper-slide" v-for="(item,index) in imgSrc" :key="index">
          <img :src="item.picUrl" alt />
        </div>
      </div>
      <div class="swiper-pagination"></div>
    </div>
  </div>
</template>

<script>
import Swiper from "swiper";
export default {
  data() {
    return {
      imgSrc: []
    };
  },
  created() {
    let url =
      "/api/getTopBanner?g_tk=1928093487&inCharset=utf8&outCharset=utf-8&notice=0&format=json&platform=yqq.json&hostUin=0&needNewCode=0&-=recom7744651407995646&data=%7B%22comm%22:%7B%22ct%22:24%7D,%22category%22:%7B%22method%22:%22get_hot_category%22,%22param%22:%7B%22qq%22:%22%22%7D,%22module%22:%22music.web_category_svr%22%7D,%22recomPlaylist%22:%7B%22method%22:%22get_hot_recommend%22,%22param%22:%7B%22async%22:1,%22cmd%22:2%7D,%22module%22:%22playlist.HotRecommendServer%22%7D,%22playlist%22:%7B%22method%22:%22get_playlist_by_category%22,%22param%22:%7B%22id%22:8,%22curPage%22:1,%22size%22:40,%22order%22:5,%22titleid%22:8%7D,%22module%22:%22playlist.PlayListPlazaServer%22%7D,%22new_song%22:%7B%22module%22:%22newsong.NewSongServer%22,%22method%22:%22get_new_song_info%22,%22param%22:%7B%22type%22:5%7D%7D,%22new_album%22:%7B%22module%22:%22newalbum.NewAlbumServer%22,%22method%22:%22get_new_album_info%22,%22param%22:%7B%22area%22:1,%22sin%22:0,%22num%22:10%7D%7D,%22new_album_tag%22:%7B%22module%22:%22newalbum.NewAlbumServer%22,%22method%22:%22get_new_album_area%22,%22param%22:%7B%7D%7D,%22toplist%22:%7B%22module%22:%22musicToplist.ToplistInfoServer%22,%22method%22:%22GetAll%22,%22param%22:%7B%7D%7D,%22focus%22:%7B%22module%22:%22QQMusic.MusichallServer%22,%22method%22:%22GetFocus%22,%22param%22:%7B%7D%7D%7D";
    this.$axios.get(url).then(res => {
      this.imgSrc = res.data.slider;
      this.$nextTick(() => {
        this.init();
      });
    });
  },
  methods: {
    init() {
      new Swiper(".swiper-container", {
        direction: "horizontal",
        loop: true,
        autoplay: true,
        pagination: {
          el: ".swiper-pagination",
          bulletActiveClass: "my-bullet-active"
        }
      });
    }
  }
};
</script>

<style lang="less" scoped>
@import "~swiper/css/swiper.min.css";
@import "../../style/mixin.less";
.swiper-container {
  .w(375);
  .h(150);
}
.swiper-slide img {
  .w(375);
  .h(150);
}
.my-bullet-active {
  background: #fff;
  opacity: 0.6;
}
</style>

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

5、歌手页面(Singer)

这里的难点
1、首先处理后台返回的数据
2、实现滚动屏幕、右边的字母跟着滚动
3、点击右边的字母,左边歌手列表跳到对应的位置
4、点击字母不放手向上或向下滚动、左边对应跟着滚动

实现代码如下
<template>
  <div class="singer">
    <div class="wrapper" ref="wrapper">
      <div class="content">
        <ul>
          <li :ref="item.Findex" v-for="(item,index) in singerData" :key="index">
            <div class="name_type">{{item.Findex}}</div>
            <div class="singer_info" v-for="(singer_info,index) in item.list" :key="index">
              <img v-lazy="singer_info.imgsrc" alt />
              <div class="singer_name">{{singer_info.Fsinger_name}}</div>
            </div>
          </li>
        </ul>
      </div>
    </div>
    <div class="slider">
      <ul>
        <li
          @click="jump(item)"
          :class="state==item? 'slider-active' :''"
          v-for="(item,index) in slideData"
          :key="index"
        >{{item}}</li>
      </ul>
    </div>
  </div>
</template>

<script>
import { nomalData } from "./index.js";
import Bs from "better-scroll";
export default {
  //import引入的组件需要注入到对象中才能使用
  components: {},
  data() {
    //这里存放数据
    return {
      singerData: [],
      state: "hot"
    };
  },
  created() {
    // 发请求,这里没有用jsonp插件所以根据axios请求回来的数据进行处理
    let url =
      "/v8/fcg-bin/v8.fcg?g_tk=1928093487&inCharset=utf-8&outCharset=utf-8&notice=0&format=jsonp&channel=singer&page=list&key=all_all_all&pagesize=100&pagenum=1&hostUin=0&needNewCode=0&platform=yqq&jsonpCallback=jp0";
    this.$axios.get(url).then(res => {
      let str = res.replace(/jp0\(/g, "");
      let newdata = JSON.parse(str.replace(/\)/g, ""));
      newdata.data.list.forEach(el => {
        el.imgsrc = `https://y.gtimg.cn/music/photo_new/T001R300x300M000${el.Fsinger_mid}.jpg?max_age=2592000`;
      });
      let result = nomalData(newdata.data.list);
      // 把处理好的数据存进组件共有数据里
      this.singerData = result;
      this.$nextTick(() => {
        // 页面渲染好后初始化better-scroll
        this.init();
      });
    });
  },
  computed: {
    slideData() {
      let result = this.singerData.map(item => {
        return item.Findex;
      });
      return result;
    }
  },
  methods: {
    // 定义初始化better-scroll方法
    init() {
      this.bscroll = new Bs(this.$refs.wrapper, {
        probeType: 3,
        click: true, //点击
        pullUpLoad: true //上拉加载更多
      });
      let disposition = [];
      for (const key in this.$refs) {
        if (key != "wrapper") {
          disposition.push(this.$refs[key][0].offsetTop);
        }
      }
      // 监听滚动来实现对应滚动监听功能
      this.bscroll.on("scroll", position => {
        let pos = parseInt(position.y * -1);
        for (let index = 0; index < disposition.length; index++) {
          if (pos >= disposition[index] && pos <= disposition[index + 1]) {
            this.state = this.slideData[index];
          }
        }
      });
    },
    // 此方法用于点击后跳转到对应的地方
    jump(item) {
      let targe = this.$refs[item][0];
      console.log(targe);
      this.bscroll.scrollToElement(targe);
    }
  },
  mounted() {}
};
</script>
<style scoped>
.wrapper {
  position: fixed;
  top: 100px;
  bottom: 0;
  left: 0;
  right: 0;
  overflow: hidden;
}
.wrapper .content {
  margin-top: 20px;
}
.name_type {
  width: 100%;
  height: 21px;
  color: hsla(0, 0%, 100%, 0.5);
  background: #333;
  padding: 9px;
  font-size: 14px;
}
.singer_info {
  width: 100%;
  height: 50px;
  padding: 20px;
}
.singer_info img {
  height: 50px;
  width: 50px;
  border-radius: 50%;
  display: block;
  float: left;
}
.singer_name {
  float: left;
  color: hsla(0, 0%, 100%, 0.5);
  font-size: 13px;
  padding: 15px;
}
.slider {
  width: 20px;
  height: 466px;
  position: absolute;
  right: 5px;
  top: 150px;
  background: rgba(0, 0, 0, 0.3);
  border-radius: 7px;
}
.slider li {
  list-style: none;
  color: hsla(0, 0%, 100%, 0.5);
  font-size: 12px;
  padding-top: 4px;
  text-align: center;
}
.slider .slider-active {
  color: yellow;
}
</style>

效果图
在这里插入图片描述
在这里插入图片描述
6、排行页面(Ranking)

代码如下

<!--  -->
<template>
  <div class="wrapper">
    <div class="content">
      <div class="box" v-for="(item,index) in rankdata" :key="index">
        <div class="left">
          <img v-lazy="item.picUrl" alt />
        </div>
        <div class="right">
          <ul>
            <li
              v-for="(sing_info,index) in item.songList"
              :key="index"
            >{{index+1}}{{sing_info.songname}}-{{sing_info.singername}}</li>
          </ul>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import BScorll from "better-scroll";
export default {
  //import引入的组件需要注入到对象中才能使用
  components: {},
  data() {
    //这里存放数据
    return {
      rankdata: []
    };
  },
  //监听属性 类似于data概念
  computed: {},
  //监控data中的数据变化
  watch: {},
  //方法集合
  methods: {
    init() {
      new BScorll(".wrapper", {});
    }
  },
  //生命周期 - 创建完成(可以访问当前this实例)
  created() {
    let url =
      "/v8/fcg-bin/fcg_myqq_toplist.fcg?g_tk=1928093487&inCharset=utf-8&outCharset=utf-8&notice=0&format=jsonp&uin=0&needNewCode=1&platform=h5&jsonpCallback=jp0";
    this.$axios(url).then(res => {
      let str = res.replace(/jp0\(/g, "");
      let data = JSON.parse(str.slice(0, length - 1));
      this.rankdata = data.data.topList;
    });
  },
  mounted() {
    this.init();
  }
};
</script>

<style lang="less" scoped>
.box {
  width: 94%;
  height: 100px;
  margin-left: 10px;
  background: #333;
  margin-top: 20px;
}
ul li {
  color: hsla(0, 0%, 100%, 0.3);
  line-height: 22px;
  font-size: 12px;
  list-style: none;
  padding-left: 10px;
  margin-top: 5px;
  width: 155px;
  height: 22px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.wrapper {
  position: fixed;
  top: 130px;
  bottom: 0;
  left: 0;
  right: 0;
  overflow: hidden;
}
.wrapper .content {
  height: auto;
  margin-top: 20px;
}
.left {
  float: left;
  height: 100px;
}
.left img {
  width: 100px;
  height: 100px;
  display: block;
}
.right {
  height: 100px;
  width: 50%;
  overflow: hidden;
  float: left;
}
.right ul {
  height: 100px;
}
</style>

效果图
在这里插入图片描述
7、搜索页面(Search)

代码如下

<!--  -->
<template>
  <div class="search">
    <div class="input_box">
      <input type="text" v-model="word" placeholder="搜索歌声、歌曲" />
      <div class="icon"></div>
    </div>
    <img
      class="loading"
      v-if="loading"
      src="data:image/gif;base64,R0lGODlhZABkAKIEAN7e3rq6uv///5mZmQAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDpBRjA4RUZDMDI3MjA2ODExODA4M0Y1OTQyMzVDRDM3MyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpCMzE0Rjk3NDdDRTgxMUUzOUJCRjk0NjAxMUE1NzRBMCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpCMzE0Rjk3MzdDRTgxMUUzOUJCRjk0NjAxMUE1NzRBMCIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M2IChNYWNpbnRvc2gpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6RDVBMTZDQjczOTIwNjgxMTgwODNGNTk0MjM1Q0QzNzMiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6QUYwOEVGQzAyNzIwNjgxMTgwODNGNTk0MjM1Q0QzNzMiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+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"
      alt
    />
    <div v-show="searched" class="searched">
      <div class="wrapper" ref="wrapper">
        <div class="content">
          <span
            v-for="(searchitem,index) in list"
            :key="index"
          >{{searchitem.songname}}-{{searchitem.singer[0].name}}</span>
        </div>
      </div>
    </div>
    <div v-show="keyword" class="keyword">
      <span class="search_title">热门搜索</span>
      <ul>
        <li v-for="(item,index) in keywordData.slice(0, 15)" :key="index">{{item.k}}</li>
      </ul>
    </div>
  </div>
</template>

<script>
import Bs from "better-scroll";
export default {
  //import引入的组件需要注入到对象中才能使用
  components: {},
  data() {
    //这里存放数据
    return {
      keywordData: [],
      word: "",
      loading: false,
      keyword: true,
      searched: false,
      list: []
    };
  },
  created() {
    //https://c.y.qq.com/splcloud/fcgi-bin/gethotkey.fcg?g_tk=1928093487&inCharset=utf-8&outCharset=utf-8&notice=0&format=jsonp&uin=0&needNewCode=1&platform=h5&jsonpCallback=jp0
    let url =
      "/splcloud/fcgi-bin/gethotkey.fcg?g_tk=1928093487&inCharset=utf-8&outCharset=utf-8&notice=0&format=jsonp&uin=0&needNewCode=1&platform=h5&jsonpCallback=jp0";
    this.$axios.get(url).then(res => {
      let str = res.replace(/jp0\(/g, "");
      let data = JSON.parse(str.slice(0, length - 1));
      this.keywordData = data.data.hotkey;
      this.$nextTick(() => {
        this.init();
      });
    });
  },
  methods: {
    init() {
      new Bs(this.$refs.wrapper, {});
    }
  },
  watch: {
    word(val) {
      this.searched = true;
      if (val == "") {
        this.searched = false;
        this.keyword = true;
        this.loading = false;
        return;
      } else {
        this.keyword = false;
        this.loading = true;
        this.$axios
          .get(
            `/search?g_tk=1928093487&inCharset=utf-8&outCharset=utf-8&notice=0&format=json&w=${val}&p=1&perpage=20&n=20&catZhida=1&zhidaqu=1&t=0&flag=1&ie=utf-8&sem=1&aggr=0&remoteplace=txt.mqq.all&uin=0&needNewCode=1&platform=h5`
          )
          .then(res => {
            this.list = [];
            res.data.song.list.forEach(item => {
              if (item.songname.indexOf(val) != -1) {
                this.list.push(item);
              }
            });
            this.loading = false;
          });
      }
    }
  }
};
</script>
<style lang="less" scoped>
@import "../../style/mixin.less";
.search {
  .w(375);
  height: 100%;
}
.input_box {
  .w(337);
  height: 40px;
  margin: 20px auto;
  position: relative;
  input {
    width: 100%;
    height: 35px;
    padding: 5px;
    background: #333;
    border: 1px solid #222;
    border-radius: 10px;
    color: #fff;
    text-indent: 2em;
  }
  .icon {
    width: 25px;
    height: 25px;
    background: url("../../../public/search.png") no-repeat;
    position: absolute;
    top: 20px;
  }
}
input:focus {
  outline: none;
}

.search_title {
  color: hsla(0, 0%, 100%, 0.5);
  font-size: 14px;
  width: 100%;
  text-indent: 1.5em;
}
.keyword {
  width: 100%;
  padding: 20px;
}
.keyword li {
  display: block;
  margin-left: 9px;
  background: #333;
  float: left;
  padding: 6px;
  border-radius: 5px;
  color: hsla(0, 0%, 100%, 0.2);
  margin-top: 20px;
  font-size: 14px;
}
.loading {
  width: 30px;
  height: 30px;
  margin-left: 45%;
  margin-top: 20px;
}
.wrapper {
  position: fixed;
  top: 160px;
  bottom: 0;
  left: 0;
  right: 0;
  overflow: hidden;
  .content {
    height: auto;
    margin-top: 40px;
    padding: 20px;
    overflow: hidden;
    span {
      display: block;
      width: 100%;
      height: 30px;
      padding: 5px;
      margin-top: 5px;
      font-size: 16px;
      color: hsla(0, 0%, 100%, 0.5);
    }
  }
}
</style>

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

这个项目用到的主要技术栈为(vue、vue-cli、axios、vue-router、better-scroll插件、Swiper插件)
未完成的部分我将在下一篇文章继续完善

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值