黑马博客管理系统MySql + Express + art-template项目搭建(源码)

  • Post author:
  • Post category:mysql


前言

关于搭建本系统要用到:

mysql数据库



exprss框架,art-template模板引擎,session会话,joi验证

(由于博主垃圾电脑无法装载

bcrypt

,所以

不作加密处理

),有疑惑或有问题欢迎提问和指出(可以私信)。

多人博客管理系统:博客内容展示,博客管理功能

项目所需文件和插件

插件

文件

js源码部分(带注释)

主路由

sever.js
const express = require("express");
const session = require("express-session");
const dateFormat = require("dateformat");
const template = require("art-template");
const home = require("./route/home");
const admin = require("./route/admin");
const bodyParser = require("body-parser");
const path = require("path");
const app = express();
app.use(express.static(path.join(__dirname, "public")));
app.use(bodyParser.urlencoded({ extended: false }));
// 配置 session
app.use(
  session({
    secret: "secret key",
    saveUninitialized: false,
    //cookie存在时间为一天
    cookie: {
      maxAge: 24 * 60 * 60 * 1000,
    },
  })
);
// 拦截请求,判断用户登录状态
app.use("/admin", require("./middleware/loginGuard"));
// 前台路由
app.use("/home", home);
// 后台路由
app.use("/admin", admin);
//集中处理错误请求
app.use((err, req, res, next) => {
  const result = JSON.parse(err);
  let params = [];
  for (const attr in result) {
    if (attr != "path") {
      params.push(attr + "=" + result[attr]);
    }
  }
  res.redirect(`${result.path}?${params.join("&")}`);
});
app.set("views", path.join(__dirname, "views"));
app.set("view engine", "art");
app.engine("art", require("express-art-template"));
// 向模板内部导入dateFormate变量
template.defaults.imports.dateFormat = dateFormat;

app.listen("3000", () => {
  console.log("服务器启动成功");
});

common.js数据库连接
const mysql = require("mysql");

const connect = mysql.createConnection({
  host: "localhost",
  port: 3306,
  user: "这里填自己MySQL的用户",
  password: "这里填自己MySQL的密码",
  database: "数据库名字",
});
connect.connect();
/**
 *@function 获取MySQL数据库中的数函
 *@param  sql一条MySQL语句
 *@return Promise对象,异步函数
 @example await {result} = db("select * from user").then((result) => {
   return result;
 });
 **/
function db(sql) {
  return new Promise((resolve, reject) => {
    connect.query(sql, (err, result) => {
      if (err) {
        reject(err);
      } else {
        resolve(result);
      }
    });
  });
}

module.exports = { db };
loginGuard.js
const guard = (req, res, next) => {
  // 判断用户访问的是否是登录页面
  // 判断用户的登录状态
  // 如果用户是登录的,将请求放行,向下执行;如果用户不是登录的,则将请求重定向到登录页
  if (req.url != "/login" && !req.session.username) {
    // 重定向到登录页
    return res.redirect("/admin/login");
  } else {
    //判断用户是否是管理员
    if (req.session.role == "normal") {
      return res.redirect("/home/");
    }
    // 用户是登录的,将请求放行,向下执行
    next();
  }
};

module.exports = guard;

admin后台js

admin.js

const express = require("express");
const common = require("../model/common");

const admin = express.Router();
//渲染登录页面
admin.get("/login", require("./admin/loginup"));
// 实现登录功能
admin.post("/login", require("./admin/login"));

//渲染用户页面
admin.get("/user", require("./admin/userPage"));
// 实现退出功能
admin.get("/logout", require("./admin/logout"));
// 渲染创建用户列表路由
admin.get("/user-edit", require("./admin/userEdit"));
// 创建实现添加用户功能
admin.post("/user-edit", require("./admin/user-edit-fn"));
// 创建实现修改用户功能
admin.post("/user-modify", require("./admin/user-modify"));
// 创建实现删除用户功能
admin.get("/delete", require("./admin/user-delete"));

//文章列表界面
admin.get("/article", require("./admin/article"));
//文章编辑界面
admin.get("/article-edit", require("./admin/article-edit"));

// 实现文章添加功能的路由
admin.post("/article-add", require("./admin/article-add"));
module.exports = admin;

home.js

const express = require("express");
const home = express.Router();

// 博客前台首页的展示页面
home.get("/", require("./home/index"));

// 博客前台文章详情展示页面
home.get("/article", require("./home/article"));

// 创建评论功能路由
home.post("/comment", require("./home/comment"));

module.exports = home;

登录

login.js
const express = require("express");
const common = require("../../model/common");
const login = express.Router();

login.post("/login", (req, res) => {
  const { email, password } = req.body;
  let sql = `select * from user where email='${email}'`;
  //判断邮件和密码是否为空
  if (email.trim().length == 0) {
    res.status(400).render("admin/error.art", { msg: "邮件地址或密码错误" });
  }
  //实现登录
  common.db(sql).then((result) => {
    if (result.length > 0) {
      result.forEach((v) => {
        if (password == v.password) {
          //将用户名存储到req请求中
          req.session.username = v.username;
          req.session.role = v.role;
          req.app.locals.userInfo = v;
          //判断用户是否是管理员,是就可以进入后台
          if (v.role == "admin") {
            res.redirect("/admin/user");
          } else {
            res.redirect("/home/");
          }
        } else {
          res
            .status(400)
            .render("admin/error.art", { msg: "邮件地址或密码错误" });
        }
      });
    } else {
      // 没有查询到用户
      res.status(400).render("admin/error.art", { msg: "邮件地址或密码错误" });
    }
  });
});

module.exports = login;
loginup.js
module.exports = (req, res) => {
  res.render("admin/login.art");
};
logout.js
module.exports = (req, res) => {
  // 删除 session
  req.session.destroy(function () {
    // 删除 cookie
    res.clearCookie("connect.sid");
    // 清除模板中的用户信息
    req.app.locals.userInfo = null;
    // 重定向到登陆页面
    res.redirect("/admin/login");
  });
};

用户

user-delete.js
const common = require("../../model/common");
module.exports = (req, res) => {
  common.db(`delete from user where _id='${req.query.id}'`);
  res.redirect("/admin/user");
};
user-edit-fn.js
// 引入 joi 模块
const Joi = require("joi");
const common = require("../../model/common");

module.exports = (req, res, next) => {
  common
    .db(`select * from user where email='${req.body.email}'`)
    .then(async (result) => {
      if (result.length > 0) {
        return res.redirect(`/admin/user-edit?message=邮箱地址已经被占用`);
      } else {
        // 定义对象的验证规则
        const schema = {
          username: Joi.string()
            .alphanum()
            .min(2)
            .max(12)
            .required()
            .error(new Error("用户名不符合验证规则")),
          email: Joi.string()
            .email()
            .required()
            .error(new Error("邮箱格式不符合验证规则")),
          password: Joi.string()
            .regex(/^[a-zA-Z0-9]{3,30}$/)
            .required()
            .error(new Error("密码格式不符合验证规则")),
          role: Joi.string()
            .valid("normal", "admin")
            .required()
            .error(new Error("角色值非法")),
          state: Joi.number()
            .valid(0, 1)
            .required()
            .error(new Error("状态值非法")),
        };

        try {
          // 实施验证
          await Joi.validate(req.body, schema);
        } catch (err) {
          // 验证没有通过
          // 重定向回用户添加页面,可以将这一类错误用一个中间件集中处理
          // res.redirect('/admin/user-edit?message=' + err.message);
          let obj = { path: "/admin/user-edit", message: err.message };
          next(JSON.stringify(obj));
          return;
        }
        // 将新用户信息添加到数据库中
        common.db(
          `insert into user values(null,'${req.body.username}','${req.body.email}','${req.body.password}','${req.body.role}','${req.body.state}')`
        );
        // 将页面重定向到用户列表页
        res.redirect("/admin/user");
      }
    });
};
user-modify.js
const common = require("../../model/common");

module.exports = (req, res, next) => {
  const body = req.body;
  const id = req.query.id;

  common.db(`select * from user where _id=${id}`).then((result) => {
    result.forEach((v) => {
      if (v.password == body.password) {
        let sql = `update user set _id=${id},username='${body.username}',email='${body.email}',password='${body.password}',role='${body.role}',state='${body.state}' where _id=${id};`;
        common.db(sql);
        res.redirect("/admin/user");
      } else {
        let obj = {
          path: "/admin/user-edit",
          message: "密码错误,不能修改用户数据",
          id: id,
        };
        next(JSON.stringify(obj));
      }
    });
  });
};
userEdit.js
const common = require("../../model/common");
module.exports = (req, res) => {
  // 标识 标识当前访问的是用户管理页面,
  req.app.locals.currentLink = "user";
  const { message, id } = req.query;
  if (id) {
    common.db(`select * from user where _id=${id}`).then((user) => {
      user.forEach((v) => {
        //修改
        return res.render("admin/user-edit.art", {
          message: message,
          user: v,
          link: `/admin/user-modify?id=${id}`,
          button: "修改",
        });
      });
    });
  } else {
    //添加
    return res.render("admin/user-edit.art", {
      message: message,
      link: "/admin/user-edit",
      button: "添加",
    });
  }
};
userPage.js
const common = require("../../model/common");

module.exports = async (req, res) => {
  // 标识 标识当前访问的是用户管理页面,
  req.app.locals.currentLink = "user";
  let page = req.query.page || 1;
  //每一页的数据条数
  let pagesize = 10;

  common.db("select * from user").then((result) => {
    let count = result.length;
    //总页数
    let total = Math.ceil(count / pagesize);
    let start = (page - 1) * pagesize;
    let sql = `select * from user limit ${start},${pagesize}`;
    common.db(sql).then((users) => {
      res.render("admin/user.art", {
        msg: req.session.username,
        users: users,
        page: page,
        total: total,
      });
    });
  });
};

文章

article.js
const common = require("../../model/common");
module.exports = async (req, res) => {
  // 标识 标识当前访问的是文章页面,
  req.app.locals.currentLink = "article";
  //当前页数
  let page = req.query.page || 1;
  //每一页的数据条数
  let pagesize = 10;
  let { total, start } = await common
    .db("select * from article")
    .then((result) => {
      if (result.length <= 0)
        return res.render("./admin/article.art", { articles: {} });
      //数据库中数据个数
      let count = result.length;
      //总页数
      let total = Math.ceil(count / pagesize);
      let start = (page - 1) * pagesize;
      return { total: total, start: start };
    });
  //从MySQL数据库中限制获取到的数据,限制查询,需要多少查多少
  let sql = `select * from article limit ${start},${pagesize}`;
  //从数据库中拿到分页数据
  let articles = await common.db(sql).then((articles) => {
    return articles;
  });
  res.render("./admin/article.art", {
    articles: articles,
    page: page,
    total: total,
  });
};
article-add.js
const formidable = require("formidable");
const path = require("path");
const common = require("../../model/common");
module.exports = (req, res) => {
  // 创建表单解析对象
  const form = new formidable.IncomingForm();
  // 配置上传文件的存放位置
  form.uploadDir = path.join(__dirname, "../", "../", "public", "uploads");
  // 保留上传文件的后缀

  form.options.keepExtensions = true;

  form.parse(req, (err, fields, files) => {
    //对文件名进行处理,因为'\'会杯MySQL数据库吞掉
    let str = files.cover._writeStream.path.split("public")[1];
    let newStr = str.slice(0, 8) + "/" + str.slice(8);
    //日期如果没有填就默认CURRENT_TIMESTAMP,数据库会自动处理
    let publishDate =
      fields.publishDate == "" ? "CURRENT_TIMESTAMP" : fields.publishDate;
    //写入
    if (publishDate == "CURRENT_TIMESTAMP") {
      common.db(
        `insert into article values(null,'${fields.title}','${fields.author}',${publishDate},'${newStr}','${fields.content}')`
      );
    } else {
      common.db(
        `insert into article values(null,'${fields.title}','${fields.author}','${publishDate}','${newStr}','${fields.content}')`
      );
    }
    // 将页面重定向到文章列表页面
    res.redirect("/admin/article");
  });
};
article-edit.js
module.exports = (req, res) => {
  // 标识 标识当前访问的是文章页面
  req.app.locals.currentLink = "article";
  res.render("./admin/article-edit.art");
};

home前台js

article.js

const common = require("../../model/common");

module.exports = async (req, res) => {
  let id = req.query.id;
  if (id == undefined) {
    return res.redirect("./");
  }
  //查找对应文章
  let article = await common
    .db(`select * from article where _id=${id}`)
    .then((article) => {
      return article;
    });
  //查找对应文章下的评论
  let comments = await common
    .db(`select * from comment where aid=${id}`)
    .then((comments) => {
      return comments;
    });
  for (let i = 0; i < comments.length; i++) {
    //通过评论的uid查找用户
    let user = await common
      .db(`select * from user where _id=${comments[i].uid}`)
      .then((user) => {
        return user;
      });
    for (let j = 0; j < user.length; j++) {
      comments[i].uid = user[j];
    }
  }

  res.render("home/article.art", {
    article: article[0],
    comments: comments,
  });
};

comment.js

const comment = require("../../model/common");

module.exports = (req, res) => {
  const { content, uid, aid } = req.body;
  if (content == "") return res.redirect("/home/article?id=" + aid);
  let date = new Date(); //返回当前的时间
  let time = `${date.getFullYear()}-${
    date.getMonth() + 1
  }-${date.getDate()} ${date.getHours()}:${date.getMinutes()}`;
  comment.db(
    `insert into comment values('${aid}','${uid}','${time}','${content}',null)`
  );
  //重定向到文章页面
  res.redirect("/home/article?id=" + aid);
};

index.js

const common = require("../../model/common");

module.exports = async (req, res) => {
  let page = req.query.page || 1;
  //每一页的数据条数
  let pagesize = 4;
  let { total = 0, start = 0 } = await common
    .db("select * from article")
    .then((result) => {
      if (result.length <= 0)
        return res.render("./admin/article.art", { articles: {} });
      let count = result.length;
      //总页数
      let total = Math.ceil(count / pagesize);
      let start = (page - 1) * pagesize;
      return { total: total, start: start };
    });
  let sql = `select * from article limit ${start},${pagesize}`;
  //从数据库中拿到分页数据
  let result = await common.db(sql).then((result) => {
    return result;
  });
  res.render("home/default.art", {
    result: result,
    page: page,
    total: total,
  });
};

html源码部分

admin后台html

common 公共部分

aside

<!-- 侧边栏 -->
<div class="aside fl">
    <ul class="menu list-unstyled">
        <li>
            <a class="item {{currentLink == 'user' ? 'active' : ''}}" href="/admin/user">
                <span class="glyphicon glyphicon-user"></span>
                用户管理
            </a>
        </li>
        <li>
            <a class="item {{currentLink == 'article' ? 'active' : ''}}" href="/admin/article">
                  <span class="glyphicon glyphicon-th-list"></span>
                  文章管理
              </a>
        </li>
    </ul>
    <div class="cprt">
        Powered by <a href="http://www.itheima.com/" target="_blank">黑马程序员</a>
    </div>
</div>
<!-- 侧边栏 -->

header

<!-- 头部 -->
<div class="header">
    <!-- 网站标志 -->
    <div class="logo fl">
      博客后台
    </div>
    <!-- /网站标志 -->
    <!-- 用户信息 -->
    <div class="info">
        <div class="profile dropdown fr">
            <span class="btn dropdown-toggle" data-toggle="dropdown">
                {{userInfo && userInfo.username}}
                <span class="caret"></span>
            </span>
            <ul class="dropdown-menu">
                <li><a href="javascript:alert('本课程不含此功能#user-edit')">个人资料</a></li>
                <li><a href="/admin/logout">退出登录</a></li>
            </ul>
        </div>
    </div>
    <!-- /用户信息 -->
</div>
<!-- /头部 -->

layout

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <title>Blog - Content Manager</title>
    <link rel="stylesheet" href="/admin/lib/bootstrap/css/bootstrap.min.css">
    <link rel="stylesheet" href="/admin/css/base.css">
    {{block 'link'}}{{/block}}
</head>

<body>
    {{block 'main'}} {{/block}}
    <script src="/admin/lib/jquery/dist/jquery.min.js"></script>
    <script src="/admin/lib/bootstrap/js/bootstrap.min.js"></script>
    <script src="/admin/js/common.js"></script>
    {{block 'script'}} {{/block}}
</body>

</html>

article-edit

{{extend './common/layout.art'}}

{{block 'main'}}
    {{include './common/header.art'}}
    <!-- 主体内容 -->
    <div class="content">
        {{include './common/aside.art'}}
        <div class="main">
            <!-- 分类标题 -->
            <div class="title">
                <h4>文章编辑</h4>
            </div>
            <!--
                enctype 指定表单数据的编码类型
                    application/x-www-form-urlencoded
                        name=zhangsan&age=20
                    multipart/form-data 将表单数据编码成二进制类型
            -->
            <!-- /分类标题 -->
            <form class="form-container" action="/admin/article-add" method="post" enctype="multipart/form-data">
                <div class="form-group">
                    <label>标题</label>
                    <input type="text" class="form-control" placeholder="请输入文章标题" name="title" required>
                </div>
                <div class="form-group">
                    <label>作者</label>
                    <input name="author" type="text" class="form-control" readonly value="{{@userInfo.username}}">
                </div>
                <div class="form-group">
                    <label>发布时间</label>
                    <input name="publishDate" type="date" class="form-control">
                </div>
                
                <div class="form-group">
                   <label for="exampleInputFile">文章封面</label>
                   <!--
                        multiple 允许用户一次性选择多个文件
                   -->
                   <input type="file" name="cover" id="file" required>
                   <div class="thumbnail-waper">
                       <img class="img-thumbnail" src="" id="preview">
                   </div>
                </div>
                <div class="form-group">
                    <label>内容</label>
                    <textarea name="content" class="form-control" id="editor"></textarea>
                </div>
                <div class="buttons">
                    <input type="submit" class="btn btn-primary">
                </div>
            </form>
            
        </div>
    </div>
    <!-- /主体内容 -->
{{/block}}

{{block 'script'}}
    <script src="/admin/lib/ckeditor5/ckeditor.js"></script>
    <script type="text/javascript">
    
        let editor;

        ClassicEditor
                .create( document.querySelector('#editor'))
                .then(newEditor => {
                    editor = newEditor;
                })
                .catch( error => {
                    console.error( error );
                });

        // 获取数据
        // const editorData = editor.getData();
        
        // 选择文件上传控件
        var file = document.querySelector('#file');
        var preview = document.querySelector('#preview');
        // 当用户选择完文件以后
        file.onchange = function () {
            // 1 创建文件读取对象
            var reader = new FileReader();
            // 用户选择的文件列表
            // console.log(this.files[0])
            // 2 读取文件
            reader.readAsDataURL(this.files[0]);
            // 3 监听onload事件
            reader.onload = function () {
                console.log(reader.result)
                // 将文件读取的结果显示在页面中
                preview.src = reader.result;
            }
        }

    </script>
{{/block}}

article

{{extend './common/layout.art'}}

{{block 'main'}}
{{include './common/header.art'}}
<!-- 主体内容 -->
<div class="content">
  {{include './common/aside.art'}}
  <div class="main">
    <!-- 分类标题 -->
    <div class="title">
      <h4>文章</h4>
      <span>找到1篇文章</span>
      <a href="/admin/article-edit" class="btn btn-primary new">发布新文章</a>
    </div>
    <!-- /分类标题 -->
    <!-- 内容列表 -->
    <table class="table table-striped table-bordered table-hover custom-table">
      <thead>
        <tr>
          <th>ID</th>
          <th>标题</th>
          <th>发布时间</th>
          <th>作者</th>
          <th>操作</th>
        </tr>
      </thead>
      <tbody>
        {{each articles}}
        <tr>
          <td>{{@$value._id}}</td>
          <td>{{ $value.title }}</td>
          <td>{{ dateFormat($value.publishDate, "yyyy-mm-dd") }}</td>
          <td>{{ $value.author }}</td>
          <td>
            <a
              href="javascript:alert('本课程不含此功能#article-edit')"
              class="glyphicon glyphicon-edit"
            ></a>
            <i
              class="glyphicon glyphicon-remove"
              data-toggle="modal"
              data-target=".confirm-modal"
            ></i>
          </td>
        </tr>
        {{/each}}
      </tbody>
    </table>
    <!-- /内容列表 -->
    <!-- 分页 -->
    <ul class="pagination">
      <li style="display: <%=page-1 < 1 ? 'none' : 'inline' %>">
        <a href="/admin/article?page=<%=page-1%>">
          <span>&laquo;</span>
        </a>
      </li>
      <% for (var i = 1; i <= total; i++) { %>
      <li>
        <a href="/admin/article?page=<%=i %>">{{ i }}</a>
      </li>
      <% } %>
      <li style="display: <%= page-0+1 > total ? 'none' : 'inline' %>">
        <a href="/admin/article?page=<%=page-0+1%>">
          <span>&raquo;</span>
        </a>
      </li>
    </ul>
    <!-- /分页 -->
  </div>
</div>
<!-- /主体内容 -->
<!-- 删除确认弹出框 -->
<div class="modal fade confirm-modal">
  <div class="modal-dialog modal-lg">
    <form class="modal-content">
      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal">
          <span>&times;</span>
        </button>
        <h4 class="modal-title">请确认</h4>
      </div>
      <div class="modal-body">
        <p>您确定要删除这篇文章吗?</p>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-default" data-dismiss="modal">
          取消
        </button>
        <input
          type="submit"
          onclick="alert('本课程不含此功能');return false"
          class="btn btn-primary"
        />
      </div>
    </form>
  </div>
</div>
{{/block}}

error

{{extend './common/layout.art'}}

{{block 'main'}}
    <p class="bg-danger error">{{msg}}</p>
{{/block}}

{{block 'script'}}
    <script type="text/javascript">
        setTimeout(function () {
            location.href = '/admin/login';
        }, 3000)
    </script>
{{/block}}

login

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>用户登录</title>
    <link rel="stylesheet" href="/admin/lib/bootstrap/css/bootstrap.min.css">
    <link rel="stylesheet" href="/admin/css/base.css">
    <!-- 登录页样式美化(可选) -->
    <link rel="stylesheet" href="/admin/css/login.css">
</head>

<body>
    <div class="login-body">
        <div class="login-container">
            <h4 class="title">登录</h4>
            <div class="login">
                <form action="http://localhost:3000/admin/login" method="post" id="loginForm">
                    <div class="form-group">
                        <label>邮箱</label>
                        <input name="email" type="email" class="form-control" placeholder="请输入邮箱">
                    </div>
                    <div class="form-group">
                        <label>密码</label>
                        <input name="password" type="password" class="form-control" placeholder="请输入密码">
                    </div>
                    <div class="savepass">
                        <div class="savepass-check">记住密码</div>
                        <div class="savepass-a">忘记密码?</div>
                    </div>
                    <button type="submit" class="btn btn-primary">登录</button>
                </form>
            </div>
            <div class="tips"></div>
        </div>
    </div>
    <script src="/admin/lib/jquery/dist/jquery.min.js"></script>
    <script src="/admin/lib/bootstrap/js/bootstrap.min.js"></script>
    <script src="/admin/js/common.js"></script>
    <script type="text/javascript">
        // 为表单添加提交事件
        $('#loginForm').on('submit', function () {
            // 获取到表单中用户输入的内容
            var result = serializeToJson($(this))
            // 如果用户没有输入邮件地址的话
            if (result.email.trim().length == 0) {
                alert('请输入邮件地址');
                // 阻止程序向下执行
                return false;
            }
            // 如果用户没有输入密码
            if (result.password.trim().length == 0) {
                alert('请输入密码')
                // 阻止程序向下执行
                return false;
            }
        });
    </script>
</body>
</html>

user-edit

{{extend './common/layout.art'}}

{{block 'main'}}
    {{include './common/header.art'}}
    <!-- /头部 -->
    <!-- 主体内容 -->
    <div class="content">
        {{include './common/aside.art'}}
        <div class="main">
            <!-- 分类标题 -->
            <div class="title">
                <h4 style="display: {{button == '修改' ? 'block' : 'none'}}">{{@user && user._id}}</h4>
                <p class="tips">{{message}}</p>
            </div>
            <!-- /分类标题 -->
            <form class="form-container" action="{{link}}" method="post">
                <div class="form-group">
                    <label>用户名</label>
                    <input name="username" type="text" class="form-control" placeholder="请输入用户名" value="{{user && user.username}}">
                </div>
                <div class="form-group">
                    <label>邮箱</label>
                    <input type="email" class="form-control" placeholder="请输入邮箱地址" name="email" value="{{user && user.email}}">
                </div>
                <div class="form-group">
                    <label>密码</label>
                    <input type="password" class="form-control" placeholder="请输入密码" name="password">
                </div>
                <div class="form-group">
                    <label>角色</label>
                    <select class="form-control" name="role">
                        <option value="normal" {{user && user.role == 'normal' ? 'selected' : ''}}>普通用户</option>
                        <option value="admin" {{user && user.role == 'admin' ? 'selected' : ''}}>超级管理员</option>
                    </select>
                </div>
                <div class="form-group">
                    <label>状态</label>
                    <select class="form-control" name="state">
                        <option value="0" {{user && user.state == '0' ? 'selected' : ''}}>启用</option>
                        <option value="1" {{user && user.state == '1' ? 'selected' : ''}}>禁用</option>
                    </select>
                </div>
                <div class="buttons">
                    <input type="submit" class="btn btn-primary" value="{{button}}">
                </div>
            </form>
        </div>
    </div>
{{/block}} 

user

{{extend './common/layout.art'}}

{{block 'main'}}
    <!-- 子模板的相对路径相对的就是当前文件 因为它是由模板引擎解析的 而不是浏览器 -->
    {{include './common/header.art'}}
    <!-- 主体内容 -->
    <div class="content">
        {{include './common/aside.art'}}
        <div class="main">
            <!-- 分类标题 -->
            <div class="title">
                <h4>用户</h4>
               <span>找到{{users.length}}个用户</span>
                <a href="/admin/user-edit" class="btn btn-primary new">新增用户</a>
            </div>
            <!-- /分类标题 -->
            <!-- 内容列表 -->
            <table class="table table-striped table-bordered table-hover custom-table">
                <thead>
                    <tr>
                        <th>ID</th>
                        <th>用户名</th>
                        <th>邮箱</th>
                        <th>角色</th>
                        <th>状态</th>
                        <th>操作</th>
                    </tr>
                </thead>
                <tbody>
                    {{each users}}
                    <tr>
                        <td>{{@$value._id}}</td>
                        <td>{{$value.username}}</td>
                        <td>{{$value.email}}</td>
                        <td>{{$value.role == 'admin' ? '超级管理员': '普通用户'}}</td>
                        <td>{{$value.state == 0 ? '启用': '禁用'}}</td>
                        <td>
                            <a href="/admin/user-edit?id={{@$value._id}}" class="glyphicon glyphicon-edit"></a>
                            <i class="glyphicon glyphicon-remove delete" data-toggle="modal" data-target=".confirm-modal" data-id="{{@$value._id}}"></i>
                        </td>
                    </tr>
                    {{/each}}
                </tbody>
            </table>
            <!-- /内容列表 -->
            <!-- 分页 -->
            <ul class="pagination">
                <li style="display: <%=page-1 < 1 ? 'none' : 'inline' %>">
                    <a href="/admin/user?page=<%=page-1%>">
                    <span>&laquo;</span>
                  </a>
                </li>
                <% for (var i = 1; i <= total; i++) { %>
                <li><a href="/admin/user?page=<%=i %>">{{i}}</a></li>
                <% } %>
                <li style="display: <%= page-0+1 > total ? 'none' : 'inline' %>">
                    <a href="/admin/user?page=<%=page-0+1%>">
                    <span>&raquo;</span>
                  </a>
                </li>
            </ul>
            <!-- /分页 -->
        </div>
    </div>
    <!-- /主体内容 -->
    <!-- 删除确认弹出框 -->
    <div class="modal fade confirm-modal">
        <div class="modal-dialog modal-lg">
            <form class="modal-content" action="/admin/delete" method="get">
                <div class="modal-header">
                    <button type="button" class="close" data-dismiss="modal"><span>&times;</span></button>
                    <h4 class="modal-title">请确认</h4>
                </div>
                <div class="modal-body">
                    <p>您确定要删除这个用户吗?</p>
                    <input type="hidden" name="id" id="deleteUserId">
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
                    <input type="submit" class="btn btn-primary">
                </div>
            </form>
        </div>
    </div>
{{/block}}

{{block 'script'}}
    <script type="text/javascript">
        $('.delete').on('click', function () {
            // 获取用户id
            var id = $(this).attr('data-id');
            // 将要删除的用户id存储在隐藏域中
            $('#deleteUserId').val(id);
        })
    </script>
{{/block}}

home前台html

common 公共部分

header

<!-- 头部框架开始 -->
<div class="header">
    <div class="w1100">
        <!-- 网站logo开始 -->
        <h1 class="logo fl">
            <a href="/home/" style="color:white;margin-top:12px;">博客前台</a>
        </h1>
        <!-- 网站logo结束 -->
        <!-- 网站导航开始 -->
        <ul class="navigation fr">
            <li>
                <a class="active" href="/home/">首页</a>
            </li>
            <li>
                <a href="/admin/login">登录</a>
            </li>
        </ul>
        <!-- 网站导航结束 -->
    </div>
</div>
<!-- 头部框架结束 -->

layout

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
    <link rel="stylesheet" href="/home/css/base.css">
    {{block 'link'}}{{/block}}
</head>
<body>
    {{block 'main'}}{{/block}}
</body>
</html>

article

{{extend './common/layout.art'}}

{{block 'link'}}
    <link rel="stylesheet" href="/home/css/article.css">
{{/block}}

{{block 'main'}}
    {{include './common/header.art'}}
    <!-- 文章框架开始 -->
    <div class="article">
        <div class="w1100">
            <div class="container">
                <div class="article-header">
                    <h3 class="article-title">{{article.title}}</h3>
                    <div class="article-info">
                        <span class="author">{{article.author}}</span>
                        <span>{{dateFormat(article.publishDate, 'yyyy-mm-dd')}}</span>
                    </div>
                </div>
                <div class="article-content">
                    {{@article.content}}
                </div>
                <div class="article-comment">
                    {{if userInfo}}
                    <h4>评论</h4>
                    <form class="comment-form" action="/home/comment" method="post">
                        <textarea class="comment" name="content"></textarea>
                        <input type="hidden" name="uid" value="{{@userInfo._id}}">
                        <input type="hidden" name="aid" value="{{@article._id}}">
                        <div class="items">
                            <input type="submit" value="提交">
                        </div>
                    </form>
                    {{else}}
                    <div><h2>先进行登录,再对文章进行评论</h2></div>
                    {{/if}}
                    <div class="comment-list">
                        {{each comments}}
                        <div class="mb10">
                            <div class="article-info">
                                <span class="author">{{$value.uid.username}}</span>
                                <span>{{dateFormat($value.time, 'yyyy-mm-dd')}}</span>
                                <span>{{$value.uid.email}}</span>
                            </div>
                            <div class="comment-content">
                                {{$value.content}}
                            </div>
                        </div>
                        {{/each}}
                    </div>
                </div>
            </div>
        </div>
    </div>
    <!-- 文章框架结束 -->
{{/block}}

default

{{extend './common/layout.art'}}

{{block 'link'}}
    <link rel="stylesheet" href="/home/css/index.css">
{{/block}}

{{block 'main'}}
    {{include './common/header.art'}}
    <!-- 文章列表开始 -->
    <ul class="list w1100">
        {{each result}}
        <li class="{{$index%2 == 0 ? 'fl' : 'fr'}}">
            <a href="/home/article?id={{@$value._id}}" class="thumbnail">
                <img src="http://localhost:3000/{{$value.cover}}">
            </a>
            <div class="content">
                <a class="article-title" href="/home/article?id={{@$value._id}}">{{$value.title}}</a>
                <div class="article-info">
                    <span class="author">{{$value.author}}</span>
                    <span>{{dateFormat($value.publishDate, 'yyyy-mm-dd')}}</span>
                </div>
                <div class="brief">
                    {{@$value.content.replace(/<[^>]+>/g, '').substr(0, 90) + '...'}}
                </div>
            </div>
        </li>
        {{/each}}
    </ul>
    <!-- 文章列表结束 -->

    <!-- 分页开始 -->
    <div class="page w1100">
                {{if page > 1}}
                <a href="/home/?page=<%=page-1%>">上一页</a>  
                {{/if}}
                <% for (var i = 1; i <= total; i++) { %>
                <a href="/home/?page=<%=i %>" class="{{i == page ? 'active' : ''}}">{{i}}</a>                
                <% } %>
                {{if page < total}}
                <a href="/home/?page=<%=page-0+1%>">下一页</a>
                {{/if}}
        // {{if result.page > 1}}
        // <a href="/home/?page={{result.page-1}}">上一页</a>
        // {{/if}}
        // {{each result.display}}
        // <a href="/home/?page={{$value}}" class="{{$value == result.page ? 'active' : ''}}">{{$value}}</a>
        // {{/each}}
        // {{if result.page < result.pages}}
        // <a href="/home/?page={{result.page - 0 + 1}}">下一页</a>
        // {{/if}}
    </div>
    <!-- 分页结束 -->
{{/block}}

mysql源码部分

article.sql

DROP TABLE IF EXISTS `article`;
CREATE TABLE `article`  (
  `_id` int NOT NULL AUTO_INCREMENT,
  `title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
  `author` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
  `publishDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `cover` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL,
  `content` varchar(10000) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
  PRIMARY KEY (`_id`) USING BTREE,
  INDEX `author`(`author`) USING BTREE,
  CONSTRAINT `author` FOREIGN KEY (`author`) REFERENCES `user` (`username`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

comment.sql

DROP TABLE IF EXISTS `comment`;
CREATE TABLE `comment`  (
  `aid` int NOT NULL,
  `uid` int NOT NULL,
  `time` datetime NOT NULL,
  `content` varchar(10000) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
  `_id` int NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 9 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

user.sql

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `_id` int NOT NULL AUTO_INCREMENT,
  `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
  `email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
  `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
  `role` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
  `state` int NOT NULL,
  PRIMARY KEY (`_id`) USING BTREE,
  INDEX `username`(`username`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 26 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;



版权声明:本文为qq_64115060原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。