github:https://github.com/Ching-Lee/vue-music
1.子路由配置
-
创建详情页组件
- 在rooter/index.js中添加子路由
{
path: '/singer',
component: Singer,
children: [
{
path: ':singer_id',
component: SingerDetail
}
]
},
- listView是基础组件,我们为每个li点击添加事件派发
<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)
}
- 在singer.vue中注册
<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}`
})
}
- 跳转发现看不到组件内容,去设置样式
这里的z-index要比头部的大,才能盖住头部。
.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中。
-
在api添加歌手详情页获取后台信息的部分
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)
}
- 在singer_detail.vue中获取数据,赋值给singerDetail
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/
-
首先安装
- 在main.js中use
import IScrollView from 'vue-iscroll-view'
import IScroll from 'iscroll'
Vue.use(IScrollView, IScroll)
- music.list中添加
<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.加载更多组件
- 实现loadMore功能
hasmore属性:表示是否还有数据
number属性:number表示已经加载的数据数量,当大于全部数量时,hasMore就是false
list:存储数据的列表
isLoading: 正在加载
begin: 发起请求时的请求参数,开始的索引
singer_detail中添加更改属性和方法
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)
}
},
- 调用musiclist的时候传递属性
@loadmore是接收触发
<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')
},
- 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>
- 这时候会发现点击时,请求到了新的数据,但是滚动条不能向下滚动。
我们需要在music_list中添加对list列表改变进行watch
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: {16
_alertfn () {
this.showMoreDec = true
},
_closefn () {
this.showMoreDec = false
}
}