<template>
<transition name="mine-backtop">
<a href="javascript:;" class="mine-backtop" v-show="visible" @click="backtotop">
<i class="iconfont icon-backtop"></i>
</a>
</transition>
</template>
<script>
export default {
name:"mebacktop",
props:{
visible:{
type:boolean,
default:false
}
},
methods:{
backtotop(){
this.$emit("backtop");//基础组件,与业务无关,具体实现去页面里
}
}
}
</script>
<style lang="scss" scoped>
@import '~assets/scss/mixins';
.mine-backtop{
overflow:hidden;
@include flex-center();
width:45px;
height:45px;
background:rgba(0,0,0,.6);
border:none;
border-radius:50%;
}
.iconfont{
color:#fff;
font-size:38px;
}
.mine-backtop{
&-enter-active,
&-leave-active{
transition:opacity 0.4s;
}
&-enter,
&-leave-to{
opacity:0;
}
}
</style>
<template>
<div class="home">
<header class="g-header-container">
<!-- 没有内容自闭合即可-->
<home-header/>
</header>
<!-- 滚动条接收到数据后开始更新 -->
<!-- pulldown是布尔值,可以使用简写直接传入,不加冒号 -->
<!-- 接收到pull-down消息后,触发pulltorefresh方法 -->
<me-scroll :data="recommends" pulldown pullup @pull-down="pulltorefresh" @pull-up="pulltoloadmore" @scroll-end="scrollend" ref="scroll">
<home-slider ref="slider" />
<home-nav />
<!-- 接收热门推荐加载完毕的消息 -->
<home-recommend @loaded="getrecommends" ref="recommend" />
</me-scroll>
<div class="g-backup-container">
<me-backtop :visible="isbacktopvisible" @backtop="backtotop" />
</div>
<!-- 当前页面存在二级页面时需要使用router-view -->
<router-view></router-view>
</div>
</template>
<script>
import mescroll from 'base/scroll';
import homeheader from './header';
import homeslider from './slider';
import homenav from './nav';
import homerecommend from './recommend';
import mebacktop from 'base/backtop';
export default {
name:"home",
components:{
homeheader,
homeslider,
mescroll,
homenav,
homerecommend,
mebacktop
},
data(){
return{
recommends:[],
isbacktopvisible:false
}
},
methods:{
getrecommends(recommends){
this.recommends=recommends;
},
updatescroll(){
},
pulltorefresh(end){
this.$refs.slider.update().then(end);
},
pulltoloadmore(end){
this.$refs.recommend.update().then(end).catch(err=>{
//没有更多内容时
if(err){
console.log(err);
}
end();
//禁止继续加载更多数据
//替换上拉时的loading,改为“没有更多数据了”
});
},
scrollend(translate,scroll){
// translate<0向下拉
// -translate>scroll.height拉过的距离大于一屏的高度
this.isbacktopvisible=translate<0 && -translate>scroll.height;
},
backtotop(){
this.$refs.scroll && this.$refs.scroll.scrolltotop();
}
}
}
</script>
<style lang="scss" scoped>
// 引入前面需要加波浪线,否则会报错
@import "~assets/scss/mixins";
.home{
overflow:hidden;
width:100%;
height:100%;
background:$bgc-theme;
}
</style>
<template>
<div>
<me-navbar class="header" v-show="visible">
<i class="iconfont icon-scan" slot="left"></i>
<div slot="center">搜索框</div>
<i class="iconfont icon-msg" slot="right"></i>
</me-navbar>
</div>
</template>
<script>
import menavbar from 'base/navbar';
export default {
name:"homeheader",
components:{
menavbar
},
data(){
return{
visible:true
}
},
methods:{
show(){
this.visible=true;
},
hide(){
this.visible=false;
}
}
}
</script>
<style lang="scss" scoped>
// 引入前面需要加波浪线,否则会报错
@import "~assets/scss/mixins";
.header{
&.mine-navbar{
background:transparent;
transition:background-color 0.5s;
}
.iconfont{
font-size:$icon-font-size;
color:$icon-color-default;
}
}
.header-transition .header.mine-navbar{
background-color:$header-bgc-translucent;
}
</style>
<template>
<!-- wiper会实例化构造函数,生成swiper实例 -->
<!-- ref="swiper"能够获取到这个swiper实例 -->
<swiper :options="swiperoption" ref='swiper'>
<div class="mine-scroll-pull-down" v-if="pulldown">
<!-- ref="pulldownloading" -- 获取下拉的loading -->
<me-loading :text="pulldowntext" inline ref="pulldownloading" />
</div>
<swiper-slide>
<slot></slot>
</swiper-slide>
<div class="mine-scroll-pull-up" v-if="pullup">
<!-- ref="pulluploading" -- 获取上拉的loading -->
<me-loading :text="pulluptext" inline ref="pulluploading" />
</div>
<div class="swiper-scrollbar" v-if="scrollbar" slot="scrollbar"></div>
</swiper>
</template>
<script>
// 组件首字母大写,否则会报错
import {swiper,swiperslide} from 'vue-awesome-swiper';
import meloading from 'base/loading';
import {
pull_down_height,
pull_down_text_init,
pull_down_text_start,
pull_down_text_ing,
pull_down_text_end,
pull_up_height,
pull_up_text_init,
pull_up_text_start,
pull_up_text_ing,
pull_up_text_end
} from './config';
export default {
name:"mescroll",
components:{
swiper,
swiperslide,
meloading
},
props:{//过滤器
scrollbar:{
type:boolean,
default:true
},
data:{//热门推荐加载完成后传递过来的recommends数据
type:[array,object]
},
pulldown:{//是真就开启下拉刷新
type:boolean,
default:false
},
pullup:{//是真就开启下拉加载
type:boolean,
default:false
}
},
methods:{
update(){//不知道怎么写就去swiper官网查api
//console.log(this.$refs.swiper);//打印swiper实例
this.$refs.swiper && this.$refs.swiper.$swiper.update();//调用swiper.update()更新滚动条
},
scrolltotop(speed,runcallback){
// slideto回到第x张幻灯片,swiper的api提供的
this.$refs.swiper && this.$refs.swiper.$swiper.slideto(0,speed,runcallback);
},
init(){
//将不需要设置getter和setter的数据,放在init中初始化即可
this.pulling=false;//是否正在下拉
this.pulldowntext=pull_down_text_init;//设置下拉初始化文字
this.pulluptext=pull_up_text_init;//设置上拉初始化文字
this.swiperoption={
direction:'vertical',//垂直方向
slidesperview:'auto',//一次显示几张
freemode:true,//任意滑动多少距离
setwrappersize:true,//根据内容设置容器尺寸
scrollbar:{
el:this.scrollbar?'.swiper-scrollbar':null,
hide:true //滚动条自动隐藏
},
on:{
//swiper配置时会触发slidermove方法,这里调用时执行自定义的scroll方法
slidermove:this.scroll,
touchend:this.touchend,//touchend是swiper提供的滚动结束的函数,this.touchend是我们自己写的函数,
transitionend:this.scrollend
}
};
},
scroll(){
const swiper=this.$refs.swiper.$swiper;
//滚动时触发scroll事件
this.$emit("scroll",swiper.translate,this.$refs.swiper.$swiper);
//如果正在下拉中,不会再次执行
if(this.pulling) return;
//console.log(swiper.translate);//打印出滚动条滚过的距离
if(swiper.translate>0){//下拉
if(!this.pulldown){//如果不需要下拉刷新
return;
}
if(swiper.translate>pull_down_height){
this.$refs.pulldownloading.settext(pull_down_text_start);
}else{
this.$refs.pulldownloading.settext(pull_down_text_init);
}
}else if(swiper.isend){//上拉
if(!this.pullup){
return;
}
//是否达到上拉的触发条件
//swiper的位移加上swiper的高度(617px)-50px的值如果大于当前内容高度
//swiper.translate这个属性可以获取到wrapper的位移,其实可以理解为滚动条滚动的距离
//swiper.height这个属性获取swiper容器的高度, 也就是显示区域的高度
//pull_up_height是我们设置的一个值。为了让页面不是到达最低部的时候,可以提前加载内容
//parseint(swiper.$wrapperel.css('height'))是wrapper的html元素的height属性, 也就是所有内容的高度
const ispullup=math.abs(swiper.translate)+swiper.height-pull_up_height>parseint(swiper.$wrapperel.css('height'));
if(ispullup){//开始上拉
this.$refs.pulluploading.settext(pull_up_text_start);
}else{//保持初始化
this.$refs.pulluploading.settext(pull_up_text_init);
}
}
},
scrollend(){
this.$emit("scroll-end",this.$refs.swiper.$swiper.translate,this.$refs.swiper.$swiper,this.pulling);
},
touchend(){
const swiper=this.$refs.swiper.$swiper;
//如果正在下拉中,不会再次执行
if(this.pulling) return;
if(swiper.translate>pull_down_height){//如果距离大于设定的距离
if(!this.pulldown){//如果不需要下拉刷新
return;
}
this.pulling=true;
swiper.allowtouchmove=false;//禁止触摸
swiper.settransition(swiper.params.speed);//设置初始速度
swiper.settranslate(pull_down_height);//移动到设定的位置(拖动过度时回到设置的位置)
swiper.params.virtualtranslate=true;//定住不给回弹
this.$refs.pulldownloading.settext(pull_down_text_ing);//设置正在刷新中的文字
this.$emit("pull-down",this.pulldownend);//触发消息,传递结束下拉的函数
}else if(swiper.isend){//上拉
//是否达到上拉的触发条件
const ispullup=math.abs(swiper.translate)+swiper.height-pull_up_height>parseint(swiper.$wrapperel.css('height'));
if(ispullup){//开始上拉
if(!this.pullup){//如果不需要上拉刷新
return;
}
this.pulling=true;
swiper.allowtouchmove=false;//禁止触摸
swiper.settransition(swiper.params.speed);//设置初始速度
swiper.settranslate(-(parseint(swiper.$wrapperel.css('height'))+pull_up_height-swiper.height));//超过拉动距离时回弹
swiper.params.virtualtranslate=true;//定住不给回弹
this.$refs.pulluploading.settext(pull_up_text_ing);//设置正在刷新中的文字
this.$emit("pull-up",this.pullupend);//触发消息,传递结束下拉的函数
}
}
},
pulldownend(){
const swiper=this.$refs.swiper.$swiper;
this.pulling=false;
this.$refs.pulldownloading.settext(pull_down_text_end);//设置加载结束后的文字
swiper.allowtouchmove=true;//可以触摸
swiper.settransition(swiper.params.speed);//设置初始速度
swiper.params.virtualtranslate=false;//可以回弹
swiper.settranslate(0);//移动到最初的位置
//下拉完成后,显示head组件(下拉过程中会被隐藏)
settimeout(()=>{
this.$emit("pull-down-transition-end");
},swiper.params.speed);
},
pullupend(){
const swiper=this.$refs.swiper.$swiper;
this.pulling=false;
this.$refs.pulluploading.settext(pull_up_text_end);//设置加载结束后的文字
swiper.allowtouchmove=true;//可以触摸
swiper.params.virtualtranslate=false;//可以回弹
}
},
watch:{//检测数据变化的事件
data(){
this.update();//data数据变化时执行update函数
}
},
created(){//在created中初始化数据
this.init();
}
}
</script>
<style lang="scss" scoped>
@import '~assets/scss/mixins';
.swiper-container{
width:100%;
height:100%;
overflow:hidden;
& .swiper-slide{
height:auto;
}
}
//默认是不显示的
.mine-scroll-pull-down{
position:absolute;
left:0;
bottom:100%;
width:100%;
height:80px;
}
.mine-scroll-pull-up{
position:absolute;
left:0;
top:100%;
width:100%;
height:30px;
}
</style>
<template>
<div class="home">
<header class="g-header-container">
<!-- 没有内容自闭合即可-->
<!-- ref用来获取到组件 -->
<home-header :class="{'header-transition':isheadertransition}" ref="header" />
</header>
<!-- 滚动条接收到数据后开始更新 -->
<!-- pulldown是布尔值,可以使用简写直接传入,不加冒号 -->
<!-- 接收到pull-down消息后,触发pulltorefresh方法 -->
<me-scroll :data="recommends" pulldown pullup @pull-down="pulltorefresh" @pull-up="pulltoloadmore" @scroll="scroll" @scroll-end="scrollend" @pull-down-transition-end="pulldowntransitionend" ref="scroll">
<home-slider ref="slider" />
<home-nav />
<!-- 接收热门推荐加载完毕的消息 -->
<home-recommend @loaded="getrecommends" ref="recommend" />
</me-scroll>
<div class="g-backup-container">
<me-backtop :visible="isbacktopvisible" @backtop="backtotop" />
</div>
<!-- 当前页面存在二级页面时需要使用router-view -->
<router-view></router-view>
</div>
</template>
<script>
import mescroll from 'base/scroll';
import homeheader from './header';
import homeslider from './slider';
import homenav from './nav';
import homerecommend from './recommend';
import mebacktop from 'base/backtop';
import {header_transition_height} from './config';
export default {
name:"home",
components:{
homeheader,
homeslider,
mescroll,
homenav,
homerecommend,
mebacktop
},
data(){
return{
recommends:[],
isbacktopvisible:false,
isheadertransition:false
}
},
methods:{
getrecommends(recommends){
this.recommends=recommends;
},
updatescroll(){
},
pulltorefresh(end){
this.$refs.slider.update().then(end);
},
pulltoloadmore(end){
this.$refs.recommend.update().then(end).catch(err=>{
//没有更多内容时
if(err){
console.log(err);
}
end();
//禁止继续加载更多数据
//替换上拉时的loading,改为“没有更多数据了”
});
},
scroll(translate){
this.changeheaderstatus(translate);
},
scrollend(translate,scroll,pulling){
if(!pulling){
this.changeheaderstatus(translate);
}
// translate<0向下拉
// -translate>scroll.height拉过的距离大于一屏的高度
this.isbacktopvisible=translate<0 && -translate>scroll.height;
},
backtotop(){
this.$refs.scroll && this.$refs.scroll.scrolltotop();
},
changeheaderstatus(translate){
if(translate>0){//上拉
this.$refs.header.hide();
return;
}
this.$refs.header.show();
this.isheadertransition=-translate>header_transition_height;
},
pulldowntransitionend(){//下拉完成之后显示导航条
this.$refs.header.show();
}
}
}
</script>
<style lang="scss" scoped>
// 引入前面需要加波浪线,否则会报错
@import "~assets/scss/mixins";
.home{
overflow:hidden;
width:100%;
height:100%;
background:$bgc-theme;
}
</style>
import axios from 'axios';
import {succ_code,timeout,home_recommend_page_size,jsonp_options} from './config';
import jsonp from 'assets/js/jsonp';
// shuffle打乱数组顺序的方法
const shuffle=(arr)=>{
const arrlength=arr.length;
let i=arrlength;
let rndnum;
while(i--){
//如果当前索引不等于随机数索引,则交换这两个索引的位置
if(i!==(rndnum=math.floor(math.random()*arrlength))){
//这是一种新的交换写法 es6解构
[arr[i],arr[rndnum]]=[arr[rndnum],arr[i]];
}
}
return arr;
}
//获取幻灯片数据 ajax
export const gethomesliders=()=>{
//演示超时错误
return axios.get('http://www.imooc.com/api/home/slider',{
timeout:timeout
}).then(res=>{
//console.log(res);
if(res.data.code===succ_code){
//这段代码的作用主要是演示下拉刷新的效果
//每次刷新会加载出不同数量,不同顺序的轮播图
let sliders=res.data.slider;
const slider=[sliders[math.floor(math.random()*sliders.length)]];//slider是从sliders中随机取出一张图片,并包装成数组
sliders=shuffle(sliders.filter(()=>math.random()>=0.5));//50%的概率,真就返回,假就剔除
if(sliders.length===0){
sliders=slider;//如果不幸一张都没有,就把之前随机的那张赋值给sliders
}
return sliders;
//return res.data.slider;
}
throw new error('没有成功获取到数据');
}).catch(err=>{
console.log(err);
//错误处理
return [{
linkurl:'www.baidu.com',
picurl:require('assets/img/404.png')
}]
});
}
//获取热门推荐数据
export const gethomerecommend=(page=1,psize=home_recommend_page_size)=>{
const url='https://ju.taobao.com/json/tg/ajaxgetitemsv2.json';
const params={
page,
psize,
type:0,
frontcatid:''//type和frontcatid是根据给定的淘宝接口来添加的
}
//调用jsonp获取数据
return jsonp(url,params,jsonp_options).then(res=>{
if(res.code==='200'){
return res;
}
throw new error('没有成功获取到数据');
}).catch(err=>{
if(err){
console.log(err);
}
});
}