Vue.js入门 0x12 实战:知乎日报项目开发-推荐列表与分类

搭建基本架构

    css样式直接写在 daily/style.css,并在main.js中导入:

//main.js
import Vue from 'vue';
import App from './app.vue';
import './style.css';
new Vue({
    el: '#app',
    render: h => {
        return h(App)
    }
});

    日报为单页应用,没有路由,只有一个入口组件app.vue。应用分左中右三栏,三栏都可以滚动。对左栏和中栏使用fixed固定,并使用overflow:auto滚动,而右栏高度自适应,使用浏览器默认的body区域滚动即可。基本结构如下:

//app.vue
<template>
    <div class="daily">
        <div class="daily-menu">
            <div class="daily-menu-item">每日推荐</div>
            <div class="daily-menu-item">主题日报</div>
        </div>
        <div class="daily-list">
            <Item></Item>
        </div>
        <daily-article></daily-article>
    </div>
</template>
/* style.css */
html,body {
    margin: 0;
    padding: 0;
    height: 100%;
    color: #657180;
    font-size: 16px;
}
.daily-menu {
    width: 150px;
    position: fixed;
    top:0;
    bottom: 0;
    left:0;
    overflow: auto;
    background: #f5f7f9;
}
.daily-menu-item{
    font-size: 18px;
    text-align: center;
    margin: 5px 0;
    padding: 10px 0;
    cursor: pointer;
    border-right: 2px solid transparent;
    transition: all .3s ease-in-out;
}
.daily-menu-item:hover{
    background: #e3e8ee;
}
.daily-menu-item.on{
    border-right: 2px solid #3399ff;
}
.daily-list{
    width: 300px;
    position: fixed;
    top:0;
    bottom: 0;
    left: 150px;
    overflow: auto;
    border-right: 1px solid #d7dde4;
}
.daily-item {
    display: block;
    color: inherit;
    text-decoration: none;
    padding: 16px;
    overflow: hidden;
    cursor: pointer;
    transition: all .3s ease-in-out;
}
.daily-item:hover {
    background: #e3e8ee;
}
.daily-article{
    margin-left: 450px;
    padding: 20px;
}

主题日报 

“主题日报”下有子类列表,默认是收起的,点击主题日报可以切换展开和收起的状态,使用数据showTheme来控制,并用themes来循环渲染子类目。

<!--app.vue-->
<template>
    <div class="daily-menu">
        <div class="daily-menu-item" 
            :class="{on:type==='recommend'}">每日推荐</div>
        <div class="daily-menu-item"
            :class="{on:type==='daily'}"
            @click="showTheme=!showTheme">主题日报</div>
        <ul v-show="showTheme">
            <li v-for="item in themes">
                <a :class="{on:item.id===themeId&&type==='daily'}">{{item.name}}</a>
            </li>
        </ul>
    </div>
</template>
<script>
    export default{
        data(){
            return {
                themes:[],
                showTheme:false,
                type:'recommend',
                themeId:0
            }
        },
    }
</script>
/*style.css*/
.daily-menu ul {
    list-style: none;
}
.daily-menu ul li a{
    display: block;
    color: inherit;
    text-decoration: none;
    padding: 5px 0;
    margin: 5px 0;
    cursor: pointer;
}
.daily-menu ul li a:hover, .daily-menu ul li a.on{
    color: #3399ff;
}

    themId会在点击子类时设置,稍后会有介绍。应用初始化时,获取主题日报的分类列表:

<!--app.vue-->
<script>
    import $ from './libs/util';
    export default{
        data(){
            return {
                themes:[],
                showTheme:false,
                type:'recommend',
                themeId:0
            }
        },
        methods:{
            getThemes(){
                //axios发起get请求
                $.ajax.get('themes').then(res=>{
                    this.themes = res.others;
                })
            }
        },
        mounted(){
            //初始化时调用
            this.getThemes();
        }
    }
</script>

    点击子类目时,将菜单type切换为“主题日报”高亮点击的子类,然后加载该类目下的文章列表

//app.vue
<template>
    <div class="daily">
        <div class="daily-menu">
            <div class="daily-menu-item" 
                :class="{on:type==='recommend'}">每日推荐</div>
            <div class="daily-menu-item"
                :class="{on:type==='daily'}"
                @click="showTheme=!showTheme">主题日报</div>
            <ul v-show="showTheme">
                <li v-for="item in themes">
                    <a 
                        :class="{on:item.id===themeId&&type==='daily'}"
                        @click="handleToTheme(item.id)">{{item.name}}</a>
                </li>
            </ul>
        </div>
        <div class="daily-list">
            <Item></Item>
        </div>
        <daily-article></daily-article>
    </div>
</template>
<script>
    import $ from './libs/util';
    export default{
        data(){
            return {
                themes:[],
                showTheme:false,
                type:'recommend',
                themeId:0
            }
        },
        methods:{
            getThemes(){
                //axios发起get请求
                $.ajax.get('themes').then(res=>{
                    this.themes = res.others;
                })
            },
            handleToTheme(id){
                //改变菜单分类
                this.type = 'daily';
                //设置当前点击子类的主题日报id
                this.themeId = id;
                //清空中间栏的数据
                this.list = [];
                $.ajax.get('theme/'+id).then(res=>{
                    //过滤掉类型为1的文章,该类型下的文章为空
                    this.list = res.stories
                        .filter(item=>item.type!==1);
                })
            }
        },
        mounted(){
            //初始化时调用
            this.getThemes();
        }
    }
</script>

每日推荐

    应用初始化和点击“每日推荐”菜单时请求推荐的文章列表。推荐列表的API相对地址为news/before/20190315,before后面是查询的日期,这个日期比要查询的真实日期多一天,比如要查20190314推荐的内容,就要请求20190315。每日推荐可以无限次地向前一天查询,为方便操作日期,在libs/util.js内定义两个时间方法:

// /libs/util.js
import axios from 'axios';
/*..*/
//获取今天的时间戳
Util.getTodayTime = function(){
    const date = new Date();
    date.setHours(0);
    date.setMinutes(0);
    date.setSeconds(0);
    date.setMilliseconds(0);
    return date.getTime();
}
//获取前一天的日期
Util.prevDay = function(timestamp=(new Date()).getTime()){
    const date = new Date(timestamp);
    const year = date.getFullYear();
    const month = date.getMonth()+1<10
        ?'0'+(date.getMonth()+1)
        :date.getMonth()+1;
    const day = date.getDate()<10
        ?'0'+date.getDate()
        :date.getDate();
    return year+month+day;
};
export default Util;

    推荐文章的列表获取

    app.vue

<script>
    import $ from './libs/util';
    export default{
        data(){
            return {
                themes:[],
                showTheme:false,
                type:'recommend',
                recommendList:[],
                dailyTime:$.getTodayTime(),
                isLoading:false
            }
        },
        methods:{
            getThemes(){
                //axios发起get请求
                $.ajax.get('themes').then(res=>{
                    this.themes = res.others;
                })
            },
            handleToTheme(id){
                //改变菜单分类
                this.type = 'daily';
                //设置当前点击子类的主题日报id
                this.themeId = id;
                //清空中间栏的数据
                this.list = [];
                $.ajax.get('theme/'+id).then(res=>{
                    //过滤掉类型为1的文章,该类型下的文章为空
                    this.list = res.stories
                        .filter(item=>item.type!==1);
                })
            },
            handleToRecommend(){
                this.type = 'recommend';
                this.recommendList = [];
                this.dailyTime = $.getTodayTime();
                this.getRecommendList();
            },
            getRecommendList(){
                this.isLoading = true;
                const prevDay = $.prevDay(this.dailyTime+86400000);
                $.ajax.get('news/before/'+prevDay).then(res=>{
                    this.recommendList.push(res);
                    this.isLoading = false;
                })
            }
        },
        mounted(){
            //初始化时调用
            this.getThemes();
            this.getRecommendList();
        }
    }
</script>

    两个文章列表(list,recommendList)的每一项都用一个组件item.vue来展示,在daily/components目录下新建item.vue

//components/item.vue
<template>
    <a class="daily-item">
        <div class="daily-img" v-if="data.images">
            <img :src="imgPath+data.images[0]">
        </div>
        <div 
            class="daily-title"
            :class="{noImg:!data.images}">{{data.title}}</div>
    </a>
</template>
<script>
    import $ from '../libs/util';
    export default {
        props:{
            data:{
                type:Object
            }
        },
        data(){
            return {
                imgPath:$.imgPath
            }
        }
    }
</script>

    style.css

.daily-item {
    display: block;
    color: inherit;
    text-decoration: none;
    padding: 16px;
    overflow: hidden;
    cursor: pointer;
    transition: all .3s ease-in-out;
}
.daily-item:hover {
    background: #e3e8ee;
}
.daily-img{
    width:80px;
    height: 80px;
    float: left;
}
.daily-img img {
    width: 100%;
    height: 100%;
    border-radius: 3px;
}
.daily-title{
    padding: 10px 5px 10px 90px;
}
.daily-title.noImg{
    padding-left: 5px;
}

    prop:data里可能没有images字段,所以列表会显示两种模式,即含封面图和不含封面图。

    Item组件会用到文章列表里,type为recommend和daily两种类型下,渲染会稍有不同。recommend会显示每天的日期,daily这没有。

//app.vue
<template>
    <div class="daily">
        <div class="daily-list">
            <template v-if="tyep==='recommend'">
                <div v-for="list in recommendList">
                    <div class="daily-date">{{formatDay(list.date)}}</div>
                    <Item
                        v-for="item in list.stories"
                        :data="item"
                        :key="item.id"></Item>
                </div>
            </template>
            <template v-if="type==='daily'">
                <Item
                    v-for="item in list"
                    :data="item"
                    :key="item.id"></Item>
            </template>
        </div>
        <daily-article></daily-article>
    </div>
</template>
<script>
    import $ from './libs/util';
    import Item from './components/item.vue';
    export default{
        components:{Item},
        data(){
            return {
                themes:[],
                showTheme:false,
                type:'recommend',
                recommendList:[],
                list:[],
                dailyTime:$.getTodayTime(),
                isLoading:false
            }
        },
        methods:{
            
            //转换为带汉字的
            formatDay(date){
                let month = date.substr(4,2);
                let day = date.substr(6,2);
                if(month.substr(0,1)==='0')month=month.substr(1,1);
                if(day.substr(0,1)==='0')day=day.substr(1,1);
                return `${month}月${dya}日`;
            }
        },
        mounted(){
            //初始化时调用
            this.getThemes();
            this.getRecommendList();
        }
    }
</script>

    style.css

.daily-list{
    width: 300px;
    position: fixed;
    top:0;
    bottom: 0;
    left: 150px;
    overflow: auto;
    border-right: 1px solid #d7dde4;
}
.daily-date{
    text-align: center;
    margin:10px 0;
}

自动加载更多推荐列表

//app.vue
<template>
    <div class="daily">
        <div class="daily-menu">
           
        </div>
        <div class="daily-list" ref="list">
            
        </div>
        <daily-article></daily-article>
    </div>
</template>
<script>
    import $ from './libs/util';
    import Item from './components/item.vue';
    export default{
        components:{Item},
        data(){
            return {
                themes:[],
                showTheme:false,
                type:'recommend',
                recommendList:[],
                list:[],
                dailyTime:$.getTodayTime(),
                isLoading:false
            }
        },
        methods:{
          
            getRecommendList(){
                //加载时设置为true,加载完设置为false
                this.isLoading = true;
                const prevDay = $.prevDay(this.dailyTime+86400000);
                $.ajax.get('news/before/'+prevDay).then(res=>{
                    this.recommendList.push(res);
                    this.isLoading = false;
                })
            }
        },
        mounted(){
            //初始化时调用
            this.getThemes();
            this.getRecommendList();
            //获取DOM
            const $list = this.$refs.list;
            //监听中栏的滚动事件 
            $list.addEventListener('scroll',()=>{
                //在主题日报或正在加载推荐列表时停止操作
                if(this.type==='daily'||this.isLoading)return;
                //已经滚动的距离加页面的高度等于整个内容区域高度时,视为接触底部
                if($list.scrollTop+document.body.clientHeight>=$list.scrollHeight){
                    //时间相对减少一天
                    this.dailyTime -=86400000;
                    this.getRecommendList();
                }
            });
        }
    }
</script>

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值