功能描述及流程在上一篇已经说明,现在来写具体实现过程。

一、页面实现

(1)模板

首页模板main_template.html

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8">
<title>博客后台管理</title> <link rel="stylesheet" href="/public/css/bootstrap.css">
<link rel="stylesheet" href="/public/css/admin.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/admin.js"></script> </head> <body> {% if
userInfo.isadmin %} <nav class="navbar navbar-default"> <div
class="container-fluid"> <!-- Brand and toggle get grouped for better mobile
display --> <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> <a
class="navbar-brand" href="/admin">后台管理</a> </div> <div class="collapse
navbar-collapse" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav">
<li><a href="/admin/user">用户信息</a></li> <li><a
href="/admin/category">所有分类</a></li> <li><a
href="/admin/category/add">添加分类</a></li> <li><a
href="/admin/content">所有博文</a></li> <li><a
href="/admin/content/add">添加博文</a></li> </ul> <ul class="nav navbar-nav
navbar-right"> <li class="dropdown"> <a href="#" class="dropdown-toggle"
data-toggle="dropdown" role="button" aria-haspopup="true"
aria-expanded="false">{{userInfo.username}}<span class="caret"></span></a> <ul
class="dropdown-menu"> <li><a href="/">退出管理</a></li> </ul> </li> </ul> </div>
</div> </nav> {% block main %} {% endblock %} {% else %} <div class="welcome" >
<h1>对不起,你不是管理员!</h1> <p><a class="btn btn-primary btn-lg" href="/"
role="button">返回上一步</a></p> </div> {% endif %} </body> </html>
首页index.html

{% extends "main_template.html" %} {% block main %} <div class="welcome" >
<h1>Welcome,管理员!</h1> <p>欢迎您管理后台!</p> </div> {% endblock %}
用户信息userindex.html

{% extends "main_template.html" %} {% block main %} <ol class="breadcrumb">
<li><a href="#">管理首页</a></li> <li><a href="#">用户信息</a></li> </ol> <table
class="table table-bordered table-hover table-striped" style="color:#fc6423">
<tr > <th>用户ID</th> <th>用户名</th> <th>密码</th> <th>是否为管理员</th> </tr> {% for user
in users %} <tr> <td>{{user._id.toString()}}</td> <td>{{user.username}}</td>
<td>{{user.password}}</td> <td>{{user.isadmin}}</td> </tr> {% endfor %}
</table> <nav aria-label="..."> <ul class="pager"> <li><a
href="/admin/user?page=1">首页</a></li> <li><a
href="/admin/user?page={{page-1}}">上一页</a></li>
<li><span>共{{counts}}条数据</span></li> <li><a
href="/admin/user?page={{page+1}}">下一页</a></li> <li><a
href="/admin/user?page={{total}}">最后一页</a></li> </ul> </nav> {% endblock %}
所有分类category.html

{% extends "main_template.html" %} {% block main %} <ol class="breadcrumb">
<li><a href="#">管理首页</a></li> <li><a href="#">分类管理</a></li> </ol> <table
class="table table-bordered table-hover table-striped" style="color:#fc6423">
<tr > <th>分类ID</th> <th>分类名称</th> <th>分类操作</th> </tr> {% for category in
categories %} <tr> <td>{{category._id.toString()}}</td>
<td>{{category.name}}</td> <td> <a
href="/admin/category/edit?id={{category._id.toString()}}">修改</a> | <a
href="/admin/category/delete?id={{category._id.toString()}}">删除</a> </td> </tr>
{% endfor % </table> {% endblock %}
添加分类addcategory.html

{% extends "main_template.html" %} {% block main %} <ol class="breadcrumb">
<li><a href="#">管理首页</a></li> <li><a href="#">分类管理</a></li> </ol> <form
role="form" method="post" style="font-size: 22px;text-align: center"> <div
class="form-group"> <label for="name">分类名称</label> <input type="text"
class="form-control" name="name" id="name" placeholder="请填写分类名称"> </div>
<button type="submit" class="btn btn-primary btn-md">新增分类</button> </form> {%
endblock %}
分类编辑editcategory.html

{% extends "main_template.html" %} {% block main %} <ol class="breadcrumb">
<li><a href="#">管理首页</a></li> <li><a href="#">分类管理</a></li> </ol> <form
role="form" method="post" style="font-size: 22px;text-align: center"> <div
class="form-group"> <label for="name">分类名称</label> <input type="text"
class="form-control" name="name" id="name" placeholder="请填写新名称"> </div> <button
type="submit" class="btn btn-primary btn-md">提交修改</button> <a class="btn
btn-primary btn-md" href="/admin/category">取消修改</a> </form> {% endblock %}
所有博文content.html

{% extends "main_template.html" %} {% block main %} <ol class="breadcrumb">
<li><a href="#">管理首页</a></li> <li><a href="#">博文管理</a></li> </ol> <table
class="table table-bordered table-hover table-striped" style="color:#fc6423">
<tr > <th>博文ID</th> <th>博文标题</th> <th>所属分类</th> <th>作者</th> <th>添加时间</th>
<th>阅读量</th> <th>博文增删</th> </tr> {% for content in contents %} <tr>
<td>{{content._id.toString()}}</td> <td>{{content.title}}</td>
<td>{{content.category.name}}</td> <td>{{content.user.username}}</td>
<td>{{content.addtime|date('Y-m-d H:i:s', -8*60)}}</td>
<td>{{content.num}}</td> <td> <a
href="/admin/content/edit?id={{content._id.toString()}}">修改</a> | <a
href="/admin/content/delete?id={{content._id.toString()}}">删除</a> </td> </tr>
{% endfor % </table> {% endblock %}
添加博文addcontent.html

{% extends "main_template.html" %} {% block main %} <ol class="breadcrumb">
<li><a href="#">管理首页</a></li> <li><a href="#">添加博文</a></li> </ol> <form
role="form" method="post" style="font-size: 22px;text-align: center"> <div
class="form-group"> <label for="name">博文标题</label> <input type="text"
class="form-control" name="name" id="name" placeholder="请填写博文名称"> </div> <div
class="form-group"> <label for="category">请选择分类</label> <select name="category"
id="category" class="form-control"> {% for category in categories %} <option
value="{{category.id}}">{{category.name}}</option> {% endfor %} </select> <div
class="form-group" style="margin-top: 20px;font-size: 20px;"> <label
for="description">请填写简介</label> <textarea name="description" id="description"
class="form-control" rows="4" placeholder="请输入内容简介" style="overflow:
auto;"></textarea> </div> <div class="form-group" style="margin-top:
20px;font-size: 20px;"> <label for="content">请填写正文</label> <textarea
name="content" id="content" class="form-control" rows="15"
placeholder="请输入内容"></textarea> </div> </div> <button type="submit" class="btn
btn-primary btn-lg" style="margin-top: 50px;">新增博文</button> </form> {% endblock
%}
博文编辑editcontent.html

{% extends "main_template.html" %} {% block main %} <ol class="breadcrumb">
<li><a href="#">管理首页</a></li> <li><a href="#">修改博文</a></li> </ol> <form
role="form" method="post" style="font-size: 22px;text-align: center"> <div
class="form-group"> <label for="name">博文标题</label> <input type="text"
class="form-control" name="name" id="name" value="{{info.title}}"> </div> <div
class="form-group"> <label for="category">请选择分类</label> <select name="category"
id="category" class="form-control"> {% for category in categories %} {% if
info.category._id.toString() == category._id.toString() %} <option
value="{{category.id}}" selected>{{category.name}}</option> {% else %}} <option
value="{{category.id}}" >{{category.name}}</option> {% endif %} {% endfor %}
</select> <div class="form-group" style="margin-top: 20px;font-size: 20px;">
<label for="description">请填写简介</label> <textarea name="description"
id="description" class="form-control" rows="4" style="overflow:
auto;">{{info.description}}</textarea> </div> <div class="form-group"
style="margin-top: 20px;font-size: 20px;"> <label for="content">请填写正文</label>
<textarea name="content" id="content" class="form-control" rows="15"
placeholder="请输入内容">{{info.composition}}</textarea> </div> </div> <button
type="submit" class="btn btn-primary btn-lg" style="margin-top:
50px;">提交修改</button> </form> {% endblock %}
删除分类确认页面confirm.html

{% extends "main_template.html" %} {% block main %} <div class="welcome" >
<h1>您确定删除?</h1> <form role="form" method="post" style="font-size:
22px;text-align: center" action="/admin/category/delete?id={{id}}"> <button
type="submit" class="btn btn-primary btn-lg">确定!</button> <a class="btn
btn-primary btn-lg" href="/admin/category">再想想</a> </form> </div> {% endblock %}
删除博文确认页面confirm2.html

{% extends "main_template.html" %} {% block main %} <div class="welcome" >
<h1>您确定删除?</h1> <form role="form" method="post" style="font-size:
22px;text-align: center" action="/admin/content/delete?id={{id}}"> <button
type="submit" class="btn btn-primary btn-lg">确定!</button> <a class="btn
btn-primary btn-lg" href="/admin/content">再想想</a> </form> </div> {% endblock %}
操作成功页面success.html

{% extends "main_template.html" %} {% block main %} <div class="welcome" >
<h1>操作成功,请点击返回上一页面</h1> <p><a class="btn btn-primary btn-lg"
href="/admin/category" role="button">返回</a></p> </div> {% endblock %}
操作失败页面error.html

{% extends "main_template.html" %} {% block main %} <div class="welcome" >
<h1>操作无效,请返回并重试!</h1> <p><a class="btn btn-primary btn-lg"
href="/admin/category/add" role="button">返回上一步</a></p> </div> {% endblock %}
(2)样式文件

admin.css

.welcome { text-align: center; } .table th, .table td { text-align: center;
vertical-align: middle!important; } .table{ table-layout: fixed; } .pager{
font-size: 20px; }
二、routers代码

admin.js

let express = require("express"); let router = express.Router(); let User =
require("../models/user"); let Category = require("../models/category"); let
Content = require("../models/content"); router.get("/",function(req,res){
res.render("admin/index",{userInfo:req.userInfo}); });
router.get("/user",function(req,res){ let page = Number(req.query.page||1); //
默认每页显示8条数据 let limit = 8; let skip = (page-1)*limit; let total; let counts;
User.count().then(function(count){ total = Math.ceil(count/limit); page =
Math.max(1,page); page = Math.min(page,total); counts = count; });
User.find().limit(limit).skip(skip).then(function(users){
res.render("admin/userindex",{ userInfo:req.userInfo, users:users, page:page,
total:total, counts:counts }) }); }); router.get("/category",function(req,res){
Category.find().sort({_id:-1}).then(function(categories){
res.render("admin/category",{ userInfo:req.userInfo, categories:categories });
}); }); router.get("/category/add",function(req,res){
res.render("admin/addcategory",{userInfo:req.userInfo}); });
router.post("/category/add",function(req,res){ let name =req.body.name||"";
if(name==""){ res.render("admin/error",{userInfo:req.userInfo}); } else{
Category.findOne({name:name},function(err,info){ if(err){ console.log(err); }
if(info){ res.render("admin/error",{userInfo:req.userInfo}); return false; }
let newcate = new Category({ name:name }); newcate.save();
res.render("admin/success",{userInfo:req.userInfo}); }); } });
router.get("/category/edit",function(req,res){ let cateid = req.query.id||"";
Category.find({id:cateid}).then(function(cateinfo){
res.render("admin/categoryedit",{ userInfo:req.userInfo , name:cateinfo.name
}); }); }); router.post("/category/edit",function(req,res){ let name
=req.body.name||""; let id = req.query.id||""; if(name==""){
res.render("admin/error",{userInfo:req.userInfo}); return false; }else{
Category.findOne({_id:id},function(err,info){ if(err){ console.log(err); }
if(info){ console.log(info); info.name = name; info.save();
res.render("admin/success",{userInfo:req.userInfo}); } }); } });
router.get("/category/delete",function(req,res){ let id = req.query.id||"";
//console.log(id); res.render("admin/confirm",{ userInfo:req.userInfo, id:id
}); }); router.post("/category/delete",function(req,res){ let id =
req.query.id||""; //console.log(id); Category.remove({_id:id}).then(function(){
res.render("admin/success",{ userInfo:req.userInfo }); }); });
router.get("/content",function(req,res){
Content.find().populate(["category","user"]).sort({_id:-1}).then(function(contents){
res.render("admin/content",{ userInfo:req.userInfo, contents:contents }); });
}); router.get("/content/add",function(req,res){ let cate=null;
Category.find().then(function(categories){ cate = categories;
res.render("admin/addcontent",{ userInfo:req.userInfo, categories :cate }); });
}); router.post("/content/add",function(req,res){ let title =
req.body.name||""; let category = req.body.category||""; let description =
req.body.description||""; let content = req.body.content||"";
if(title==""||category==""||description==""||content==""){
res.render("admin/addok",{ userInfo:req.userInfo, message:"还有未填入的信息,请重新填入!" });
return false; }else { let newcontent = new Content({ title:title,
category:category, description:description, composition:content, addtime:new
Date(), num:0, user :req.userInfo._id.toString() }); newcontent.save();
res.render("admin/addok",{ userInfo:req.userInfo, message : "更新博文成功" }); } });
router.get("/content/edit",function(req,res){ let cate=null; let id =
req.query.id||""; Category.find().then(function(categories){ cate = categories;
}); Content.findOne({_id:id}).populate("category").then(function(info){
//console.log(info); res.render("admin/editcontent",{ userInfo:req.userInfo,
info :info, categories:cate }); }); });
router.post("/content/edit",function(req,res){ let id = req.query.id||""; let
title = req.body.name||""; let category = req.body.category||""; let
description = req.body.description||""; let content = req.body.content||"";
if(title==""||category==""||description==""||content==""){
res.render("admin/addok",{ userInfo:req.userInfo, message:"无效的修改,请重新修改!" });
return false; }else { Content.update({_id:id},{ title : title, category:
category, description : description, content : content, addtime:new Date(),
user :req.userInfo._id.toString() }).then(function(){
res.render("admin/addok",{ userInfo:req.userInfo, message : "修改成功!" }); }); }
}); router.get("/content/delete",function(req,res){ let id = req.query.id||"";
res.render("admin/confirm2",{ userInfo:req.userInfo, id:id }); });
router.post("/content/delete",function(req,res){ let id = req.query.id||"";
//console.log(id); Content.remove({_id:id}).then(function(){
res.render("admin/addok",{ userInfo:req.userInfo, message : "删除成功!点击返回上一页!" });
}); }); module.exports = router;
三、运行效果













四、项目总结


至此,这个简单的项目就完结了,作为一个入门项目,简单是必须的,不过,通过这个我也学到了express框架、mongodb、mongoose数据库框架的基本使用,express框架作为一个轻量级的web框架,小而快是它的特性。这个项目也有了最基本的业务逻辑,前端向后端提交数据,提交方法根据不同需求可以是get、post、put、delete方法,其中本项目没有使用到后面两个方法,后端收到前端提交的数据后,用body-parse框架去获取发送过来的数据,这些数据由业务逻辑代码作相应的解析和格式化后请求数据库信息并反馈给前端,前端用swig模板引擎去作渲染,这就是体现了一个web应用的基本数据传递的流程。当然,nodejs和express以及mongoose还有很多强大的功能待作者后续研究,以上便是我对这个小项目的总结。所有的代码都贴出来了,如果还需要源码请转:
https://download.csdn.net/download/weixin_42363997/10745703
<https://download.csdn.net/download/weixin_42363997/10745703>