一、功能需求描述
用一张导图来说明:
二、页面设计
页面设计如下:
三、梳理下整个系统的业务流程
对这个小项目进行业务流程的梳理,流程图大致如下:
四、用户模块实现
1、数据库设计及代码
(1)用户表(users)
(2)博文分类表(categories)
(3)博文评论列表(contents)
从title往下依次是博文标题,分类,浏览次数,所属用户id,评论列表(评论内容,评论所属用户id),发表时间,文章描述,文章详情,数据库版本。
2、用户模块
由上述流程图得用户模块有登录、注册、博文列表、阅读原文及评论功能。
(1)页面代码
首页的界面使用的是bootstrap+jquery框架设计,首页总共有三个div块,一个是header,content-details,login-register。下面是页面的代码:
main_template.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>简单博客系统</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="stylesheet" type="text/css" href="/public/css/bootstrap.css">
<link rel="stylesheet" type="text/css" href="/public/css/index.css">
<script type="text/javascript" src="/public/js/jquery.js"></script>
<script type="text/javascript" src="/public/js/bootstrap.js"></script>
<script type="text/javascript" src="/public/js/index.js"></script>
</head>
<body>
<header>
<div class="container-fluid header1">
<span>NodeJS简单博客系统</span>
</div>
<nav class="navbar navbar-default">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
</div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
{%if category == ''%}
<li><a href="/" class="focus">首页</a></li>
{%else%}
<li><a href="/">首页</a></li>
{%endif%}
{%for cate in categories%}
{%if category == cate.id%}
<li><a href="/?category={{cate.id}}" class="focus">{{cate.name}}</a></li>
{%else%}
<li><a href="/?category={{cate.id}}">{{cate.name}}</a></li>
{%endif%}
{%endfor%}
</ul>
</div>
</div>
</nav>
</header>
<section>
<div class="container">
<div class="row">
<div class="col-lg-8 col-md-8 moveup" id="content">
<!--文章列表block-->
{% block content %}
{% endblock %}
</div>
<div class="col-lg-4 col-md-4" >
{% if userInfo._id %}
<div class="userinfo spindown" id="userinfo" >
<h2>用户信息</h2>
<h3 class="account">用户名:{{userInfo.username}}</h3>
{% if userInfo.isadmin %}
<p>您好,管理员! <a href="/admin/"> 点击这里</a>进入管理页面</p>
{% else %}
<p>你好,欢迎光临我的博客!</p>
{% endif %}
<p><a href="javascript:;" class="logout">退出</a></p>
</div>
{% else %}
<div class="register spindown" id="register" style="display: none">
<h2>注册</h2>
<div class="line">
<span>用户名:</span>
<input type="text" name="username" title="username">
</div>
<div class="line">
<span>密码:</span>
<input type="password" name="password" title="password">
</div>
<div class="line">
<span>确认:</span>
<input type="password" name="repassword" title="repassword">
</div>
<div class="line">
<input type="submit" name="submit" value="注册" >
</div>
<p class="warning"></p>
<p>已有账号? <a href="javascript:;">点击登录</a></p>
</div>
<div class="login spindown" id="login" >
<h2>登录</h2>
<div class="line">
<span>用户名:</span>
<input type="text" name="username" title="username">
</div>
<div class="line">
<span>密码:</span>
<input type="password" name="password" title="password">
</div>
<div class="line">
<input type="submit" name="submit" value="登录" >
</div>
<h2 class="warning"></h2>
<p>还没注册? <a href="javascript:;">点击注册</a></p>
</div>
{% endif %}
</div>
</div>
</div>
</section>
<footer>
<p>Copyright © 小马实验室 | 京ICP备11951015号 | 京公网安备11011105210084</p>
<a href="#"><span class="glyphicon glyphicon-arrow-up"></span></a>
</footer>
</body>
</html> |
文章详情article_detail.html
{% extends "main_template.html" %}
{% block content %}
<div class="papers">
<h2>{{contents.title}}</h2>
<p class="paperabout">
作者:<span class="paperinfo">{{contents.user.username}}</span>-
时间:<span class="paperinfo">{{contents.addtime|date('Y-m-d H:i:s', -8*60)}}</span>-
阅读:<span class="paperinfo">{{contents.num}}</span>-
分类于:<span class="paperinfo">{{contents.category.name}}</span>-
评论:<span class="paperinfo">{{contents.comment.length}}</span>
</p>
<dfn><p>{{contents.composition}}</p></dfn>
<div class="readmore"><a href="javascript:window.history.back()">返回</a></div>
</div>
<div id="comment">
<h3 ><strong>评论 </strong> <span class="much"> 共 <em id="commentCount">0</em> 条评论</span></h3>
<div style="font-size: 22px;">
<div>
<textarea name="name" id="commentarea" placeholder="请填写评论"
style="height: 150px;width: 100%;"></textarea>
<input type="hidden" id="contentid" name="contentid" value="{{contents.id}}">
</div>
<button type="submit" id="addcomment" class="btn btn-primary btn-lg">发表评论</button>
</div>
{% if userInfo._id %}
{% else %}
<h4 class="loginfo" >你还没有登录,请先登录!</h4>
{% endif %}
{% if contents.comment.length == 0 %}
<h4 class="loginfo" >暂无评论,赶紧来评论吧!</h4>
{% endif %}
<div id="commentlist">
</div>
<div class="pages">
<a id="prevpage" style="float: left;"><span>上一页</span></a>
<span id="currentpage"></span> / <span id="totalpage"></span>
<a id="nextpage" style="float: right;"><span>下一页</span></a>
</div>
</div>
{% endblock %} |
首页index.html
<!--首页-->
{% extends "main_template.html" %}
{% block content %}
{% for content in contents %}
<div class="papers">
<h2>{{content.title}}</h2>
<p class="paperabout">
作者:<span class="paperinfo">{{content.user.username}}</span>-
时间:<span class="paperinfo">{{content.addtime|date('Y-m-d H:i:s', -8*60)}}</span>-
阅读:<span class="paperinfo">{{content.num}}</span>-
分类于:<span class="paperinfo">{{content.category.name}}</span>-
评论:<span class="paperinfo">{{content.comment.length}}</span>
</p>
<dfn><p>description:{{content.description}}</p></dfn>
<div class="readmore"><a href="/article?contentid={{content.id}}">阅读全文</a></div>
</div>
{% endfor %}
<div class="pages">
<a href="/?category={{category}}&page={{page-1}}" style="float: left;"><span>上一页</span></a>
<span>{{page}}</span> / <span>{{pages}}</span>
<a href="/?category={{category}}&page={{page+1}}" style="float: right;"><span>下一页</span></a>
</div>
{% endblock %} |
(2)页面对应的js、css代码
index.js
$(function(){
var loginbox = $("#login");
var registerbox = $("#register");
var userinfobox = $("#userinfo");
loginbox.find("a").on("click",function(){
loginbox.hide();
registerbox.show();
});
registerbox.find("a").on("click",function(){
loginbox.show();
registerbox.hide();
}); registerbox.find("input[name='submit']").on("click",function(){
$.ajax({
type: "post",
url: "/api/user/register",
dataType: "json",
data: {
username: registerbox.find('input[name="username"]').val(),
password: registerbox.find('input[name="password"]').val(),
repassword: registerbox.find('input[name="repassword"]').val()},
success :function(result){
console.log(result); registerbox.find(".warning").html(result.message);
setTimeout(function(){
registerbox.find(".warning").html("");
},1500);
if(!result.code){
setTimeout(function(){
loginbox.show();
registerbox.hide(); registerbox.find('input[name="username"]').val("");
registerbox.find('input[name="password"]').val("");
registerbox.find('input[name="repassword"]').val("");
},1500);
}
}
});
});
loginbox.find("input[name='submit']").on("click",function(){
$.ajax({
type: "post",
url: "/api/user/login",
dataType: "json",
data: {
username: loginbox.find('input[name="username"]').val(),
password: loginbox.find('input[name="password"]').val()
},
success :function(result){
console.log(result);
loginbox.find(".warning").html(result.message);
setTimeout(function(){
loginbox.find(".warning").html("");
},1500);
if(!result.code){
setTimeout(function(){
window.location.reload();
},1500);
}
}
});
});
userinfobox.find(".logout").on("click",function(){
$.ajax({
url:"/api/user/logout",
success:function(result){
console.log(result);
if(!result.code){
window.location.reload();
}
}
})
});
//在页面加载时获取评论
$.ajax({
url: '/api/pinglun',
type:"get",
dataType:"json",
data: {
contentid: $('#contentid').val()
},
success: function(result) {
//console.log(111111);
render(result.postdata);
quanju=result.postdata;
}
});
//提交评论
$("#addcomment").on("click",function(){
$.ajax({
type:"post",
url:"/api/comment",
dataType:"json",
data:{
comment: $("#comment").find("textarea").val(),
contentid: $("#contentid").val()
},
success:function(result){
//console.log(result);
$("#commentarea").val("");
render(result.postdata);
quanju=result.postdata;
}
})
});
var quanju=null;
var page=1;
var limit=3;
var pagecount=0;
$("#prevpage").on("click",function(){
page--;
render(quanju);
});
$("#nextpage").on("click",function(){
page++;
render(quanju);
});
function render(data) {
var str = "";
var start=(page-1)*limit;
var end = start+limit;
var comments=data.comment.reverse();
var showcomments=comments.slice(start,end);
pagecount = Math.ceil(data.comment.length/limit);
page = Math.min(pagecount,page);
page = Math.max(1,page);
$("#totalpage").html(pagecount);
$("#currentpage").html(page);
$("#commentCount").html(comments.length);
for (var i = 0; i < showcomments.length; i++) {
str += `<div>
<span class="commenter">${showcomments[i].user}</span>
<span class="commenttime">${formatDate(showcomments[i].time)}</span>
</div>
<p class="contents">${showcomments[i].comment}</p>`;
}
$("#commentlist").html(str);
}
function formatDate(d) {
var date1 = new Date(d);
return date1.getFullYear() + '-' + (date1.getMonth()+1) + '-' + date1.getDate() + '- ' + date1.getHours() + ':' + date1.getMinutes() + ':' + date1.getSeconds();
}
}); |
index.css
body{
background:#ebebeb;
min-width: 650px;
}
body h2, body h3 {
padding: 0;
margin: 0;
}
a{
text-decoration: none !important;
color: #fc6423;
}
.container .row .container-fluid {
padding: 0;
}
.header1{
height: 200px !important;
background:url(/public/img/backimg.jpg);
background-size:cover;
}
.header1 span{
margin-left: 40px;
line-height: 200px;
font-size: 30px;
color: #fc6423;
opacity: 0.7;
filter: alpha(opacity=70);
}
div[class*="col"]{
padding: 0;
color:#000;
}
#content .papers{
height: 500px;
margin-bottom: 20px;
background:#fff;
text-align: center;
position: relative;
}
#content .papers h2{
/*height: 80px;*/
line-height: 50px;
font-size: 25px;
}
#content .paperabout{
font-size: 18px;
}
#content .paperinfo{
color: #fc6423;
}
#content dfn p{
height: 300px;
font-size: 25px;
padding: 20px;
background: #ddd;
}
#content .readmore{
width: 150px;
height: 40px;
line-height: 36px;
font-size: 25px;
background:#fff;
border:2px solid #fc6423;
position: absolute;
bottom: 30px;
left: 50px;
}
#content .readmore a{
display: inline-block;
width: 150px;
height: 40px;
}
#content .readmore:hover{
background:#fc6423;
border:2px solid #fff;
}
#content .pages{
font-size: 18px;
height: 40px;
line-height: 40px;
text-align: center;
margin-bottom:20px;
}
#content .pages a{
color: #fc6423;
display: block;
height: 40px;
line-height: 40px;
width: 80px;
border:1px solid #fc6423;
}
#content .pages a:hover{
color: #fff;
background-color: #fc6423;
}
#content .pages>span{
color: #fc6423;
}
#register{
background:#fff;
margin-left: 30px;
padding-bottom: 20px;
margin-bottom: 20px;
transition: 0.5s ease-out;
}
#register h2{
height: 50px;
margin-left: 30px;
line-height: 50px;
font-size: 24px;
color: #fc6423;
}
#register .line{
height: 50px;
text-align: center;
line-height: 50px;
font-size: 18px;
}
#register .line input{
height: 60%;
outline: none;
border-color: #fc6423;
}
#register .line input[type="submit"]{
width: 200px;
border:none;
padding: 0;
background-color: #fc6423;
line-height: 100%;
}
#register .warning{
height: 50px;
text-align: center;
line-height: 50px;
font-size: 20px;
color: #fc6423;
}
#register>p{
margin-left: 30px;
}
#login{
background:#fff;
padding-bottom: 20px;
margin-left: 30px;
margin-bottom: 20px;
transition: 0.5s ease-out;
}
#login h2{
height: 50px;
margin-left: 30px;
line-height: 50px;
font-size: 24px;
color: #fc6423;
}
#login .line{
height: 50px;
text-align: center;
line-height: 50px;
font-size: 18px;
}
#login .line input{
height: 60%;
outline: none;
border-color: #fc6423;
}
#login .line input[type="submit"]{
width: 200px;
border:none;
padding: 0;
background-color: #fc6423;
line-height: 100%;
}
#login .warning{
height: 50px;
text-align: center;
line-height: 50px;
font-size: 20px;
color: #fc6423;
}
#login>p{
margin-left: 30px;
}
#userinfo, #community{
background:#fff;
margin-left: 30px;
margin-bottom: 20px;
padding-bottom: 20px;
}
#userinfo h2, #community h2{
height: 50px;
margin-left: 30px;
line-height: 50px;
font-size: 24px;
color: #fc6423;
}
#userinfo h3, #community h3{
height: 35px;
margin-left: 30px;
line-height: 35px;
font-size: 16px;
}
#userinfo>p{
text-align: center;
font-size: 15px;
}
footer{
height: 60px;
background:#333;
position: relative;
}
footer p{
text-align: center;
line-height: 60px;
color: #fff;
opacity: 0.7;
}
footer a{
display: block;
width: 60px;
height: 60px;
position: absolute;
top: -70px;
right: 10px;
background-color: #fc6423;
border-radius: 30px;
text-align: center;
line-height:100%;
}
footer a span{
font-size: 30px;
color: #fff;
margin-top: 10px;
}
#bs-example-navbar-collapse-1 ul{
width: 100%;
padding-left: 15%;
text-align: center;
}
#bs-example-navbar-collapse-1 li{
height: 60px;
}
#bs-example-navbar-collapse-1 ul a{
margin: 0;
padding: 0;
color: #fc6423;
width: 150px;
height: 60px;
line-height: 60px;
font-size: 20px;
}
#bs-example-navbar-collapse-1 ul a:hover{
background: #fc6423;
color:#fff;
-webkit-transform: scale(1.1);
transform: scale(1.1);
transition: 0.5s ease-out;
}
#comment{
padding: 30px;
background: #fff;
}
#comment h3{
text-align: left;
height: 60px;
line-height: 100%;
color: #fc6423;
}
#comment h3 .much{
float: right;
color: #888;
font-size: 18px;
}
#comment h4{
color: red;
text-align: center;
}
#commentlist{
margin-bottom: 30px;
}
#commentlist>p{
background: #eee;
height: 80px;
text-indent: 2em;
padding:5px 20px;
font-size: 18px;
color: #555;
margin-bottom: 10px;
}
#commentlist>div{
line-height: 40px;
padding: 5px;
height: 40px;
}
#commentlist>div .commenter{
float: left;
margin-left: 20px;
color: #fc6423;
}
#commentlist .commenttime{
float: right;
margin-right: 20px;
}
@keyframes moveup {
0%{
-webkit-transform: translateY(100px);
transform: translateY(100px);
opacity: 0;
filter: alpha(opacity=0);
}
50%{
-webkit-transform: translateY(30px);
transform: translateY(30px);
opacity: 0.25;
filter: alpha(opacity=25);
}
75%{
-webkit-transform: translateY(13px);
transform: translateY(13px);
opacity: 0.5;
filter: alpha(opacity=50);
}
100%{
-webkit-transform: translateY(0);
transform: translateY(0);
opacity: 1;
filter: alpha(opacity=100);
}
}
@keyframes turndown {
0%{
opacity: 0;
filter: alpha(opacity=0);
}
100%{
opacity: 1;
filter: alpha(opacity=100);
}
}
.moveup{
-webkit-animation: moveup 0.7s ease-in;
animation: moveup 0.7s ease-in;
-webkit-animation-fill-mode: forwards;
}
.spindown{
-webkit-animation: turndown 0.7s ease-in;
animation: turndown 0.7s ease-in;
-webkit-animation-fill-mode: forwards;
} |
3、后台代码
(1)项目配置
package.json
{
"name": "myblog",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"body-parser": "^1.18.2",
"cookies": "^0.7.1",
"express": "^4.16.2",
"mongoose": "^4.12.4",
"promise": "^8.0.1",
"swig": "^1.4.2"
}
}
|
app.js
//加载express模块
var express = require("express");
//加载swig模块
var swig = require("swig");
var User = require("./models/user");
//加载mongoose数据库,这个中间件是nodejs与mongoDB数据库的桥梁
var mongoose = require("mongoose");
var Cookies = require('cookies');
//创建一个新的服务器,相当于httpcreateServer
var app = express();
//静态文件资源托管的,js css img等
app.use("/public",express.static( __dirname+"/public"));
//定义应用使用的模板引擎,第一个参数:所要渲染模板文件的后缀,也是模板引擎的名称,第二个参数:渲染的方法
app.engine("html",swig.renderFile);
//定义模板文件存放的路径,第一个参数必须是views,这是模块内指定的解析字段,第二个参数为路径:./表示根目录
app.set("views","./views");
//注册使用模板引擎;第一个参数不能变,第二个参数和上面的html一致
app.set("view engine","html");
//设置完就可以直接在res中渲染html文件了:res.render("index.html",{要渲染的变量})第一个参数是相对于views文件夹
//在开发过程中要取消模板缓存,便于调试
swig.setDefaults({cache : false});
//加载bodyparser模块,用来解析前端提交过来的数据
var bodyparser = require("body-parser");
app.use(bodyparser.urlencoded({extended:true}));
app.use( function(req, res, next) {
req.cookies = new Cookies(req, res);
req.userInfo = {};
if(req.cookies.get('userInfo')){
var str1 = req.cookies.get('userInfo');
req.userInfo=JSON.parse(str1);
User.findById(req.userInfo._id).then(function(userInfodata){
req.userInfo.isadmin = Boolean(userInfodata.isadmin);
});
}
next();
} );
//浏览器地址映射
app.use("/admin" ,require("./routers/admin"));
app.use("/" ,require("./routers/main"));
app.use("/api" ,require("./routers/api"));
// 连接数据库
mongoose.connect("mongodb://localhost:27017/myBlog",{useMongoClient:true},function (err) {
if(err){
console.log("数据库连接失败!");
}else{
console.log("数据库连接成功!");
app.listen(3000);
}
});
|
(2)schemas
users.js
var mongoose = require("mongoose");
module.exports = new mongoose.Schema({
username: String,
password: String,
isadmin:{
type:Boolean,
default:false
}
});
|
categories.js
var mongoose = require("mongoose");
module.exports = new mongoose.Schema({
name: String
}); |
contents.js
var mongoose = require("mongoose");
module.exports = new mongoose.Schema({
title: String,
category : {
type:mongoose.Schema.Types.ObjectId,
ref : "Category"
},
composition:{
type: String,
default : ""
},
description :{
type: String,
default : ""
},
user:{
type:mongoose.Schema.Types.ObjectId,
ref : "User"
},
num:{
type:Number,
dafault:0
},
addtime:{
type:Date,
default: new Date()
},
comment:{
type:Array,
default:[]
}
}); |
(3)models
user.js
var mongoose = require("mongoose");
var userschama = require("../schemas/users");
module.exports = mongoose.model("User",userschama); |
category.js
var mongoose = require("mongoose");
var userschama = require("../schemas/users");
module.exports = mongoose.model("User",userschama); |
contents.js
var mongoose = require("mongoose");
var contentschama = require("../schemas/contents");
module.exports = mongoose.model("Content",contentschama); |
(4)路由(routers)
api.js
var express = require("express");
var User = require("../models/user");
var Content = require("../models/content");
var router= express.Router();
//统一返回给前端的数据格式
var resdata;
router.use(function(req,res,next){
resdata = {
code:0,
message:""
};
next();
});
router.post("/user/register",function(req ,res ){
var username = req.body.username;
var password = req.body.password;
var repassword = req.body.repassword;
if(username == ""){
resdata.code=1;
resdata.message="用户名不能为空!";
res.json(resdata);
return;
}
if(password == ""){
resdata.code=2;
resdata.message="密码不能为空!";
res.json(resdata);
return;
}
if(password != repassword){
resdata.code=3;
resdata.message="两次输入的密码不一致!";
res.json(resdata);
return;
}
User.findOne({
username:username
},function(err,userinfo){
if(err){
console.log(err);
}
if(userinfo){
resdata.code = 4;
resdata.message = "该用户已被注册!";
res.json(resdata);
return false;
}else{
var newuser = new User({
username: username,
password: password
});
newuser.save();
resdata.message = "注册成功!";
res.json(resdata);
}
});
});
router.post("/user/login",function(req ,res ){
var username = req.body.username;
var password = req.body.password;
if(username == ""||password==""){
resdata.code=1;
resdata.message="用户名和密码不能为空!";
res.json(resdata);
return;
}
User.findOne({
username:username,
password:password
},function(err,userinfo){
if(err){
console.log(err);
}
if(!userinfo){
resdata.code = 2;
resdata.message = "用户名或密码错误!";
res.json(resdata);
return false;
}
resdata.message = "登录成功!";
resdata.userinfo={
id:userinfo._id ,
username:userinfo.username,
isadmin:userinfo.isadmin
};
req.cookies.set('userInfo', JSON.stringify({
"_id": userinfo._id,
"username": userinfo.username,
"isadmin":userinfo.isadmin
}));
res.json(resdata);
})
});
router.get("/user/logout",function(req ,res ){
req.cookies.set('userInfo', null);
res.message="退出成功!";
res.json(resdata);
});
router.get('/pinglun', function(req, res) {
var contentid = req.query.contentid || '';
Content.findOne({
_id: contentid
}).then(function(content) {
//content.comment.reverse();
resdata.postdata = content;
//resdata.data.comments.reverse();
res.json(resdata);
})
});
router.post("/comment",function(req,res){
var id = req.body.contentid;
var commentdata={
comment:req.body.comment||"",
user:req.userInfo.username,
time: new Date()
};
Content.findOne({_id:id}).then(function(thiscon){
if(commentdata.comment!=""){
thiscon.comment.push(commentdata);
}
//thiscon.comment.reverse();
thiscon.save().then(function(newcon){
resdata.postdata = newcon;
resdata.message="评论成功!";
res.json(resdata);
//console.log(newcon);
});
});
});
module.exports=router; |
main.js
var express = require("express");
var router= express.Router();
var Category = require("../models/category");
var Content = require("../models/content");
var data;
//处理通用的数据,首页,分类页,每篇文章详情页均需要的变量
router.use(function (req, res, next) {
data = {
userInfo: req.userInfo,
categories: []
};
Category.find().then(function(categories) {
data.categories = categories;
next();
});
});
//渲染首页
router.get("/", function(req, res) {
data.category = req.query.category ||"";
data.count = 0;
data.page = Number(req.query.page || 1);
// 默认两条数据
data.limit = 2;
data.pages = 0;
var where = {};
if (data.category) {
where.category = data.category
}
Content.where(where).count().then(function(count) {
data.count = count;
//计算总页数
data.pages = Math.ceil(data.count / data.limit);
//取值不能超过pages
data.page = Math.min( data.page, data.pages );
//取值不能小于1
data.page = Math.max( data.page, 1 );
var skip = (data.page - 1) * data.limit;
return Content.where(where).find().limit(data.limit).skip(skip).populate(['category', 'user']).sort({
addtime:-1
});
}).then(function(contents) {
data.contents = contents;
//console.log(data);
res.render('main/index', data);
})
});
//进入详细阅读部分
router.get("/article",function(req,res){
var id = req.query.contentid||"";
Content.findOne({_id:id}).populate(["category","user"]).then(function(content){
data.contents = content;
content.num++;
content.save();
//console.log(data);
res.render("main/article_detail",data);
});
});
module.exports = router; |
(五)运行效果
(1)首页
(2)博文详情页面
下篇博文将说明后台管理的设计与实现。

京公网安备 11010502036488号