Vue webAPP首页开发(五)

断了今生、忘了曾经 提交于 2020-04-05 16:34:33

接上篇 https://www.cnblogs.com/chenyingying0/p/12635369.html

 

返回顶部组件

base/backtop/index.vue

<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>

 

pages/home/index.vue

<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>

 

修改assets/scss/_containers.scss(之前写错了)

 

 

效果图

 

Header动画效果和显示隐藏

pages/home/header.vue

<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>

 

base/scroll/index.vue

<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>

 

pages/home/config.js

 

 

pages/home/index.vue

<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>

 

api/home.js

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);
        }
        
    });
    
}

 

效果图

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!