Editor.md 使用小结
概要
Editor.md
- 开源在线 Markdown 编辑器- 图片上传:
- csrf 问题
- 补充复制粘贴、拖拽上传
- 意外离开页面的措施
- localStorge 保存正在编辑的内容
- 离开页面时提示
TOC
目录- 项目实践
1、下载
百度搜索进入 github
下载即可。
开发中使用 editormd.js
,上线后使用 min.js
。
2、页面中使用
<!-- laravel框架注意添加这个 meta -->
<meta name="_token" content="{{ csrf_token() }}">
<!-- 引入editormd.css -->
<link href="{{asset('static/editormd')}}/css/editormd.css" rel="stylesheet">
<form class="layui-form" accept-charset="UTF-8">
@csrf
<!--保存TOC-->
<input id="markdownToC" type="hidden" name="toc" value="">
<!-- MD editor -->
<div id="editor">
<textarea
style="display:none;"
class="form-control"
id="content-editormd-markdown-doc"
name="body_original"
place-holder="请使用Markdown语法"
></textarea>
</div>
</form>
<!-- 当然 jquery 怎么能少呢 -->
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.0.0/jquery.min.js"></script>
<!-- 引入 editormd.js -->
<script src="{{asset('static/editormd')}}/editormd.js"></script>
<script>
// 这里你需要判断是编辑模式还是创建模式
var model = "{{isset($article) ? 'edit' : 'create'}}";
// 是否是发布、保存草稿、保存修改,而不是意外离开页面
var isSubmit = false;
// 检测是否支持本地存储
function enableLocalStorage(){
// 菜鸟教程里推荐的写法
if(localStorage){
try {
localStorage.setItem("test", "yes");
localStorage.removeItem("test");
return true;
} catch (err) {
console.log(err);
}
}
return false;
}
var enableLS = enableLocalStorage();
// 编辑器
var editor = editormd("editor", {
width : "100%",
height : 600,
fontSize:"14px",
placeholder:'请使用 Markdown 语法',
lineNumbers:false, //行号
styleActiveLine:false, //当前行高亮
// tocContainer : "#test",//你可以将TOC结构输出到自定义容器中
path : "{{asset('static')}}/editormd/lib/",
toolbarIcons : function() {
return ["h3","h4","bold", "quote", "hr","|", "list-ul","list-ol", "|","link", "image", "table", "|", "watch", "fullscreen","preview"]
},
syncScrolling: true, //左右侧预览同步
toolbarAutoFixed:true,//工具栏自动固定定位的开启与禁用
saveHTMLToTextarea : true, // 保存 HTML 到 Textarea
imageUpload : true, //图片上传
imageFormats : ["jpg", "jpeg", "gif", "png"],//上传图片格式
imageUploadURL : "{{route('img-upload')}}",//图片上传URL
onload : function() {
console.log(this);
// 如果浏览器支持本地存储
if(enableLS){
let markdownBody = localStorage.getItem('markdownBody');
// 注意 只有创建时才使用本地存储
if(markdownBody && model=='create'){
this.setMarkdown(markdownBody);//填充markdown编辑区域
}
}
},
onchange: function(){
// 提取TOC
$('#markdownToC').val(JSON.stringify(this.markdownToC));
// 保存编辑区域的内容
if(enableLS && model=='create'){
localStorage.setItem("markdownBody", this.getMarkdown());
}
}
});
// 监听离开页面动作
window.onbeforeunload=function(e){
// 如果不是点击的发布文章或保存草稿或保存修改 离开页面时提示
if(!isSubmit){
return "确定离开页面?系统可能不会保存更改";
}
}
</script>
3、了解 editormd 的数据结构
上方 onload
中我们添加了 console.log(this)
onload : function() {
console.log(this);
}
你可以了解下有用的信息如:
classPrefix: "editormd-"
类前缀markdownToC: []
TOC目录可以this.markdownToC
获得- 你可以找到所有的
settings
而不用到处找文档
等等,我只截了一部分 - 所有的工具栏
4、处理表单
检查元素
可以看到下面的 textarea
是 editormd
生成的保存 html
源码的表单
那么后端就可以这么接收
$article['body_original'] = $post['body_original'];
$article['body_html'] = $post['editor-html-code'];
关于 TOC
目录表单,已在上面代码中体现了,即在 onchange
中
<input id="markdownToC" type="hidden" name="toc" value="">
onchange: function(){
$('#markdownToC').val(JSON.stringify(this.markdownToC));
}
5、图片上传
后端交互返回数据格式
// MD编辑器 $code=0|1 success时为1
public static function mdImgUploadRet($code, $message='', $url='')
{
return response()->json([
'success' => $code,
'message' => $message,
'url' => $url
], 200);
}
laravel 框架图片上传 csrf 问题
所说的就是编辑器工具栏的上传图片
我们需要改动下 editormd
的一个图片文件的源码/editormd/plugins/image-dialog/image-dialog.js
大概在 49 行左右,它创建了一个表单 form
,我们只需要添加一个 csrf
的 input
到 form
里就可以了。
// 添加代码 1
var csrfToken = $('meta[name="_token"]').attr('content');
var csrfField = csrfToken ? "<input type='hidden' name='_token' value='" + csrfToken + "' />": '';
// 添加代码 2
+ csrfField +
补充复制粘贴、拖入上传
需要引入一个插件 paste-upload-img.js
,依赖 jquery.js
function initPasteDragImg(Editor){
var doc = document.getElementById(Editor.id)
doc.addEventListener('paste', function (event) {
var items = (event.clipboardData || window.clipboardData).items;
var file = null;
if (items && items.length) {
// 搜索剪切板items
for (var i = 0; i < items.length; i++) {
if (items[i].type.indexOf('image') !== -1) {
file = items[i].getAsFile();
break;
}
}
} else {
console.log("当前浏览器不支持");
return;
}
if (!file) {
console.log("粘贴内容非图片");
return;
}
uploadImg(file,Editor);
});
var dashboard = document.getElementById(Editor.id)
dashboard.addEventListener("dragover", function (e) {
e.preventDefault()
e.stopPropagation()
})
dashboard.addEventListener("dragenter", function (e) {
e.preventDefault()
e.stopPropagation()
})
dashboard.addEventListener("drop", function (e) {
e.preventDefault()
e.stopPropagation()
var files = this.files || e.dataTransfer.files;
uploadImg(files[0],Editor);
})
}
function uploadImg(file,Editor){
var formData = new FormData();
var fileName=new Date().getTime()+"."+file.name.split(".").pop();
formData.append('editormd-image-file', file, fileName);
// 如果使用的 laravel 框架需要加上
var csrfToken = $('meta[name="_token"]').attr('content');
formData.append('_token', csrfToken);
$.ajax({
url: Editor.settings.imageUploadURL,
type: 'post',
data: formData,
processData: false,
contentType: false,
dataType: 'json',
success: function (msg) {
var success=msg['success'];
if(success==1){
var url=msg["url"];
if(/\.(png|jpg|jpeg|gif|bmp|ico)$/.test(url)){
Editor.insertValue("");
}else{
Editor.insertValue("[下载附件]("+msg["url"]+")");
}
}else{
console.log(msg);
alert("上传失败");
}
}
});
}
然后在 onload
中添加
onload : function() {
initPasteDragImg(this);
}
6、项目实践 (laravel)
Route::resource('articles', 'ArticleController');//资源路由
Route::post('articles/create/upload', 'ArticleController@upload')->name('img-upload');//图片上传
关于资源路由
class ArticleController extends Controller
{
// 文章列表
// GET /articles route('articles.index')(路由命名以下不再备注)
public function index()
// 显示创建页面
// GET /articles/create route('articles.create')
public function create()
// 保存你创建的数据
// POST /articles route('articles.store')
public function store(Request $request)
// 显示对应id的文章内容
// GET articles/1 route('articles.show',['article'=>$article->id])
public function show($id)
// 显示编辑表单
// GET articles/1/edit route('articles.edit',['article'=>$article->id])
public function edit($id)
// 保存编辑的数据
// PUT/PATCH articles/1 route('articles.update',['article'=> $article->id])
public function update(Request $request, $id)
// 删除
// DELETE articles/1 route('articles.destroy',['article'=>$article->id])
public function destroy($id)
}
关于 blade
目录结构 (仅供参考)
views
-- articles
----index.blade.php
----create.blade.php
----show.blade.php
----edit.blade.php
关于提取文章摘要
$desc = strip_tags($post['editor-html-code']); // 去除html标签
$article['desc'] = trim(mb_substr($desc,0,40)); // 提取前40个字
关于 TOC
前端传入 JSON.stringify(this.markdownToC)
格式如下
[{"text":"标题","level":3,"slug":"-"},{"text":"文本加粗","level":3,"slug":"-"},{"text":"引用","level":3,"slug":"-"},{"text":"全屏","level":3,"slug":"-"},{"text":"如何上传图片","level":3,"slug":"-"}]
后端直接保存到数据库即可
$article['toc'] = $post['toc'];
//提取时
$toc = json_decode($article->toc,true);
显示时示例
<ul>
@foreach($toc as $item)
<li style="padding: 7px 0;white-space:nowrap;text-overflow:ellipsis;overflow:hidden;">
<span>{{str_repeat(' ', $item['level'])}}</span>
<span><a href="#{{$item['text']}}" >{{$item['text']}}</a></span>
</li>
@endforeach
</ul>
关于如何显示页面
editormd
保存的 html
是没有任何样式的,你可以自定义样式、或者 copy
一份 editormd.css
剔除用不到的样式,然后自定义样式。
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: