# articles_manage **Repository Path**: aeipyuan/articles_manage ## Basic Information - **Project Name**: articles_manage - **Description**: No description available - **Primary Language**: NodeJS - **License**: AFL-3.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-04-25 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 文章管理系统 -- Express学习 仓库:[https://gitee.com/aeipyuan/articles_manage](https://gitee.com/aeipyuan/articles_manage) ## 1.项目搭建 生成`express`项目 ```javascript express -e article_managemet 创建项目 -e 表示使用ejs模板引擎 ``` `mongodb`创建数据库 ```javascript 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`连接的模块 ```javascript 文件位置:model > index.js const 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) else console.log(docs); }); });//{ _id: 5ea442dbbc0dfbff14afd728, username: 'admin', passsword: '000' ``` ## 2.注册页 ![在这里插入图片描述](https://images.gitee.com/uploads/images/2020/0426/233151_25987f8e_6562638.png) **注册路由** ```javascript //位置:routes > index.js router.get('/regist', (req, res, next) => { res.render('regist'); }) ``` **页面结构** ```html
已有帐户,立即登录
``` **提交处理** ```javascript //位置 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.登录页 ![在这里插入图片描述](https://images.gitee.com/uploads/images/2020/0426/233151_0dc40895_6562638.png) **页面结构** ```html
没有帐号,立即注册
``` **提交处理** (1)安装`express-session`拦截登录状态 ```javascript 安装 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`` ```javascript /* 登录提交 */ 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. 通用顶部组件 ![在这里插入图片描述](https://images.gitee.com/uploads/images/2020/0426/233151_9156016b_6562638.png) **结构** ```html
<%- username %> 写文章 退出 首页
``` **处理退出请求** ```javascript router.get('/logout', (req, res, next) => { req.session.username = null; res.redirect('/login'); }) ``` ## 5. 写文章 ![在这里插入图片描述](https://images.gitee.com/uploads/images/2020/0426/233151_7be59c8e_6562638.png) **页面结构** ```html <%- include('bar',{username:username}) %>
``` **数据提交** (1)创建`article`路由处理文件 ```javascript /* app.js配置 */ var articleRouter=require('./routes/article'); app.use('/article', articleRouter); ``` (2)处理提交请求 ```javascript /* 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. 首页 ![在这里插入图片描述](https://images.gitee.com/uploads/images/2020/0426/233151_a3f89994_6562638.png) **页面结构** ```html <%- include('bar',{username}) %>
<% data.list.map((item,index)=>{ %>
<%- index+1 %> <%- item.username %> <%- item.title %> <%- item.time %>
编辑 删除
<% }) %>
<% for(let i=1;i<=data.total;i++){ %> <%- i %> <% } %>
``` **数据请求实现分页** 通过`url`将当前页码`pageIndex`传入,然后第一次查询总的数量处理`pageSize`获得总页数`total`,第二次查询添加限制条件获取页面需要显示的数据列表,针对不是第一页时页面数据为空的问题,将`pageIndex-1`重新加载 ```javascript /* 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.文章详情 ![在这里插入图片描述](https://images.gitee.com/uploads/images/2020/0426/233151_dc6f29d4_6562638.png) **页面结构** ```html <%- item.title %> <%- include('bar',{username}) %>
<%- data.title %>
作者:<%- data.title %> 发布时间:<%- data.time %>
<%- data.content %>
``` **数据处理** ```javascript /* 文章详情 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.文章编辑 ![在这里插入图片描述](https://images.gitee.com/uploads/images/2020/0426/233151_55f0538f_6562638.png) **页面结构** 编辑与添加文章结构相同,区别在于数据`id`和`pageIndex`记录,修改`write`页结构即可 ```html 编辑
``` **数据处理** 1.进入`write`页面数据获取 写文章进入`write`页时没有`id`和`pageIndex`传入,修改文章时可以通过`id`查询到文章`title`和`content`,通过`pageIndex`可以标识修改完成后要返回的页面 ```javascript /* 文章 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`来判断是更新操作还是插入操作,操作失败返回当前页,成功则返回主页 ```javascript /* 文章发布/修改 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.删除文章 ```html 删除 ``` 在主页加入删除文章选项,`id`用于找到指定文章,`pageIndex`确定删除后返回的页面 ```javascript /* 文章删除 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`插件解析请求 ```javascript npm i multiparty -S ``` 配置`xheditor`富文本编辑器 ```javascript $('.xheditor').xheditor({ tools: 'full', skin: 'default', upImgUrl: '/article/upload',/* 上传路由 */ html5Upload: false, upMultiple: 1 }); ``` 配置上传路由 ```javascript /* 图片上传 */ 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 } ] } */ ```