文章管理系统 – Express学习

仓库:https://gitee.com/aeipyuan/articles_manage

1.项目搭建

生成express项目

express -e article_managemet   创建项目 -e 表示使用ejs模板引擎

mongodb创建数据库

mongo                               - 开启mongodb
use articles_db                     - 创建/使用数据库
db.createCollection('users')        - 创建users集合
db.createCollection('articles')     - 创建article集合
show collections                    - 查询是否创建成功测试:
db.users.insertOne({username:'admin',passsword:'000'})
db.users.find()

创建连接mongodb连接的模块

文件位置:model > index.jsconst MongoClient = require('mongodb').MongoClient;/* 连接数据库的url,在mongo命令下可以找到 */let url = 'mongodb://localhost:27017';/* 连接的数据库名字 */let dbName = 'articles_db';/* 封装数据库连接方法 */function connect(callback) {MongoClient.connect(url, (err, client) => {if (err) {console.log('数据库连接错误!', err);} else {/* 根据数据库名获取数据库返回给callback处理 */let db = client.db(dbName);callback && callback(db);client.close();}})}module.exports = { connect };/* 测试 */connect(db => {db.collection('users').findOne({ username: 'admin' }, (err, docs) => {if (err)console.log(err)elseconsole.log(docs); });});//{ _id: 5ea442dbbc0dfbff14afd728, username: 'admin', passsword: '000'

2.注册页

注册路由

//位置:routes > index.js
router.get('/regist', (req, res, next) => {res.render('regist');
})

页面结构

<div class='form-box'><form action="/users/regist" method="post"><input type="text" name="username" placeholder="请输入用户名"><input type="password" name="password" placeholder="请输入密码"><input type="password" name="password2" placeholder="请确认密码"><input type="submit" value="注册"></form><div>已有帐户,<a href="/login">立即登录</a></div>
</div>

提交处理

//位置 routes > user.js
router.post('/regist', (req, res, next) => {/* 取出提交数据 */let { username, password, password2 } = req.body;let data = { username, password, password2 };/* 校验数据 */if (password !== password2) {console.log("两次输入的密码不一致");res.redirect('/regist');} else {model.connect(db => {/* 写入数据库 */db.collection('users').insertOne(data, (err, docs) => {if (err) {console.log('注册失败');res.redirect('/regist');//返回注册页} else {console.log('注册成功');res.redirect('/login');//返回登陆页}})})}
})

3.登录页

页面结构

<div class="form-box"><form action="/users/login" method="post"><input type="text" name="username" placeholder="请输入用户名"><input type="password" name="password" placeholder="请输入密码"><input type="submit" value="登录"></form><div>没有帐号,<a href="/regist">立即注册</a>!</div>
</div>

提交处理
(1)安装express-session拦截登录状态

安装
npm i express-session -S /* -------------app.js配置------------- */
/* 配置session */
app.use(session({secret: 'qf project',resave: false,saveUninitialized: true,cookie: { maxAge: 1000 * 60 * 10 }   // 指定登录会话的有效时长
}))
/* 拦截登陆状态 */
app.get('*', (req, res, next) => {let username = req.session.username;let path = req.path;console.log("session-" + username);if (path != '/login' && path != '/regist') {if (!username)res.redirect('/login');}next();
})

(2)从数据库查询信息并存入session

/* 登录提交 */
router.post('/login', (req, res, next) => {let data = {username: req.body.username,password: req.body.password}/* 连接数据库 */model.connect(db => {db.collection('users').findOne(data, (err, docs) => {if (err) {console.log(err);res.redirect('/login');} else {req.session.username = data.username;res.redirect('/');}})})
})

4. 通用顶部组件


结构

<!-- bar.ejs -->
<div class='bar'><span><%- username  %></span><a href='/write'>写文章</a><a href='/users/logout'>退出</a><a href='/' class='home'><img src="/images/home.png" alt="首页"></a>
</div>

处理退出请求

router.get('/logout', (req, res, next) => {req.session.username = null;res.redirect('/login');
})

5. 写文章

页面结构

<%- include('bar',{username:username}) %>
<form class="article" action="/article/add" method="post"><input type="text" name="title" placeholder="请输入文章标题"><textarea name="content" class="xheditor" cols="30" rows="10"></textarea><input type="submit" value="发布">
</form>
<!-- xheditor富文本编辑器 -->
<script type="text/javascript" src="/xheditor/jquery/jquery-1.4.4.min.js"></script>
<script type="text/javascript" src="/xheditor/xheditor-1.2.2.min.js"></script>
<script type="text/javascript" src="/xheditor/xheditor_lang/zh-cn.js"></script>
<script>$('.xheditor').xheditor({tools: 'full',skin: 'default',upImgUrl: '/article/upload',html5Upload: false,upMultiple: 1});
</script>

数据提交
(1)创建article路由处理文件

/* app.js配置 */
var articleRouter=require('./routes/article');
app.use('/article', articleRouter);

(2)处理提交请求

/* routes > article.js */
router.post('/add', (req, res, next) => {/* 获取数据 */let username = req.session.username;let data = {title: req.body.title,content: req.body.content,id: Date.now(),username: username}/* 写入数据库 */model.connect(db => {db.collection('articles').insertOne(data, (err, docs) => {if (err) {console.log('提交失败', err);res.redirect('/write');} else {console.log('提交成功');res.redirect('/');}})})
})

6. 首页

页面结构

<%- include('bar',{username}) %>
<div class='list'><% data.list.map((item,index)=>{ %><div class='row'><span><%- index+1 %></span><!-- 序号 --><span><%- item.username %></span><!-- 作者 --><span><a href="/detail?id=<%- item.id %>&pageIndex=<%- data.pageIndex %> "> <%- item.title %></a><!-- 标题 --></span><span><%- item.time %></span><!-- 时间 --><div><a href=" /write?id=<%- item.id %>&pageIndex=<%- data.pageIndex %>">编辑</a><a href="/article/delete?id=<%- item.id %>&pageIndex=<%- data.pageIndex %>">删除</a></div></div><% }) %><!-- 显示页码 --><div class="pages"><% for(let i=1;i<=data.total;i++){ %><a href="/?pageIndex=<%- i %>"><%- i %></a><!-- 根据页码重新请求数据 --><% } %></div>
</div>
<!--
data数据结构:
{total: 1,pageIndex: 1,list: [{_id: 5ea5538ad8c2e22d908155eb,title: '1dxsxx',content: 'dccfwvfr',id: 1587893130840,username: 'a',time: '2020-04-26 05:25:30'}]
} -->

数据请求实现分页
通过url将当前页码pageIndex传入,然后第一次查询总的数量处理pageSize获得总页数total,第二次查询添加限制条件获取页面需要显示的数据列表,针对不是第一页时页面数据为空的问题,将pageIndex-1重新加载


/* http://localhost:8888/?pageIndex=1  */
router.get('/', async (req, res, next) => {/* 页面数据 */let pageIndex = req.query.pageIndex || 1;let pageInfo = {total: 0,/* 总共页数 */pageIndex,/* 当前页 */list: []/* 页面数据 */}let pageSize = 3;model.connect(db => {/* 查询所有数据 */db.collection('articles').find().toArray((err, docs) => {if (err) {console.log('首页数据查询错误');res.render('index', { username, pageInfo });} else {pageInfo.total = Math.ceil(docs.length / pageSize);//获取总的页面数model.connect(db => {/* 限制条件查询 */db.collection('articles').find()/* 查找所有 */.sort({ id: -1 })/* 按时间倒序 */.limit(pageSize)/* 限制数量 */.skip((pageIndex - 1) * pageSize)/* 跳过数量 */.toArray((err2, list) => {if (err2) {console.log('首页数据查询错误', err2);} else {/* 除第一页若页面数据条数为0则请求前一页 */if (pageIndex != 1 && !list.length) {res.redirect('/?pageIndex=' + (pageIndex - 1));} else {/* 格式化时间 */list.forEach(v => v.time = moment(v.id).format('YYYY-MM-DD hh:mm:ss'))pageInfo.list = list;}/* 将数据传给页面 */res.render('index', {username: req.session.username,data: pageInfo});}})})}})})
})

7.文章详情

页面结构

<!-- index.ejs入口 -->
<a href="/detail?id=<%- item.id %>&pageIndex=<%- data.pageIndex %> "><%- item.title %>
</a><!-- 标题 --><!-- detail.ejs -->
<%- include('bar',{username}) %>
<div class='detail'><div class='title'><%- data.title %></div><div class="desc"><span>作者:<%- data.title %></span> <span> 发布时间:<%- data.time %></span></div><div class="content"><%- data.content %></div>
</div>
<!--
data格式:
{_id: 5ea5538ad8c2e22d908155eb,title: '1dxsxx',content: 'dccfwvfr',id: 1587893130840,username: 'a',time: '2020-04-26 05:25:30'
}-->

数据处理

/* 文章详情 http://localhost:8888/detail?id=1587900351782&pageIndex=1 */
router.get('/detail', (req, res, next) => {let id = parseInt(req.query.id);let pageIndex = req.query.pageIndex;model.connect(db => {db.collection('articles').findOne({ id }, (err, docs) => {if (err) {console.log('详情获取失败' + err);res.redirect('/?=' + pageIndex);//返回主页} else {/* 时间格式化 */docs.time = moment(docs.id).format('YYYY-MM-DD hh:mm:ss');console.log(docs)res.render('detail', {username: req.session.username,data: docs})}})})
})

8.文章编辑

页面结构
编辑与添加文章结构相同,区别在于数据idpageIndex记录,修改write页结构即可

<!-- index.ejs的入口 -->
<a href=" /write?id=<%- item.id %>&pageIndex=<%- data.pageIndex %>">编辑</a>
<!-- write.ejs -->
<form class="article" action="/article/add" method="post"><!-- 传递参数使用hidden --><input type="hidden" name="id" value="<%- data.id %>"><input type="hidden" name="pageIndex" value="<%- data.pageIndex %>"><input type="text" name="title" placeholder="请输入文章标题" value="<%- data.title %>"><textarea class="xheditor" name="content" cols="30" rows="10"><%- data.content %></textarea><input type="submit" value="<%- data.id?'修改':'发布' %>">
</form>
<!-- data数据结构{id: 1587893123696,title: 'qqqqq',content: 'qcdxcdxcxzczx',pageIndex: '1'}
-->

数据处理
1.进入write页面数据获取
写文章进入write页时没有idpageIndex传入,修改文章时可以通过id查询到文章titlecontent,通过pageIndex可以标识修改完成后要返回的页面

/* 文章 http://localhost:8888/write?id=1587900351782&pageIndex=1*/
router.get('/write', (req, res, next) => {/* 获取数据 */let id = parseInt(req.query.id) || null;let pageIndex = req.query.pageIndex || 1;let data = {id,title: '',content: '',pageIndex}/* 查询数据 */model.connect(db => {db.collection('articles').findOne({ id: id }, (err, docs) => {if (err) {console.log('获取文章数据失败', err);res.redirect('/?pageIndex=' + pageIndex);} else {if (docs) {/* 查询结果为空是新增文章 */data.title = docs.title;data.content = docs.content;}res.render('write', {username: req.session.username,data})}})})
})

2.点击修改按钮更新数据
通过是否含有id来判断是更新操作还是插入操作,操作失败返回当前页,成功则返回主页

/* 文章发布/修改 req.body => id pageIndex title content*/
router.post('/add', (req, res, next) => {/* 获取数据 */let username = req.session.username;let id = parseInt(req.body.id);let pageIndex = req.body.pageIndex;let data = {title: req.body.title,content: req.body.content,id: id || Date.now(),/* 修改是id,添加是Date.now() */username: username,}model.connect(db => {if (id) {/* 修改 */db.collection('articles').updateOne({ id }, { $set: data }, (err, docs) => {if (err) {console.log('修改失败', err);res.redirect(`/write?id=${id}&pageIndex=${pageIndex}`);/* 重新操作 */} else {res.redirect(`/?pageIndex=${pageIndex}`);//回主页}})} else {/* 添加 */db.collection('articles').insertOne(data, (err, docs) => {if (err) {console.log('发布失败', err);res.redirect('/write');/* 重新操作 */} else {console.log('发布成功');res.redirect('/');/* 回主页 */}})}})
})

9.删除文章

<a href="/article/delete?id=<%- item.id %>&pageIndex=<%- data.pageIndex %>">删除</a>

在主页加入删除文章选项,id用于找到指定文章,pageIndex确定删除后返回的页面

/* 文章删除  http://localhost:8888/?id=1587874300950&pageIndex=1 */
router.get('/delete', (req, res, next) => {/* 获取id和当前页码 */let id = parseInt(req.query.id);let pageIndex = req.query.pageIndex;/* 删除数据 */model.connect(db => {db.collection('articles').deleteOne({ id }, (err, ret) => {if (err) {console.log('删除失败', err);res.redirect(`/?pageIndex=${pageIndex}`);} else {console.log('删除成功', ret);res.redirect(`/?pageIndex=${pageIndex}`);}})})
})

10.实现图片上传

安装multiparty插件解析请求

npm i multiparty -S

配置xheditor富文本编辑器

$('.xheditor').xheditor({tools: 'full',skin: 'default',upImgUrl: '/article/upload',/* 上传路由 */html5Upload: false,upMultiple: 1
});

配置上传路由

/* 图片上传 */
router.post('/upload', (req, res, next) => {var form = new multiparty.Form();form.parse(req, (err, fields, files) => {if (err) {console.log('上传失败', err);} else {let file = files.filedata[0];/* 创建读写流 */let rs = fs.createReadStream(file.path);/* 存图片的路径为public下的/uploads/ */let newPath = '/uploads/' + file.originalFilename;let ws = fs.createWriteStream('./public' + newPath);/* 边读边写 */rs.pipe(ws);ws.on('close', () => {console.log('文件上传成功');res.send({ err: '', msg: newPath });})}})
})
/*
files {filedata: [{fieldName: 'filedata',originalFilename: 'Snipaste_2019-09-23_00-10-40.png',path: 'C:\\Users\\14329\\AppData\\Local\\Temp\\qWOzXsQRXhVyqiO57W3qjMgC.png',headers: [Object],size: 146188}]
}
*/

文章管理系统 -- Express学习相关推荐

  1. 基于DotNet构件技术的企业级敏捷软件开发平台 - AgileEAS.NET - 文章汇总及学习指南...

    一.AgileEAS.NET平台简介 AgileEAS.NET平台是一套应用系统快速开发平台,用于帮助中小软件开发商快速构建自己的企业信息管理类开发团队,以达到节省开发成本.缩短开发时间,快速适应市场 ...

  2. 基于SSM框架的文章管理系统(增加留言回复功能)

    一个简单的文章管理系统(增加留言回复功能) 简单使用百度富文本实现了文章管理的功能,增加了留言回复功能,其实这个小项目只是为了测试怎样实现留言回复功能而提取出来的,并不是很完善,主要实现了对文章的增删 ...

  3. Java Web 文章管理系统(Jsp+Ajax+JDBC+MySql实现)

    本示例是使用JavaWeb技术实现一个简单的文章管理系统(新闻管理系统)其中主要功能如下: 用户和管理员登录 用户发布新文章.文章详情查看.文章修改.文章删除与恢复 用户查看他人对自己授权的文章及其文 ...

  4. java留言并后台回复_基于SSM的文章管理系统(增加留言回复功能)

    食用说明 这是本人初学SSM的时候写的,旨在学习SSM框架使用,在达到学习目的的前提下实现部分功能,项目本身大体没有问题,如果细节部分你遇到了问题还请自行完善下 本人不会再维护此项目 本人不会再维护此 ...

  5. php申请系统,PHP+MYSQL的文章管理系统(一)_php

    此篇文章属原创,如有引用,请标明作者信息. Email: leo_cdp@yeah.net http://www.cfeng.net/ 本文代码任意转载,使用请保留此声明 ############## ...

  6. 小孔子文章管理系统V2.0发布测试

    小孔子文章管理系统V2.0 [感谢]  本系统是仿照nbArticle开发,在此表示感谢nb联盟的作品. [版权声明]                                          ...

  7. 简易php access文章管理系统,PHP+ACCESS 文章管理程序代码

    代码如下: session_start(); //========================== //作者:biyuan //时间:2006.07 //联系:QQ6010707 //====== ...

  8. express学习 - (3)express 路由

    express学习 (3) express 路由 李俊才 的 CSDN 博客 邮箱 :291148484@163.com CSDN 主页:https://blog.csdn.net/qq_285502 ...

  9. express学习 - (1)环境配置与第一个express项目

    express学习 (1) 环境配置与第一个express项目 CSDN主页:jcLee95 邮箱:291148484@163.com [任务]: 安装express: 创建第一个express项目: ...

最新文章

  1. AI时代,如何缓解CMO的决策焦虑?
  2. iOS11 UITableViewCell滑动事件改动
  3. MySQL表结构设计之范式化和反范式化对比
  4. 《2017微信春节数据报告》出炉 初一到初五微信红包收发总量达到460亿个
  5. Redis初识、设计思想与一些学习资源推荐
  6. Ae:Roto 笔刷工具和调整边缘工具
  7. python职位要求_python 开发工程师职位描述与岗位职责任职要求
  8. 寒霜系列引擎技术解析
  9. Java 时间相关 获取某月的某一天
  10. Tomcat与JDK版本对应关系,Tomcat各版本特性,鸟哥linux视频教程
  11. 三星海外远程真机调试使用教程
  12. 【004】国家企业信用信息公示系统-官方渠道查询企业信息
  13. redis实现坐标附近查询
  14. 本地win10安装的MySQL8.0.12用navicat12报错 2509 -Authentication plugin ' caching_sha2_password' cannot be :
  15. JavaScript基础知识总结复习(一)
  16. 抖音企业号建议做吗?有什么好处?
  17. linux高手知乎,配置一个简洁高效的 Zsh | Linux 中国
  18. SAPトランザクション一覧(メモ)
  19. 可控硅过零导通程序--可控硅驱动程序
  20. 3 基于matplotlib的python数据可视化——导入Excel数据绘制组合图表

热门文章

  1. 《图解HTTP》读感
  2. 超级简便:公式居中编号右对齐方法
  3. 论坛论坛发帖html功能,论坛发帖报错解决方法
  4. 敏捷管理(1)- 什么是敏捷开发?为什么要采用敏捷?
  5. ios swift 聊天_iOS Swift上的加密聊天
  6. C语言刷题(8)——“C”
  7. NLP领域中文对话系统数据集总结(有下载地址)
  8. 为什么建议软件测试自学而不推荐去IT培训机构?浅谈IT培训机构存在的意义
  9. php知识管理系统,PHP开源内容管理系统YzmCMS
  10. 员工绩效考核管理PPT模板-优页文档