前言
关于搭建本系统要用到:
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>«</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>»</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>×</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>«</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>»</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>×</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 版权协议,转载请附上原文出处链接和本声明。