UEditor集成Markdown编辑功能完整方案,支持Markdown与HTML的双向转换
微wx笑
2025-03-02【前端开发】
18
0
0关键字:
Ueditor Markdown
上一篇 UEditor集成Markdown编辑功能方案,实现的思路是支持Markdown格式的内容插入功能,后来一想,既然可以将Markdown格式转换为HTML格式,为什么不同时支持HTML格式转Markdown格
上一篇 UEditor集成Markdown编辑功能方案,实现的思路是支持Markdown格式的内容插入功能,后来一想,既然可以将Markdown格式转换为HTML格式,为什么不同时支持HTML格式转Markdown格式呢!
同时对界面也做了样式上的美化;
点击 Ueditor 工具栏上的 markdown 图标,如果 编辑器内有内容,就直接转换为 Markdown 格式,显示在Markdown 格式编辑器的编辑框内,这样就实现的双向的编辑与转换。
效果如下:
依赖库
markdown-it
是一个功能强大、快速且轻量级的 JavaScript 库,用于将 Markdown 文本解析并渲染为 HTML。以下为你详细介绍 markdown-it
的相关内容:
特点
速度快:采用了高效的解析算法,能够快速处理大量的 Markdown 文本。
扩展性强:支持通过插件扩展其功能,例如添加代码高亮、表格渲染、任务列表等功能。
符合规范:遵循 CommonMark 规范,确保 Markdown 解析的一致性。
轻量级:体积小巧,不会给项目带来过多的负担。
特点
简单易用:提供了简洁的 API,能轻松地将 HTML 字符串转换为 Markdown 文本。
高度可定制:允许用户自定义规则,以满足不同的转换需求。
广泛兼容性:可以在浏览器环境和 Node.js 环境中使用。
UI代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | < div id = "mdOverlay" style = "display:none;position: fixed; top:0; left:0; width:100%; height:100%; background: rgba(0,0,0,0.5); opacity: 0.3; filter: alpha(opacity=30); z-index: 9999;" ></ div > < div id = "mdModal" style = "display: none; position: fixed; top: 50%; left: 50%; transform: translate(-50%,-50%); background: white; box-shadow: 1px 1px 30px rgba(0,0,0,0.3); z-index: 10000; border-radius: 3px;" > < div style = "padding:10px; line-height: 22px; border-bottom: 1px solid #ddd; font-size: 14px; color: #333; overflow: hidden; background-color: #f8f8f8; border-radius: 3px 3px 0 0;" >Markdown格式编辑器 < span class = "mdDlgCloseBtn" onclick = "closeMDDialog()" style="float:right; width: 16px; height: 16px; justify-content: center; align-items: center; color: rgb(255, 255, 255); background-color: rgb(0, 0, 0); opacity: 0.7; cursor: pointer; line-height: 16px; text-align: center; border-radius: 50%; transition: all 0.3s ease 0s;">< svg width = "8" height = "10" viewBox = "0 0 9 9" fill = "none" xmlns = "http://www.w3.org/2000/svg" hanging = "8" >< path d = "M7.83725 1.30615L1.30664 7.83676M1.30664 1.30615L7.83725 7.83676" stroke = "currentColor" stroke-width = "1.2" stroke-linecap = "round" stroke-linejoin = "round" ></ path ></ svg ></ span > </ div > < textarea id = "mdContent" style = "width:600px;height:300px; margin: 10px 10px 0 10px;" ></ textarea > < div style = "text-align: right; padding: 10px;" > < button type = "button" onclick = "updateEditor()" >确定</ button > < button type = "button" onclick = "closeMDDialog()" >关闭</ button > </ div > </ div > |
脚本依赖
1 2 | < script type = "text/javascript" charset = "utf-8" src = "third-party/turndown720.js" ></ script > < script type = "text/javascript" charset = "utf-8" src = "third-party/markdown-it1301.min.js" ></ script > |
完整脚本实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | //以下为Markdown格式内容器相关代码 // 初始化转换器 const mdParser = window.markdownit(); const turndownService = new TurndownService({ codeBlockStyle: 'fenced' , // 强制使用围栏代码块 headingStyle: 'atx' // 保证标题转换兼容性 }); // ================= 多行代码块规则(高优先级) ================= turndownService.addRule( 'codeBlocks' , { filter: function (node) { // 匹配两种情况:1. PRE>CODE 2. 单独的PRE(兼容异常情况) return node.nodeName === 'PRE' && ( node.firstChild?.nodeName === 'CODE' || !node.querySelector( 'code' ) ); }, replacement: function (content, node) { // 提取代码内容 const codeNode = node.querySelector( 'code' ) || node; let codeText = codeNode.textContent; // 优化1:清理首尾空白 codeText = codeText.replace(/^\n+|\n+$/g, '' ); // 优化2:转义内部反引号 codeText = codeText.replace(/`/g, '\\`' ); // 优化3:识别语言类型(支持language-xxx和lang-xxx格式) const langMatch = codeNode.className.match(/(?:lang|language)-(\w+)/i); const lang = langMatch ? langMatch[1] : '' ; // 优化4:处理混合内容的情况 if (node !== codeNode.parentNode) { return `\`\`\`${lang}\n${codeText}\n\`\`\`\n\n`; } return `\`\`\`${lang}\n${codeText}\n\`\`\`\n\n`; } }); // ================= 行内代码/意外多行代码(低优先级) ================= turndownService.addRule( 'inlineCode' , { filter: [ 'code' ], replacement: function (content, node) { // 优化5:检测是否包含换行符 const hasNewline = /\n/.test(content); // 优化6:处理嵌套在pre外的代码 if (hasNewline || node.parentNode.nodeName !== 'PRE' ) { const escaped = content.replace(/`/g, '\\`' ); return hasNewline ? `\`\`\`\n${escaped}\n\`\`\`` : `\`${escaped}\``; } return content; // 已在codeBlocks规则处理 } }); turndownService.addRule( 'table' , { // 添加名为'table'的新转换规则 filter: 'table' , // 指定过滤目标为<table>标签 replacement: (content) => `\n${content}\n` // 定义转换方式:在表格内容前后添加换行符 }); let currentEditor = null ; function showMDDialog(editor) { currentEditor = editor; // 获取编辑器原始内容并转换 const htmlContent = editor.getContent(); const markdownContent = turndownService.turndown(htmlContent); document.getElementById( 'mdContent' ).value = markdownContent; document.getElementById( 'mdModal' ).style.display = 'block' ; document.getElementById( 'mdOverlay' ).style.display = 'block' ; } function closeMDDialog() { document.getElementById( 'mdModal' ).style.display = 'none' ; document.getElementById( 'mdOverlay' ).style.display = 'none' ; } // 更新编辑器内容 function updateEditor() { const markdownContent = document.getElementById( 'mdContent' ).value; const newHtml = mdParser.render(markdownContent); // 使用setContent完全替换编辑器内容 currentEditor.setContent(newHtml); closeMDDialog(); } UE.registerUI( 'insert_markdown' , function (editor, uiName) { editor.registerCommand(uiName, { execCommand: function () { showMDDialog(editor); } }); var btn = new UE.ui.Button({ name: uiName, title: "Markdown格式编辑器" , cssRules: 'background-position: -775px -76px;' , onclick: function () { editor.execCommand(uiName); } }); editor.addListener( 'selectionchange' , function () { var state = editor.queryCommandState(uiName); if (state == -1) { btn.setDisabled( true ); btn.setChecked( false ); } else { btn.setDisabled( false ); btn.setChecked(state); } }); return btn;}); |
本文由 微wx笑 创作,采用 署名-非商业性使用-相同方式共享 4.0 许可协议,转载请附上原文出处链接及本声明。
原文链接:https://www.ivu4e.cn/blog/front/2025-03-02/2040.html
下一篇:返回列表