废弃说明:
这个专栏的文章本意是记录笔者第一次搭建博客的过程,文章里里有很多地方的写法都不太恰当,现在已经废弃,关于SpringBoot + Vue 博客系列,笔者重新写了这个系列的文章,不敢说写的好,但是至少思路更加清晰,还在看SpringBoot + Vue 博客系列文章的朋友可以移步:https://blog.csdn.net/li3455277925/category_10341110.html,文章中有错误的地方或者大家有什么意见或建议都可以评论或者私信交流。
1. 实现MarkDown to HTML
- 因为存入数据库中的文章内容为
markdown
格式,要像展示在网页上必须将markdown
转换为HTML
,这里用到一个开源项目:showdownjs
项目地址为:https://github.com/showdownjs/showdown
- 使用
npm install showdown
命令安装 - 新建
/src/components/page/articleDetails.vue
文件,在文件中导入showdown,在vue生命周期created
函数中使用将markDown转换为html并存在变量content中,再渲染到页面中,代码如下:
<template>
<b-container class="main">
<!-- 文章标题 -->
<h2 class="title">这是文章的标题</h2>
<!-- 文章描述 -->
<h6 class="description"></h6>
<!-- 文章内容 -->
<div v-html="content"></div>
<!-- 标签 -->
<div class="tag-box"></div>
</template>
<script>
import showdown from "showdown";
export default {
name: "articleDetails",
data() {
return {
content: null
};
},
methods: {
created() {
let converter = new showdown.Converter();
let text = "- 列表 \n - 列表 ";
this.content = converter.makeHtml(text);
}
};
</script>
2. 实现评论以及二级评论
- 由于展示二级评论的时候需要用到动画,vue的动画效果比较繁琐,而且对CSS3要求比较高,所以这里使用JQuery实现的简单的动画。在
/static/
目录下新建js
文件夹,存放js文件,将下载的JQuery放在文件夹下。在index.html
中引入。
JQuery官网:https://jquery.com/
另外Vue和JQuery一起使用时有一些注意事项:https://blog.csdn.net/qq_39692256/article/details/82835096
- 动画使用JQuery中的
slideDown()
和slideUp()
方法实现,并且给每条评论添加唯一的id,避免出现点击一条评论的图标所有评论的二级评论都展开的情况。 - 代码实现:
<template>
<b-container class="main">
<!-- 文章标题 -->
<h2 class="title">这是文章的标题</h2>
<!-- 文章描述 -->
<h6 class="description"></h6>
<!-- 文章内容 -->
<div v-html="content"></div>
<!-- 标签 -->
<div class="tag-box"></div>
<!-- 评论回复 -->
<hr />
<h5>总共有4条评论</h5>
<hr />
<!-- 一级评论列表 start -->
<b-media>
<template v-slot:aside>
<b-img width="60" height="60" src="/static/images/no-name.png"></b-img>
</template>
<h6 thumbnail class="commentator-name">匿名用户</h6>
<p>一级评论1</p>
<p>
<i class="icon iconfont icon-dianzan1 link-icon"></i>
<i class="icon iconfont icon-pinglun link-icon" @click="showOrHideSecondLevelComments(1)"></i>2
</p>
<div id="second-level-comment-1" style="display:none;">
<!-- 二级评论列表 start -->
<b-media>
<template v-slot:aside>
<b-img width="60" height="60" src="/static/images/no-name.png"></b-img>
</template>
<h6 thumbnail class="commentator-name">匿名用户</h6>
<p>二级评论1</p>
</b-media>
<!-- 二级评论 end -->
<hr />
<b-textarea placeholder="请输入评论内容......"></b-textarea>
<b-button class="pull-right" variant="success">提交</b-button>
</div>
</b-media>
<!-- 一级评论列表 end -->
<hr />
<b-media>
<template v-slot:aside>
<b-img width="60" height="60" src="/static/images/no-name.png"></b-img>
</template>
<h6 thumbnail class="commentator-name">匿名用户</h6>
</b-media>
<b-textarea placeholder="请输入评论内容......"></b-textarea>
<b-button variant="success" class="pull-right">提交</b-button>
</b-container>
</template>
<script>
import showdown from "showdown";
export default {
name: "articleDetails",
data() {
return {
content: null
};
},
methods: {
showOrHideSecondLevelComments(id) {
let element = $("#second-level-comment-" + id);
let display = element.css("display");
if (display == "none") {
element.slideDown();
} else {
element.slideUp();
}
}
}
};
</script>
3. 预览
- 在路由中注册
articleDetails
组件
import Vue from 'vue'
import Router from 'vue-router'
import mainPage from "@/components/page/mainPage"
import articleDetails from "@/components/page/articleDetails"
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'mainPage',
component: mainPage
},
{
path:'/articleDetails',
name:"articleDetails",
component:articleDetails
}
]
})
- 完整代码:
<template>
<b-container class="main">
<!-- 文章标题 -->
<h2 class="title">这是文章的标题</h2>
<!-- 文章描述 -->
<h6 class="description">
<b-badge variant="info">原创</b-badge>
<i class="icon iconfont icon-riqi"></i> 2019-10-12
<i class="icon iconfont icon-gaojian-zuozhe"></i> 芊雨
<i class="icon iconfont icon-yuedu"></i> 20
<i class="icon iconfont icon-fenlei"></i> 学习经验
</h6>
<!-- 文章内容 -->
<div v-html="content"></div>
<!-- 标签 -->
<div class="tag-box">
<b-link class="tag" variant="info">
<i class="icon iconfont icon-tag"></i>java
</b-link>
<b-link class="tag" variant="info">
<i class="icon iconfont icon-tag"></i>java
</b-link>
<b-link class="tag" variant="info">
<i class="icon iconfont icon-tag"></i>java
</b-link>
</div>
<!-- 评论回复 -->
<hr />
<h5>总共有4条评论</h5>
<hr />
<!-- 一级评论列表 start -->
<b-media>
<template v-slot:aside>
<b-img width="60" height="60" src="/static/images/no-name.png"></b-img>
</template>
<h6 thumbnail class="commentator-name">匿名用户</h6>
<p>一级评论1</p>
<p>
<i class="icon iconfont icon-dianzan1 link-icon"></i>
<i class="icon iconfont icon-pinglun link-icon" @click="showOrHideSecondLevelComments(1)"></i>2
</p>
<div id="second-level-comment-1" style="display:none;">
<!-- 二级评论列表 start -->
<b-media>
<template v-slot:aside>
<b-img width="60" height="60" src="/static/images/no-name.png"></b-img>
</template>
<h6 thumbnail class="commentator-name">匿名用户</h6>
<p>二级评论1</p>
</b-media>
<!-- 二级评论 end -->
<hr />
<b-textarea placeholder="请输入评论内容......"></b-textarea>
<b-button class="pull-right" variant="success">提交</b-button>
</div>
</b-media>
<!-- 一级评论列表 end -->
<hr />
<b-media>
<template v-slot:aside>
<b-img width="60" height="60" src="/static/images/no-name.png"></b-img>
</template>
<h6 thumbnail class="commentator-name">匿名用户</h6>
</b-media>
<b-textarea placeholder="请输入评论内容......"></b-textarea>
<b-button variant="success" class="pull-right">提交</b-button>
<!-- 回顶部按钮 -->
<i class="icon iconfont icon-huidingbu" @click="backTop()"></i>
</b-container>
</template>
<script>
import showdown from "showdown";
export default {
name: "articleDetails",
data() {
return {
content: null
};
},
methods: {
showOrHideSecondLevelComments(id) {
let element = $("#second-level-comment-" + id);
let display = element.css("display");
if (display == "none") {
element.slideDown();
} else {
element.slideUp();
}
},
//回到顶部
backTop() {
let back = setInterval(() => {
if (document.body.scrollTop || document.documentElement.scrollTop) {
document.body.scrollTop -= 50;
document.documentElement.scrollTop -= 50;
} else {
clearInterval(back);
}
});
}
},
created() {
let converter = new showdown.Converter();
let text =
"- 列表 \n - 列表 \n - 列表 \n - 列表 \n- 列表 \n - 列表 \n- 列表 \n - 列表 \n- 列表 \n - 列表 \n- 列表 \n - 列表 \n- 列表 \n - 列表 \n- 列表 \n - 列表 \n- 列表 \n - 列表 \n- 列表 \n - 列表 \n- 列表 \n - 列表 \n- 列表 \n - 列表 \n- 列表 \n - 列表 \n- 列表 \n - 列表 \n- 列表 \n - 列表 \n- 列表 \n - 列表 \n- 列表 \n - 列表 \n- 列表 \n - 列表 \n- 列表 \n - 列表 \n- 列表 \n - 列表 \n- 列表 \n - 列表 \n- 列表 \n - 列表 \n- 列表 \n - 列表 \n- 列表 \n - 列表 \n- 列表 \n - 列表 \n- 列表 \n - 列表 \n- 列表 \n - 列表 \n- 列表 \n - 列表 \n- 列表 \n - 列表 \n- 列表 \n - 列表 \n- 列表 \n - 列表 \n- 列表 \n - 列表 \n- 列表 \n - 列表 \n- 列表 \n - 列表 \n- 列表 \n - 列表 \n- 列表 \n - 列表 \n- 列表 \n - 列表 \n- 列表 \n - 列表 \n- 列表 \n - 列表 \n- 列表 \n - 列表 \n- 列表 \n - 列表 \n- 列表 \n - 列表 \n- 列表 \n - 列表 \n- 列表 \n - 列表 \n- 列表 \n - 列表 \n- 列表 \n - 列表 \n";
this.content = converter.makeHtml(text);
}
};
</script>
<style scoped>
.main {
margin-top: 10px;
padding: 20px 10px;
border-radius: 5px;
background-color: #fff;
/* 防止当文章内容较少的时候,出现上下颜色不一致的问题 */
min-height: 100%;
/* 解决元素浮动后从父元素溢出的问题 */
overflow: auto;
}
.title,
.description {
text-align: center;
}
.tag-box {
margin-left: 10px;
display: flex;
flex-direction: row;
justify-content: left;
}
.tag {
text-decoration: none;
margin-left: 5px;
}
.commentator-name {
font-weight: bold;
text-align: left;
}
.link-icon {
margin-right: 5px;
}
.link-icon:hover {
color: #337ab7;
}
textarea {
margin: 10px 0;
}
/* 回顶部按钮 start */
.icon-huidingbu:before {
font-size: 25px;
}
.icon-huidingbu {
text-align: center;
width: 37px;
height: 37px;
position: fixed;
left: 92%;
top: 80%;
}
.icon-huidingbu:hover {
border-radius: 6px;
background-color: #495057;
color: #eee;
}
/* 回顶部按钮 end */
</style>
- 在浏览器中输入:http://localhost:8080/#/articleDetails
- 效果: