搭建基本架构
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>