相關文章
基于阿里egg框架搭建博客(1)——開發(fā)準備
基于阿里egg框架搭建博客(2)——Hello World
基于阿里egg框架搭建博客(3)——注冊與登錄
基于阿里egg框架搭建博客(4)——權限控制
基于阿里egg框架搭建博客(5)——置頂導航條
基于阿里egg框架搭建博客(6)——瀏覽、發(fā)表文章
基于阿里egg框架搭建博客(7)——編輯文章
git
https://github.com/ZzzSimon/egg-example
喜歡就點個贊吧镰绎!
正文
瀏覽跟狱、發(fā)表文章簡單來講就是對article表的讀/寫操作驶臊。
Article表設計
字段說明
名稱 | 解釋 |
---|---|
id | 主鍵id |
title | 文章標題 |
url | 文章訪問path |
detail | 文章內容 |
author | 作者扛门,對應username |
invisible | 是否保密论寨,保密則不顯示在文章列表 |
create_time | 文章第一次發(fā)表時間 |
update_time | 文章最后一次修改時間 |
sql腳本
DROP TABLE IF EXISTS `article`;
CREATE TABLE `article` (
`id` varchar(20) NOT NULL,
`title` varchar(255) NOT NULL,
`url` varchar(255) NOT NULL,
`detail` varchar(4096) NOT NULL,
`author` varchar(255) NOT NULL,
`invisible` int(1) NOT NULL DEFAULT '0',
`create_time` datetime NOT NULL,
`update_time` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
頁面設計
瀏覽文章
發(fā)表文章
功能設計
瀏覽文章
- 點擊文章標題查看文章詳細內容
發(fā)表文章
- 輸入文章標題
- 選擇是否保密葬凳,保密則不顯示在文章列表
- 保存文章
- 支持markdown
markdown支持
對于前端md編輯器,我們選擇了Editor.md
火焰。
前端代碼
瀏覽文章
list.tpl 文章列表
我們創(chuàng)建app/view/article/list.tpl
文件:
{% extends "parent.tpl" %}
{% block head %}
<title>文章列表</title>
{% endblock %}
{% block content %}
<ul class="article-view view">
{% for item in list %}
<li class="item">
<dl>
<dt><a href="{{ item.url }}">{{ item.title }}</a></dt>
<dd><small>{{item.author}}</small> 最后更新于 {{helper.formatTime(item.update_time)}}</dd>
</dl>
</li>
{% endfor %}
</ul>
{% endblock %}
detail.tpl 文章詳情
我們創(chuàng)建app/view/article/detail.tpl
文件:
{% extends "parent.tpl" %}
{% block head %}
<title>{{article.title}}</title>
<link rel="stylesheet" href="/public/editormd/editormd.css">
<script src="/public/editormd/lib/marked.min.js"></script>
<script src="/public/editormd/lib/prettify.min.js"></script>
<script src="/public/editormd/lib/raphael.min.js"></script>
<script src="/public/editormd/lib/underscore.min.js"></script>
<script src="/public/editormd/lib/sequence-diagram.min.js"></script>
<script src="/public/editormd/lib/flowchart.min.js"></script>
<script src="/public/editormd/lib/jquery.flowchart.min.js"></script>
<script type="text/javascript" src="/public/editormd/editormd.js"></script>
{% endblock %}
{% block content %}
<div class="page-header">
<h1>{{article.title}} <small style="font-size: small">{{article.author}} 最后更新于 {{helper.formatTime(article.update_time)}}</small></h1>
</div>
<div id="detail" style="visibility: hidden">{{article.detail}}</div>
<div id="layout">
<div id="test-editormd-view">
</div>
</div>
{% endblock %}
{%block script%}
<script type="text/javascript">
$(function () {
const markdown = $('#detail').text();
var testEditormdView = editormd.markdownToHTML("test-editormd-view", {
markdown: markdown,//+ "\r\n" + $("#append-test").text(),
//htmlDecode : true, // 開啟 HTML 標簽解析,為了安全性谦疾,默認不開啟
htmlDecode: "style,script,iframe", // you can filter tags decode
//toc : false,
tocm: true, // Using [TOCM]
//tocContainer : "#custom-toc-container", // 自定義 ToC 容器層
//gfm : false,
//tocDropdown : true,
// markdownSourceCode : true, // 是否保留 Markdown 源碼,即是否刪除保存源碼的 Textarea 標簽
emoji: true,
taskList: true,
tex: true, // 默認不解析
flowChart: true, // 默認不解析
sequenceDiagram: true, // 默認不解析
});
});
</script>
{% endblock %}
此處需要注意1點:
md的內容先通過模板樊诺,渲染在一個隱藏的div中词爬。之后顿膨,通過editormd
動態(tài)渲染出來恋沃。
發(fā)表文章
article.tpl 發(fā)表文章
我們創(chuàng)建app/view/article/article.tpl
文件:
{% extends "parent.tpl" %}
{% block head %}
<title>Markdown Editor</title>
<link rel="stylesheet" href="/public/editormd/editormd.css">
<script type="text/javascript" src="/public/editormd/editormd.js"></script>
{% endblock %}
{% block content %}
<div class="row">
<div class="form-group">
<label for="title">文章標題:</label>
<input id="title" type="text" class="form-control">
</div>
<div class="checkbox ">
<label>
<input id="invisible" type="checkbox">保密(勾選后將<strong style="color: red">不顯示</strong>在文章列表)
</label>
</div>
<div class="form-group pull-right">
<button id="save" class="btn btn-success ">保存</button>
</div>
</div>
<div class="row">
<div id="layout">
<div id="test-editormd"></div>
</div>
</div>
{% endblock %}
{% block script %}
<script type="text/javascript">
let testEditor = editormd("test-editormd", {
width: "100%",
height: 740,
path: '/public/editormd/lib/',
// theme: "dark",
// previewTheme: "dark",
// editorTheme: "pastel-on-dark",
// markdown: md,
codeFold: true,
//syncScrolling : false,
saveHTMLToTextarea: true, // 保存 HTML 到 Textarea
searchReplace: true,
//watch : false, // 關閉實時預覽
htmlDecode: "style,script,iframe|on*", // 開啟 HTML 標簽解析,為了安全性梅割,默認不開啟
//toolbar : false, //關閉工具欄
//previewCodeHighlight : false, // 關閉預覽 HTML 的代碼塊高亮户辞,默認開啟
emoji: true,
taskList: true,
tocm: true, // Using [TOCM]
tex: true, // 開啟科學公式TeX語言支持刃榨,默認關閉
flowChart: true, // 開啟流程圖支持枢希,默認關閉
sequenceDiagram: true, // 開啟時序/序列圖支持晴玖,默認關閉,
//dialogLockScreen : false, // 設置彈出層對話框不鎖屏,全局通用敬察,默認為true
//dialogShowMask : false, // 設置彈出層對話框顯示透明遮罩層莲祸,全局通用,默認為true
//dialogDraggable : false, // 設置彈出層對話框不可拖動缴阎,全局通用蛮拔,默認為true
//dialogMaskOpacity : 0.4, // 設置透明遮罩層的透明度建炫,全局通用肛跌,默認值為0.1
//dialogMaskBgColor : "#000", // 設置透明遮罩層的背景顏色,全局通用西饵,默認為#fff
imageUpload: true,
imageFormats: ["jpg", "jpeg", "gif", "png", "bmp", "webp"],
imageUploadURL: "/edit/uploadPic?_csrf={{ ctx.csrf | safe }}",
/* 后端需返回: {
success : 0 | 1, //0表示上傳失敗;1表示上傳成功
message : "提示的信息",
url : "圖片地址" //上傳成功時才返回
}*/
onload: function () {
console.log('onload', this);
//this.fullscreen();
//this.unwatch();
//this.watch().fullscreen();
//this.setMarkdown("#PHP");
//this.width("100%");
//this.height(480);
//this.resize("100%", 640);
}
});
$('#save').bind('click', function () {
data = {
article: {
title: $('#title').val(),
detail: testEditor.getMarkdown(),
invisible: $('#invisible').prop('checked') ? 1:0
}
};
$.post('/edit/save?_csrf={{ ctx.csrf | safe }}', data, function (resp) {
if (resp.flag === '1') {
window.location.href = resp.url;
}
})
})
</script>
{% endblock %}
此處需要注意1點:
ajax默認是不重定向的期虾,所以當保存成功,我們需要返回文章的訪問url茂蚓,在回調函數(shù)里重定向聋涨。
后端代碼
ArticleController
我們創(chuàng)建app/controller/article.js
文件:
const Controller = require('egg').Controller;
class ArticleController extends Controller {
async list() {
const ctx = this.ctx;
const articleList = await ctx.service.article.list();
await ctx.render('article/list.tpl', { list: articleList });
}
async detail(){
const ctx = this.ctx;
const queryRes = await ctx.service.article.detail(ctx.params.id);
ctx.logger.info(queryRes);
await ctx.render('article/detail.tpl', { article: queryRes[0] });
}
}
module.exports = ArticleController;
EditController
我們創(chuàng)建app\controller\edit.js
文件:
const Controller = require('egg').Controller;
const fs = require('mz/fs');
class EditController extends Controller{
async editHtm(){
await this.ctx.render('article/edit.tpl');
}
async save(){
const ctx = this.ctx;
const article = ctx.request.body.article;
article.id = ctx.helper.uuid();
article.url = '/article/'+article.id+'.htm';
article.author = ctx.session.user.username;
const nowTime = new Date();
article.create_time = nowTime;
article.update_time = nowTime;
const result = await ctx.service.article.save(article);
if (result) {
ctx.body = {flag:'1',msg:'保存成功',url:article.url}
}else {
ctx.body = {flag:'0',msg:'保存失敗'}
}
}
async uploadPic(){
const { ctx } = this;
const file = ctx.request.files[0];
let filenameNew = ctx.helper.uuid() +'.'+ file.filename.split('.').pop();
let filepathNew = this.config.baseDir+'\\app\\public\\mdPic\\'+filenameNew;
//把臨時文件剪切到新目錄去
await fs.rename(file.filepath, filepathNew);
//按editormd要求格式返回
ctx.body = {
success : 1, //0表示上傳失敗;1表示上傳成功
message : "上傳成功",
url : filepathNew.split(this.config.baseDir+'\\app')[1] //上傳成功時才返回
}
}
}
module.exports = EditController;
此處需要注意1點:
-
uoloadPic
方法主要用于md編輯器的圖片上傳茂腥。
ArticleService
我們創(chuàng)建app/service/article.js
文件:
const Service = require('egg').Service;
class ArticleService extends Service {
async list() {
const sql = "SELECT url,title,author,update_time FROM article WHERE invisible = 0";
const list =await this.app.mysql.query(sql);
return list;
}
async detail(id = 1){
const sql = "SELECT title,detail,author,update_time FROM article WHERE id = ?";
return await this.app.mysql.query(sql,[id])
}
async save(article = {}){
const res = await this.app.mysql.insert('article',article);
return res.affectedRows === 1;
}
}
module.exports = ArticleService;
router.js
我們往 app/router.js
中添加一下內容:
router.get('/edit.htm',controller.edit.editHtm);
router.get('/article/:id.htm',controller.article.detail);
router.get('/articleList.htm', controller.article.list);
router.post('/edit/save',controller.edit.save);
router.post('/edit/uploadPic',controller.edit.uploadPic);
結尾
如果看完覺得有用朝捆,請給作者一個喜歡吧右蹦!謝謝啦何陆!