Vue移动端

06.vue.2.x开发音乐App-歌手详情页

2018-06-13  本文已影响128人  Ching_Lee

github:https://github.com/Ching-Lee/vue-music

1.子路由配置

  {
      path: '/singer',
      component: Singer,
      children: [
        {
          path: ':singer_id',
          component: SingerDetail
        }
      ]
    },
<li v-for="(item, index) in (value)" v-bind:key="index" class="singer_item" @click="selectItem(item)">
      <img v-lazy="item.singer_pic" class="singerPic">
      <span class="singer_name">{{item.name}}</span>
</li>
 // 点击歌手,跳转到该歌手的歌单页面
    // 这里派发了事件
    selectItem (item) {
      this.$emit('select', item)
    }
<div class="singer" v-if="singerlist.length">
      <listview  v-bind:data=" _singerCountryMap()" @select="selectSinger"></listview>
      <router-view></router-view>
</div>

// 手动跳转

 selectSinger (singer) {
      this.$router.push({
        path: `/singer/${singer.singer_id}`
      })
    }
 .singer_detail{
    z-index: 2;
    position:fixed;
    top:0;
    left: 0;
    right: 0;
    bottom: 0;
    background-color: white;
  }

2.做渐入渐出的动画特效

<template>
  <transition name="slide">
    <div class="singer_detail">
      <span>歌手详情页</span>
    </div>
  </transition>
</template>
.slide-enter-active, .slide-leave-active {
    transition: all .3s;
  }
  .slide-enter, .slide-leave-to /* .fade-leave-active below version 2.1.8 */
  {
    transform: translate3d(100%, 0, 0);
  }

点击的时候会有滑屏的效果。

3.使用qq音乐后台数据


这里每次请求15个数据,请求参数中有begin表示请求开始的索引,num是15表示一次15个。
请求参数中有singerid,通过singerid返回不同歌手的信息。
音乐信息在list中。


import jsonp from '../assets/js/jsonp'
import {commonParams, options} from './config'

// 获取歌手详情页面相关数据
export function getSingerDetail (singerid) {
  const url = 'https://c.y.qq.com/v8/fcg-bin/fcg_v8_singer_track_cp.fcg'
  const param = Object.assign({}, commonParams, {
    singerid: singerid,
    uin: 0,
    format: 'json',
    platform: 'h5page',
    needNewCode: 1,
    order: 'listen',
    from: 'h5',
    num: 15,
    begin: 0,
    _: 1528443902453
  })
  return jsonp(url, param, options)
}

export default {
  data () {
    return {
      singerDetail: null
    }
  },
  computed: {
    getFans () {
      return `粉丝:${(this.singerDetail.fans / 10000).toFixed(2)}万人`
    },
    getImage () {
      return `http://y.gtimg.cn/music/photo_new/T001R150x150M000${this.singerDetail.singer_mid}.jpg?max_age=2592000`
    }
  },
  created () {
    this._getSinerDetailbyid(this.$route.params.singer_id)
  },
  methods: {
    _getSinerDetailbyid (singerid) {
      getSingerDetail(singerid).then((result) => {
        if (result.code === ERR_OK) {
          this.singerDetail = result.data
        }
      })
    }
  },
}

4.头部组件开发singer_detail_header


布局是左边图片div,右边介绍div,下面按钮div

<template>
  <div class="detail_top" ref="detailtop">
    <div class="detail_header clearfix">
      <div class="detail_left">
        <img v-bind:src=image />
      </div>
      <div class="detail_right">
        <h2>{{name}}</h2>
        <P class="fans">{{fans}}</P>
        <p class="intruduce">{{intruduce}}</p>
      </div>
    </div>
    <div class="bottom_detail">
      <div class="button">
        <i class="icon-player"></i>
        <span>播放全部</span>
      </div>
    </div>
  </div>
</template>

默认属性:

props: {
    image: {
      type: String,
      default: ''
    },
    name: {
      type: String,
      default: ''
    },
    fans: {
      type: String,
      default: ''
    },
    intruduce: {
      type: String,
      default: ''
    }
  },

css样式:
图片和介绍:是一个左边固定右边自适应的布局。

左边固定右边自适应的实现方法:
1)左边float:left,就会脱离文档流,然后解决高度塌陷的问题
2)position:absolute,使用绝对定位,左边的left:0,右边的right:0
3 ) flex布局,整体的盒子display:flex,右边flex:1
4 ) grid布局,display:grid,template-grid-column:左边宽度 auto
5)table布局,容器宽度设置为100%,左右设置为display:table-cell
这里使用float实现

<style>
  .detail_header {
    background-color:rgba(0, 0, 0, 0.6);
    padding: 1rem;
  }

  .detail_left {
    float: left;
    width: 150px;
    margin-right: 0.5rem;
  }

  .clearfix:after {
    clear: both;
    content: '';
    display: table;
  }

  .detail_right h2 {
    font-size: 1.2rem;
    margin-top: 1rem;
    color: white;
  }

  .detail_right .fans {
    font-size: 1rem;
    margin: 1rem 0;
    color: whitesmoke;
  }

/*设置三行内容,超出...*/
  .detail_right .intruduce {
    white-space: initial;
    font-size: 0.8rem;
    line-height: 1.2rem;
    color: whitesmoke;
    height:3.6rem;
    overflow: hidden;
    position: relative;
  }
  .detail_right .intruduce:after{
    content:'...';
    font-weight: bold;
    position: absolute;
    bottom: 0;
    right: 0;
  }

  .bottom_detail {
    background-color:rgba(0, 0, 0, 0.6);
    padding-bottom: 1rem;
    text-align: center;
  }
  .button{
    display: inline-block;
    background-color: rgba(0, 0, 0, 0);
    border-radius: 3rem;
    padding: 0.4rem 1.2rem;
    color: white;
    border: 1px orange solid;
  }
/*怎么让图片和文字对齐*/
  .button *{
    display:inline-block;
    vertical-align:middle
  }
  .button i{
    font-size: 1.5rem;
    color: orange;
  }

</style>

怎么让图片和文字对齐
父元素里的所有元素设置成内敛块元素,然后居中
.button *{
display:inline-block;
vertical-align:middle
}

怎么设置三行内容,多余的超出:
通过设置行高+绝对定位
/设置三行内容,超出.../
.detail_right .intruduce {
line-height: 1.2rem;
/height是line-height的3倍/
height:3.6rem;
overflow: hidden;
position: relative;
}
.detail_right .intruduce:after{
content:'...';
font-weight: bold;
position: absolute;
bottom: 0;
right: 0;
}

 mounted () {
    this._setBackgroundPic()
  },
  methods: {
    _setBackgroundPic () {
      let target = this.$refs.detailtop
      target.style.backgroundImage = `url(${this.image})`
      target.style.backgroundSize = '100% auto'
    }
  }
<template>
  <transition name="slide">
      <div class="singer_detail" v-if="singerDetail" >
        <singer-detail-header v-bind:image=getImage v-bind:name="singerDetail.singer_name" v-bind:fans="getFans" v-bind:intruduce="singerDetail.SingerDesc"></singer-detail-header>
        <p class="total_song">歌曲 共{{singerDetail.total}}首</p>
      </div>
  </transition>
</template>

5.music-list组件开发


<template>
  <div>
    <ul class="music_list">
      <li v-for="(item, index) in list" v-bind:key="index">
        <h2>{{item.musicData.songname}}</h2>
        <p>{{item.musicData.singer[0].name}}   {{item.musicData.albumdesc}}</p>
      </li>
    </ul>
  </div>
</template>

<script type="text/ecmascript-6">
export default{
  props: {
    list: {
      type: Array,
      default: null
    }
  }
}
</script>

<style>
  .music_list li{
    padding: 1rem;
    border-bottom: 1px solid whitesmoke;
  }
  .music_list li h2{
    font-size: 1rem;
  }
  .music_list li p{
    font-size: 0.8rem;
    margin-top: 10px;
    color: orange;
    line-height: 1rem;
  }
</style>

6.让music-list组件能够滑动

使用vue-iscroll-view,参考文档
https://dafrok.github.io/vue-iscroll-view/

import IScrollView from 'vue-iscroll-view'
import IScroll from 'iscroll'
Vue.use(IScrollView, IScroll)
<template>
  <iscroll-view ref='scrollView' class='scroll_view' :options="{preventDefault: true}">
    <ul class="music_list">
      <li v-for="(item, index) in list" v-bind:key="index">
        <h2>{{item.musicData.songname}}</h2>
        <p>{{item.musicData.singer[0].name}}   {{item.musicData.albumdesc}}</p>
      </li>
    </ul>
  </iscroll-view>
</template>
 mounted () {
    this.$refs.scrollView.refresh()
  }
 .scroll_view{
    touch-action:none;
    position: fixed;
    left: 0;
    right: 0;
    top: 280px;
    bottom: 0;
    overflow: hidden;
  }

7.加载更多组件

export default {
  data () {
    return {
      singerDetail: null,
      begin: 0,
      isLoading: false,
      list: [],
      number: 0, // number表示已经加载的数据数量,当大于全部数量时,hasMore就是false
      hasMore: true, // 表示是否还有数据
      showMoreDec: false
    }
  },
methods: {
    _getSinerDetailbyid (singerid, begin) {
      console.log(singerid)
      getSingerDetail(singerid, begin).then((result) => {
        if (result.code === ERR_OK) {
          this.singerDetail = result.data
          //在用push方法时报错
          this.list = this.list.concat(result.data.list)
          this.number = this.number + result.data.list.length
          if (this.number >= result.data.total) {
            this.hasMore = false
          }
          this.isLoading = false
        }
      })
    },
    _loadFn () {
      if (this.hasMore) {
        this.isLoading = true
        this.begin = this.begin + 15
        this._getSinerDetailbyid(this.$route.params.singer_id, this.begin)
      }
    },
<music-list v-bind:list="list" @loadmore="_loadFn" v-bind:hasMore="hasMore"></music-list>

misiclist中调用load-more组件,完成分发

 <load-more @loadmore="dispachload()" v-bind:isLoading="isLoading" v-bind:hasMore="hasMore"></load-more>
export default {
  props: {
    list: {
      type: Array,
      default: null
    },
    isLoading: {
      type: Boolean,
      default: false
    },
    hasMore: {
      type: Boolean,
      default: true
    }
  },
 components: {
    'load-more': LoadMore
  },
  methods: {
    dispachload () {
      this.$emit('loadmore')
    },
<template>
  <div v-if='hasMore' ref="loaddiv" v-show="!isLoading" v-on:click="dispachload()" class="loaddiv">上拉加载更多</div>
  <div class="loaddiv" v-else>我是有底线的</div>
</template>

<script type="text/ecmascript-6">
export default{
  props: {
    isLoading: {
      type: Boolean,
      default: false
    },
    hasMore: {
      type: Boolean,
      default: true
    }
  },
  methods: {
    dispachload () {
      this.$emit('loadmore')
    }
  }
}

</script>

<style>
 .loaddiv{
    text-align: center;
    line-height: 1rem;
  }
</style>

 watch: {
    list: function () {
      this.$refs.scrollView.refresh()
    }
  },
 methods: {
    dispachload () {
      this.$emit('loadmore')
    },
    /* pullDown () {
      console.log('pullDown')
    }, */
    pullUp () {
      this.dispachload()
    }
  }

8.弹出歌手详细介绍的模态框


一个大的div,设置成绝对定位,top:0,left:0,right:0,bottom:0,设置背景颜色和透明度
里面包裹着一个中间显示的div,设置背景为白色。

<template>
  <transition name="slide">
      <div class="singer_detail" v-if="singerDetail" >
        <singer-detail-header @alert="_alertfn" v-bind:image=getImage v-bind:name="singerDetail.singer_name" v-bind:fans="getFans" v-bind:intruduce="singerDetail.SingerDesc"></singer-detail-header>
        <p class="total_song">歌曲 共{{singerDetail.total}}首</p>
        <music-list v-bind:list="list" @loadmore="_loadFn" v-bind:hasMore="hasMore"></music-list>
        <div class="back" v-show="showMoreDec">
          <div id="moredec">
            <div id="dectext">{{singerDetail.SingerDesc}}</div>
            <div id="closebutton" v-on:click="_closefn()">关闭</div>
          </div>
        </div>
      </div>
  </transition>
</template>

通过属性showMoreDec控制显示与否,当点击singer-detail-header组件中的介绍时,触发分发alert事件给父组件sing_detail,在父组件中改变属性showMoreDec值为true

 data () {
showMoreDec: false
}
 methods: {
 _alertfn () {
      this.showMoreDec = true
    },
    _closefn () {
      this.showMoreDec = false
    }
}
上一篇 下一篇

猜你喜欢

热点阅读