当前位置: 首页 > 后端技术 > Node.js

基于阿里egg框架搭建博客(6)——浏览、发表文章

时间:2023-04-03 21:03:35 Node.js

相关文章基于阿里egg框架搭建博客(1)——开发准备基于阿里egg框架搭建博客(2)——Hello World基于阿里egg框架搭建博客(3)——注册与登录基于阿里egg框架搭建博客(4)——权限控制基于阿里egg框架搭建博客(5)——置顶导航条基于阿里egg框架搭建博客(6)——浏览、发表文章基于阿里egg框架搭建博客(7)——编辑文章githttps://github.com/ZzzSimon/e...喜欢就点个赞吧!正文浏览、发表文章简单来讲就是对article表的读/写操作。Article表设计字段说明名称解释id主键idtitle文章标题url文章访问pathdetail文章内容author作者,对应usernameinvisible是否保密,保密则不显示在文章列表create_time文章第一次发表时间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;页面设计浏览文章发表文章功能设计浏览文章点击文章标题查看文章详细内容发表文章输入文章标题选择是否保密,保密则不显示在文章列表保存文章支持markdownmarkdown支持对于前端md编辑器,我们选择了Editor.md。官方文档:http://pandao.github.io/edito...前端代码浏览文章list.tpl 文章列表我们创建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 文章详情我们创建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动态渲染出来。发表文章article.tpl 发表文章我们创建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,在回调函数里重定向。后端代码ArticleController我们创建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我们创建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我们创建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);结尾如果看完觉得有用,请给作者一个喜欢吧!谢谢啦!