zoukankan      html  css  js  c++  java
  • 文章管理系统 -- Express学习

    文章管理系统 – 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.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.注册页

    在这里插入图片描述

    注册路由

    //位置: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
        }
      ]
    }
    */
    
  • 相关阅读:
    Allegro PCB Design GXL (legacy) 使用slide无法将走线推挤到焊盘的原因
    OrCAD Capture CIS 16.6 导出BOM
    Altium Designer (17.0) 打印输出指定的层
    Allegro PCB Design GXL (legacy) 将指定的层导出为DXF
    Allegro PCB Design GXL (legacy) 设置十字大光标
    Allegro PCB Design GXL (legacy) 手动更改元器件引脚的网络
    magento产品导入时需要注意的事项
    magento url rewrite
    验证台湾同胞身份证信息
    IE8对css文件的限制
  • 原文地址:https://www.cnblogs.com/aeipyuan/p/12783489.html
Copyright © 2011-2022 走看看