文章管理系統 -- 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
    }
  ]
}
*/
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章