这里先祝大家新年快乐!谢谢大家一直以来的支持。
Flask系列索引,
开发自己的所见即所得Markdown 编辑器
不写一行Javascript实现省市区三级联动
几行代码轻松实现图片点击、黏贴、拖拽上传和预览
将机器学习模型部署为REST API
几款基于Flask框架的企业级开源软件
通过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)所见即所得模式对不熟悉 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)所见即所得模式对不熟悉 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),编辑器支持模式切换,如下图,
image.png
app.py
from hashlib import md5from pathlib import Pathfrom flask import Flask,request,jsonify,render_templateimport osapp = Flask(__name__)app.config.from_object('config')from flask_wtf.csrf import CSRFProtectcsrf = 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),暗号:入群
也欢迎投稿!