App.vue
<template>
<div id="app">
<!--博客首页显示-->
<router-view>
</router-view>
<!--回到顶部的组件-->
<go-top></go-top>
</div>
</template>
<script>
import GoTop from "@/components/gotop/GoTop"
export default
{
name: 'App',
components:
{
"go-top": GoTop,
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
这里由@作为src目录,需要进行一个配置
vue.config.js:
const path = require('path')
function resolve(dir) {
return path.join(__dirname, dir)
}
module.exports =
{
lintOnSave:true,
pages:
{
index:
{
entry: 'src/main.js',
}
},
configureWebpack: {
resolve: {
alias: {
'@': resolve('src')
}
}
}
}
在components下新建gotop目录,新建组件GoTop.vue,实现首页到了底部,点击一个按钮重新回到顶部:
<template>
<transition>
<div @click="toTop" v-if="topShow" class="me-to-top">
<i class="el-icon-caret-top"></i>
</div>
</transition>
</template>
<script>
export default
{
name:'GoTop',
data()
{
return{
topShow:false//默认不在底部就不显示
}
},
methods:
{
toTop()//回到顶部
{
document.body.scrollTop = 0;
document.documentElement.scrollTop = 0;
this.topShow = false;
},
needToTop()
{
let currentHeight = document.documentElement.scrollTop || document.body.scrollTop;
this.topShow = currentHeight > 400;
},
},
mounted()
{
this.$nextTick(()=>
{
//对窗体的滚动添加一个事件,监听窗体的滚动(只要滚动栏一滚动,该事件就生效)
window.addEventListener("scroll", this.needToTop);
});
}
}
</script>
<style>
.me-to-top
{
background-color: #fff;
/*相对于浏览器窗口进行定位*/
position: fixed;
right: 100px;
bottom: 150px;
width: 40px;
height: 40px;
border-radius: 20px;
/*移入该范围光标变成手指*/
cursor: pointer;
transition: .3s;
/*阴影度*/
box-shadow: 0 0 6px rgba(0, 0, 0, .12);
/*重叠度*/
z-index: 5;
}
.me-to-top i
{
color: #00d1b2;
display: block;
line-height: 40px;
text-align: center;
font-size: 18px;
}
</style>
这里需要引入elementUI的一个样式icon,需要先安装elementUI
然后package.json文件会出现相应的依赖
main.js引入elementUI
import Vue from 'vue'
import App from './App.vue'
import router from "./router";
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
Vue.config.productionTip = false
new Vue({
render: h => h(App),
router,
}).$mount('#app')
重新运行就有该样式了
在views下添加Index.vue,充当中间的页面
<template>
<div>
<el-container>
<el-main class="myArticles">
<!--文章列表-->
<ArticleItem v-for="article in articles"
v-bind="article"
:key="article.id"
></ArticleItem>
</el-main>
<el-aside>Aside</el-aside>
</el-container>
</div>
</template>
<script>
import ArticleItem from "@/components/article/ArticleItem"
export default
{
name: '',
components:
{
ArticleItem,
},
data()
{
return{
articles:
[
{
id:"1",
weight:1,
title:"标题1",
commentCounts:123,
viewCounts:123,
summary:"概要11",
author:"作者",
tags:[{"tagName":"vue"}],
createDate:"3333"
},
{
id:"2",
weight:1,
title:"标题2",
commentCounts:123,
viewCounts:123,
summary:"概要11",
author:"作者",
tags:[{"tagName":"vue"}],
createDate:"111"
},
{
id:"3",
weight:1,
title:"标题3",
commentCounts:123,
viewCounts:123,
summary:"概要11",
author:"作者",
tags:[{"tagName":"vue"}],
createDate:"222"
}
]
}
}
}
</script>
<style scoped>
.el-container
{
width: 960px;
}
/*右侧边栏*/
.el-aside
{
/*右侧和主部空了20px的距离*/
margin-left: 20px;
width: 260px;
}
/*主栏*/
.el-main {
padding: 0px;
line-height: 16px;
}
/*文章列表*/
.el-card {
border-radius: 0;
}
/*设置卡片最后一个子元素除外的样式,第一个不用和上面有间隙*/
.el-card:not(:first-child)
{
margin-top: 20px;
}
</style>
在router的index文件中添加对应的路由(不然App的router-view没有显示的)
import Vue from 'vue'
import VueRouter from 'vue-router'
import foo from '../pages/foo'
import bar from '../pages/bar'
Vue.use(VueRouter)
const router = new VueRouter({
routes:
[
{
path:"/",
component: r => require.ensure([], () => r(require('@/views/Index')), 'index')
}
]
});
export default router
文章列表是由一组card组成,所以在components下新建article,在其下新建ArticleItem.vue:
<template>
<el-card class="my-area" :body-style="{ padding: '16px' }">
<!--文章标题、评论数、阅读文章数-->
<div class="my-article-header">
<a @click="view(id)" class="my-article-title">{{title}}</a>
<el-button v-if="weight > 0" class="my-article-icon" type="text">置顶</el-button>
<!--评论数-->
<span class="my_pull-right my_article-count">
<i class="my-icon-comment"></i>
{{commentCounts}}
</span>
<!--阅读文章数-->
<span class="my-pull-right my-article-count">
<i class="el-icon-view"></i>
{{viewCounts}}
</span>
</div>
<!--摘要-->
<div class="my-article-description">
{{summary}}
</div>
<!--作者、标签、时间-->
<div class="my-article-footer">
<span class="my-article-author">
<i class="my-icon-author"></i>
<el-tag>{{author}}</el-tag>
</span>
<el-tag v-for="t in tags" :key="t.tagName" size="mini" type="success">
{{t.tagName}}
</el-tag>
<span class="my-pull-right my-article-count">
<i class="el-icon-time"></i>
{{createDate | format}}
</span>
</div>
</el-card>
</template>
<script>
export default
{
name:"ArticleItem",
props:
{
id: String,
weight: Number,
title: String,
commentCounts: Number,
viewCounts: Number,
summary: String,
author: String,
tags: Array,
createDate: String
},
methods:
{
/*点击文章标题,跳转到文章详情*/
view(id)
{
this.$router.push({path:`/view/${id}`})
}
}
}
</script>
<style>
/*文章header*/
.my-article-header
{
padding-bottom: 10px;
}
/*标题大小*/
.my-article-title
{
font-weight: 600;
}
/*是否置顶icon*/
.my-article-icon
{
padding: 3px 8px;
}
/*阅读文章数*/
my-article-count
{
color: #ff00ff;
padding-left: 14px;
font-size: 13px;
}
/*??*/
.my-pull-right
{
float: right;
}
/*文章摘要*/
.my-article-description
{
font-size: 13px;
line-height: 24px;
margin-bottom: 10px;
}
.my-article-author
{
color: #aa00ff;
padding-right: 18px;
font-size: 13px;
}
.el-tag
{
margin-left: 6px;
color: #ffffff;
background-color: #ff00ff;
}
</style>
时间格式化工具类:
/*文章创建时间格式化*/
export function formatTime(time)
{
const d = new Date(time);
const now = Date.now();
const diff = (now - d) /1000;//单位为秒
if(diff<30)/*如果文章创建时间小于30秒*/
{
return "刚刚";
}else if(diff < 3600)
{
/*向上取整*/
return Math.ceil(diff/60) + "分钟前";
}else if(diff < 3600 * 24)
{
return Math.ceil(diff/3600) + "小时前";
}else if(diff < 3600 * 24 *2)
{
return "1天前";
}
return time;
}
main.js里面使用:
/*格式化时间*/
import {formatTime} from "./utils/time";
Vue.filter('format',formatTime);
文章列表是一个分页列表,这里我们需要实现,下拉分页,即如果拉到底部,就会请求下一页的数据,如果有数据就加载出来,如果没数据,就证明到达最后一页
新建scrollpage/index.vue
<!--加载动画-->
<template>
<div ref="scroll" id="scroll-page" style="overflow: hidden">
<slot></slot>
<!--loading动画,加载的数据的过程中显示 elementUI load-->
<div
style="height: 40px;margin-top: 10px;z-index: 1"
v-loading="loading"
element-loading-text="拼命加载中"
element-loading-spinner="el-icon-loading"
element-loading-background="rgba(245,245,245)">
</div>
</div>
</template>
<script>
export default
{
name: "index",
props: {
loading: Boolean,
noData: Boolean,
offset: Number
},
mounted() {
//绑定事件监听,滚动的时候触发
window.addEventListener('scroll', this.handleScroll, false);
},
beforeDestroy() {
//移出事件监听
window.removeEventListener('scroll', this.handleScroll);
},
data() {
return {
scrollAction: {
x: 'undefined',
y: 'undefined'
}
}
},
methods: {
handleScroll(e) {
let that = this;
if (!that.noData) {
//如果有数据,触发
let curHeight = document.documentElement.scrollTop || document.body.scrollTop;
//获取div区域
let scrollPage = document.getElementById('scroll-page');
if ((curHeight + window.innerHeight >= that.$refs.scroll.offsetHeight + that.offset) && that.isDownDirection())
{
//判断是否到达底部
if (!that.loading) {
//调用load加载数据
that.$emit('load')
}
}
}
},
isDownDirection()
{
if (typeof this.scrollAction.x == 'undefined') {
this.scrollAction.x = window.pageXOffset;
this.scrollAction.y = window.pageYOffset;
}
let diffX = this.scrollAction.x - window.pageXOffset;
let diffY = this.scrollAction.y - window.pageYOffset;
this.scrollAction.x = window.pageXOffset;
this.scrollAction.y = window.pageYOffset;
if (diffX < 0) {
// Scroll right
} else if (diffX > 0) {
// Scroll left
} else if (diffY < 0) {
// Scroll down
return true
} else if (diffY > 0) {
// Scroll up
} else {
// First scroll event
}
return false
}
}
}
</script>
<style scoped>
</style>
在views下新建common文件夹,其中新建ArticleScrollPage.vue,
将文章列表放入滚动分页区域内
<template>
<scroll-page :loading="loading"
:offset="offset"
:no-data="noData"
@load="load">
<article-item v-for="article in articles"
:key="article.id"
v-bind="article">
</article-item>
</scroll-page>
</template>
<script>
import ArticleItem from '@/components/article/ArticleItem'
import ScrollPage from '@/components/scrollpage'
export default
{
name: "ArticleScrollPage",
props: {
offset: {
type: Number,
default: 0
}
},
watch: {
},
created() {
this.getArticles()
},
data() {
return {
loading: false,
noData: false,
//分页信息
innerPage: {
pageSize: 5,
page: 1,
name: 'a.createDate',
sort: 'desc'
},
//文章列表
articles: []
}
},
methods: {
load() {
//下拉触发分页的时候,调用接口加载
alert("触发分页")
this.getArticles();
},
getArticles() {
let that = this
that.loading = true
that.articles = that.articles.concat([
{
id:"1",
weight:1,
title:"标题1",
commentCounts:123,
viewCounts:123,
summary:"概要11",
author:"作者",
tags:[{"tagName":"vue"}],
createDate:"3333"
},
{
id:"232",
weight:1,
title:"标题1",
commentCounts:123,
viewCounts:123,
summary:"概要11",
author:"作者",
tags:[{"tagName":"vue"}],
createDate:"3333"
},
{
id:"2",
weight:1,
title:"标题2",
commentCounts:123,
viewCounts:123,
summary:"概要11",
author:"作者",
tags:[{"tagName":"vue"}],
createDate:"11111"
},
{
id:"3",
weight:1,
title:"标题3",
commentCounts:123,
viewCounts:123,
summary:"概要11",
author:"作者",
tags:[{"tagName":"vue"}],
createDate:"2222"
},
{
id:"4",
weight:1,
title:"标题3",
commentCounts:123,
viewCounts:123,
summary:"概要11",
author:"作者",
tags:[{"tagName":"vue"}],
createDate:"2222"
},
{
id:"5",
weight:1,
title:"标题3",
commentCounts:123,
viewCounts:123,
summary:"概要11",
author:"作者",
tags:[{"tagName":"vue"}],
createDate:"2222"
},
{
id:"6",
weight:1,
title:"标题3",
commentCounts:123,
viewCounts:123,
summary:"概要11",
author:"作者",
tags:[{"tagName":"vue"}],
createDate:"2222"
},
{
id:"7",
weight:1,
title:"标题3",
commentCounts:123,
viewCounts:123,
summary:"概要11",
author:"作者",
tags:[{"tagName":"vue"}],
createDate:"2222"
},
{
id:"8",
weight:1,
title:"标题3",
commentCounts:123,
viewCounts:123,
summary:"概要11",
author:"作者",
tags:[{"tagName":"vue"}],
createDate:"2222"
}
]);
this.noData = false;
that.loading = false
}
},
components: {
'article-item': ArticleItem,
'scroll-page': ScrollPage
}
}
</script>
<style scoped>
.el-card {
border-radius: 0;
}
.el-card:not(:first-child) {
margin-top: 10px;
}
</style>
这样,views下的Index就直接用ArticleScrollPage组件即可
<template>
<div>
<el-container>
<el-main class="myArticles">
<!--文章列表-->
<ArticleScrollPage>
</ArticleScrollPage>
</el-main>
<el-aside>Aside</el-aside>
</el-container>
</div>
</template>
<script>
import ArticleScrollPage from "@/components/common/ArticleScrollPage"
export default
{
name: '',
components:
{
ArticleScrollPage,
},
}
</script>
<style scoped>
.el-container
{
width: 960px;
}
/*右侧边栏*/
.el-aside
{
/*右侧和主部空了20px的距离*/
margin-left: 20px;
width: 260px;
}
/*主栏*/
.el-main {
padding: 0px;
line-height: 16px;
}
/*文章列表*/
.el-card {
border-radius: 0;
}
/*设置卡片最后一个子元素除外的样式,第一个不用和上面有间隙*/
.el-card:not(:first-child)
{
margin-top: 20px;
}
</style>