
这里先祝大家新年快乐!谢谢大家一直以来的支持。
Flask系列索引,
开发自己的所见即所得Markdown 编辑器
通过GitHub快速部署Flask App 到 Heroku Cloud
在 Heroku 上部署 Python Flask App
用Flask来渲染Markdown
Python开发的Web应用方便分发吗?
编辑器功能特点,
浏览器端的 Markdown 编辑器
支持三种编辑模式:所见即所得(wysiwyg)、即时渲染(ir,类似 Typora)、分屏预览(sv)
支持大纲、数学公式、脑图、图表、流程图、甘特图、时序图、五线谱、多媒体、语音阅读、标题锚点、代码高亮及复制、graphviz、PlantUML 渲染
内置安全过滤、导出、图片懒加载、任务列表、多平台预览、多主题切换、复制到微信公众号/知乎功能
实现 CommonMark 和 GFM 规范,可对 Markdown 进行格式化和语法树查看,并支持 10+ 项配置
工具栏包含 36+ 项操作,除支持扩展外还可对每一项中的快捷键、提示、提示位置、图标、点击事件、类名、子工具栏进行自定义
表情/at/话题等自动补全扩展
可使用拖拽、剪切板粘贴上传,显示实时上传进度,支持 CORS 跨域上传
实时保存内容,防止意外丢失
录音支持,用户可直接发布语音
粘贴 HTML 自动转换为 Markdown,如粘贴中包含外链图片可通过指定接口上传到服务器
支持主窗口大小拖拽、字符计数
多主题支持,内置黑白绿三套主题
多语言支持,内置中、英、韩文本地化
支持主流浏览器,对移动端友好
好吧,如果你看了我的终于找到一款比 Typora 还好用的免费 Markdown 编辑器可能会想,这不是思源的功能吗?
没有错,今天要用的就是思源所使用的编辑器核心组件,Vditor, 来开发我们自己的Markdown编辑器。
index.html
<html>
<head>
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/vditor/dist/index.css" />
<script src="//cdn.jsdelivr.net/npm/vditor/dist/index.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/vditor/dist/js/lute/lute.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/vditor/dist/js/highlight.js/highlight.pack.js"></script>
<script src="//cdn.jsdelivr.net/npm/vditor/dist/js/mermaid/mermaid.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"
integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script>
window.Lute = window.Lute || {}
window.hljs = window.hljs || {}
</script>
<style>
.header {
background-color: #fff;
box-shadow: rgba(0, 0, 0, 0.05) 0 1px 7px;
border-bottom: 1px solid #e1e4e8;
}
</style>
<link rel="stylesheet" type="text/css" href="//fonts.googleapis.com/css?family=Open+Sans" />
</head>
<body>
<h1>Markdown Editor</h1>
<script>
function save(event) {
event.preventDefault();
var fname = document.getElementById("fname").value;
alert(fname)
$.ajax({
type: 'POST', url: "/vditor/save/"
, data: JSON.stringify({
'fname': fname,
'content': vditor.getValue(),
})
, dataType: 'json'
, contentType: 'application/json'
, success: function (data) {
console.log("success")
}
});
}
</script>
file name:<input type="text" id="fname" value="untitled.md"><button onclick="save(event)">Save</button>
<div id="vditor"></div>
<script>
var vditor = new Vditor('vditor', {
"height": 720,
"cache": {
"enable": false
},
"value": "## 所见即所得(WYSIWYG)\n所见即所得模式对不熟悉 Markdown 的用户较为友好,熟悉 Markdown 的话也可以无缝使用。 ",
"mode": "wysiwyg",
upload: {
url: '/vditor/uploads/',
linkToImgUrl: '/static/uploads/',
accept: '.jpg,.png,.gif,.jpeg',
filename(name) {
return name.replace(/\?|\\|\/|:|\||<|>|\*|\[|\]|\s+/g, '-')
},
},
})
</script>
</body>
</html>
HTML 模版部分主要分几部分,
vditor 所需的 JavaScript 库和 css 库
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/vditor/dist/index.css" />
<script src="//cdn.jsdelivr.net/npm/vditor/dist/index.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/vditor/dist/js/lute/lute.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/vditor/dist/js/highlight.js/highlight.pack.js"></script>
<script src="//cdn.jsdelivr.net/npm/vditor/dist/js/mermaid/mermaid.min.js"></script>实现 ajax 保存所需的 jquery 库 及 ajax 保存 markdown 内容的 JavaScript 代码。
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"
integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script>
function save(event) {
event.preventDefault();
var fname = document.getElementById("fname").value;
alert(fname)
$.ajax({
type: 'POST', url: "/vditor/save/"
, data: JSON.stringify({
'fname': fname,
'content': vditor.getValue(),
})
, dataType: 'json'
, contentType: 'application/json'
, success: function (data) {
console.log("success")
}
});
}
</script>vditor 初始化脚本, 包括图片上传的配置(upload 部分)。
<div id="vditor"></div>
<script>
var vditor = new Vditor('vditor', {
"height": 720,
"cache": {
"enable": false
},
"value": "## 所见即所得(WYSIWYG)\n所见即所得模式对不熟悉 Markdown 的用户较为友好,熟悉 Markdown 的话也可以无缝使用。 ",
"mode": "wysiwyg",
upload: {
url: '/vditor/uploads/',
linkToImgUrl: '/static/uploads/',
accept: '.jpg,.png,.gif,.jpeg',
filename(name) {
return name.replace(/\?|\\|\/|:|\||<|>|\*|\[|\]|\s+/g, '-')
},
},
})
</script>模式设置为所见即所得(wysiwyg),编辑器支持模式切换,如下图,

app.py
from hashlib import md5
from pathlib import Path
from flask import Flask,request,jsonify,render_template
import os
app = Flask(__name__)
app.config.from_object('config')
from flask_wtf.csrf import CSRFProtect
csrf = CSRFProtect(app)
@app.route("/")
def index():
return render_template("index.html")
@csrf.exempt
@app.route('/vditor/uploads/',methods=['POST'])
def vditor_uploads():
"""
支持黏贴、拖拽和点击图片上传
"""
images_upload = request.files.get('file[]', None)
img = images_upload.stream.read()
digest=md5(img).hexdigest()
suffix = Path(images_upload.filename).suffix
images_name = f'{digest}{suffix}'
image_full_name = os.path.join(app.config['IMG_UPLOAD_FOLDER'], images_name)
if not Path(image_full_name).exists():
with open(image_full_name,"wb") as f :
f.write(img)
image_full_path = os.path.join(app.config['IMG_UPLOAD_URL'], images_name)
# 返回的json有指定的结构
return jsonify(
{
"msg": "Success!",
"code": 0,
"data": {
"errFiles": [],
"succMap": {
images_upload.filename: image_full_path,
}
}
}
),200
@csrf.exempt
@app.route('/vditor/save/',methods=['POST'])
def vditor_save():
""""
markdown 保存
json格式
"""
data = request.json
print(data['fname'])
print(data['content'])
# save it
return jsonify({"msg":0}),200
后台部分,目前实现两个功能,
图片的保存(前端支持黏贴,拖拽和点击上传) markdown 的保存(具体实现逻辑没有写)
后端的注意点在于图片保存后返回的 json 格式是固定的,这个需要注意。
只需要简单的代码,一个所见即所得的 Markdown 编辑器就实现了。
欢迎关注公众号

有兴趣加群讨论数据挖掘和分析的朋友可以加我微信(witwall),暗号:入群

也欢迎投稿!




