赞
踩
首先,要搞清楚一点,markdown 编辑器与传统的富文本编辑器实际上一点区别都没有!只是可能由于某些原因放到了一个概念,那么是一个什么概念呢?
在传统的富文本时代,我们不需要接触任何的标签,我们只用编辑器自带的功能就行,而markdown多了一层标签的概念,而这一层标签的概念被大家无限放大,感觉markdown 是什么神奇的东西。
在富文本编辑器年代,对于引入一个链接,你只要复制这个链接,然后粘贴过去。而在目前部分markdown编辑器,由于设计者默认你懂了markdown的语法就不搞这么一个工具栏而是让你自己手动加入,由于一些完全不知道markdown是什么东西的人来说,就觉得与平常的东西不一样,就感觉markdown是什么神奇的东西。
实际上markdown 一点都不神奇,也没有一点的与众不同,它与大家认知的富文本编辑器实际都一样的,就是一个html解释器在富文本的时候是一个工具栏,在markdown里面是一些标签语法一样。上面说了一些markdown的概念,现在回到这个问题本身,个人认为这两者是没有比较的意义,因为两者都是html的解析器引擎,富文本编辑器操作上面比较简单,但是,markdown一样能做到,就如同segmentfault的编辑器,就有用到markdown,不过可能加了一个工具栏,可能就觉得这是一个富文本编辑器而不是markdown。
由于项目中的回复问题模块可读性较差,需要增加一个富文本编辑器的功能,项目是用vue做的,最终选择了mavon-editor做富文本编辑器,富文本编辑器集成到项目中效果如下:
首先,要想使用mavon-editor,需要先下载并安装
npm install mavon-editor --save
下载完毕查看package.json中是否显示"mavon-editor": “^2.9.0”,确保下载成功就好
接下来就是在项目中使用了,在页面中如下引用
<template> <div class="mavonEditor"> <mavon-editor ref="md" :toolbars="markdownOption" :autofocus="false" v-model="markdownStr" @imgAdd="$imgAdd" @change="changeEditor"> </mavon-editor> <input type="file" @change="getFile" id="imgFile" accept="image/*" /> <label for="imgFile" class="nb-img-box fix-icon"> <i class="nb-image"></i> </label> <i class="nb-tip fix-icon" @click="markdownTip = true"></i> <a-modal title="Markdown语法参考" width="320px" :footer="null" :closable="false" class="common-modal" :visible.sync="markdownTip"> <Tip></Tip> <div class="closeDialog" @click="markdownTip = false">我知道了</div> </a-modal> </div> </template> <script> import { attachNoticesApi } from '@/apis' import { mavonEditor } from 'mavon-editor' import { cbSuccess } from '@/utils' import Tip from './tip' export default { data() { return { markdownTip: false, markdownOption: { bold: true, // 粗体 header: true, // 标题 underline: true, // 下划线 strikethrough: true, // 中划线 mark: true, // 标记 quote: true, // 引用 ol: true, // 有序列表 ul: true, // 无序列表 link: true, // 链接 // imagelink: true, // 图片链接 code: true, // code table: true, // 表格 fullscreen: true, // 全屏编辑 alignleft: true, // 左对齐 aligncenter: true, // 居中 alignright: true, // 右对齐 subfield: true, // 单双栏模式 }, markdownStr: '', markdownHtmlStr: '' }; }, props: { value: { type: String, default: () => { return '' } } }, watch: { value (val) { this.markdownStr = val } }, components: { mavonEditor, Tip }, methods: { getFile (e) { if (!e || !window.FileReader) return let file = e.target.files[0] this.uploadImg(file) }, async uploadImg (file) { let formdata = new FormData() let $vm = this.$refs.md formdata.append('file', file) let { data } = await attachNoticesApi(formdata) if (data.success) { let _imgUrl = data.operateCallBackObj let _text = file.name let insert_text = { prefix: `![${_text}](`, subfix: ')', str: _imgUrl } $vm.insertText($vm.getTextareaDom(), insert_text) } else { this.$message.error(data.operateMessage) } }, // 绑定@imgAdd event上传图片 async $imgAdd (pos, $file) { let formdata = new FormData() formdata.append('file', $file) let { data } = await attachNoticesApi(formdata) if (data.success) { this.$refs.md.$img2Url(pos, data.operateCallBackObj) } else { this.$message.error(data.operateMessage) } }, changeEditor (mdStr, htmlStr) { this.$emit('setValue', htmlStr, mdStr, 'markdown') this.markdownHtmlStr = htmlStr }, setContent (html) { let turndownService = new TurndownService() let _markdown = turndownService.turndown(html) this.markdownStr = _markdown || "" } }, created () { this.markdownStr = this.value || "" } } </script> <style lang="stylus"> .closeDialog{ font-size: 16px; line-height: 20px; border-top: 1px solid #d9d9d9; padding: 15px 0 0; text-align: center; color: #ec7259; cursor: pointer; } .mavonEditor{ .v-note-wrapper .v-note-op .v-left-item, .v-note-wrapper .v-note-op .v-right-item{ flex none } .op-icon-divider{ display none } } </style> <style lang="stylus" scoped> .mavonEditor{ overflow auto border 1px solid #DCDFE6 position relative .markdown-body >>> .v-right-item{ max-width 100px margin-left 60px } .fix-icon{ cursor pointer color #757575 position absolute text-align center top 12px right 70px z-index 90 width 20px } .nb-tip{ left 475px font-size 19px } .nb-img-box{ left 445px .i{ font-size 19px } } } </style>
由于项目中不仅仅需要富文本支持,还需要Markdown和富文本切换,所以又做了一个切换的组件。
下面是wEditor组件的内容。
<template> <div class="w-editor-box"> <div id="wEditor"></div> </div> </template> <script> import hljs from 'highlight.js' import { attachNoticesApi } from '@/apis' export default { data () { return { editor: null, isHTML: false } }, props: { value: { type: String, default: () => { return '' } } }, methods: { initEditor () { let _this = this const E = window.wangEditor const { $, BtnMenu } = E class AlertMenu extends BtnMenu { constructor(editor) { const $elem = E.$( `<div class="w-e-menu"> <i class="nb-html"></i> </div>` ) super($elem, editor) } clickHandler() { _this.showSource() this.tryChangeActive() } tryChangeActive() { if (_this.isHTML) this.active() else this.unActive() } } this.editor = new E('#wEditor') const editor = this.editor editor.config.menus = [ 'bold', 'head', 'italic', 'underline', 'strikeThrough', 'link', 'list', 'justify', 'quote', 'image', 'table', 'code', 'undo', 'redo', ] editor.highlight = hljs editor.config.languageType = [ 'SQL', 'Shell Session', 'Java', 'JavaScript', 'JSON', 'Markdown', 'TypeScript', 'Plain text', 'Html', 'CSS', 'Python', 'XML', 'Go', 'Bash', 'C', 'C#', 'C++', 'Kotlin', 'Lua', 'PHP', 'Ruby', ] // 注册菜单 const menuKey = 'alertMenuKey' // 菜单 key ,各个菜单不能重复 editor.menus.extend('alertMenuKey', AlertMenu) editor.config.menus = editor.config.menus.concat(menuKey) editor.config.zIndex = 8 editor.config.customUploadImg = function (resultFiles, insertImgFn) { let file = resultFiles[0] let formdata = new FormData() formdata.append('file', file) attachNoticesApi(formdata).then(res => { let imgUrl = res.data.operateCallBackObj insertImgFn(imgUrl) }) } editor.config.onchange = function (newHtml) { let _text = editor.txt.text() if (_this.isHTML) { newHtml = _text.replace(/</ig, "<").replace(/>/ig, ">").replace(/ /ig, " ") } _this.$emit('setValue', newHtml, _text, 'richtxt') } editor.create() if (_this.value) _this.setContent(_this.value) }, setContent (html) { this.editor.txt.html(html) }, showSource () { let _this = this let _editor = _this.editor _this.isHTML = !_this.isHTML let _source = _editor.txt.html() if (_this.isHTML) { _source = _source.replace(/</g, "<").replace(/>/g, ">").replace(/ /g, " ") } else { _source = _editor.txt.text().replace(/</ig, "<").replace(/>/ig, ">").replace(/ /ig, " ") } _editor.txt.html(_source) } }, mounted () { this.initEditor() } } </script> <style lang="stylus"> .w-e-toolbar { padding 6px .w-e-menu{ width 28px height 28px margin 0 2px .nb-html{ font-size 22px } i{ color #757575 } } } </style>
引入之后,可以进行富文本和Markdown的切换
<template> <div class="mw-editor-box"> <div class="editor-switch flex center" @click="switchShow = true"> <i class="nb-qiehuan"></i> <span>{{isMarkdown ? '富文本' : 'Markdown'}}</span> </div> <MavonEditor v-show="isMarkdown" ref="MavonEditor" :value="mdValue" @setValue="setMdValue"> </MavonEditor> <WEditor v-show="!isMarkdown" ref="WEditor" :value="htmlValue" @setValue="setMdValue"> </WEditor> <a-modal class="common-modal" title="提示" width="320px" :footer="null" :closable="false" :visible.sync="switchShow"> <div class="tip-box">切换为{{isMarkdown ? '富文本' : 'Markdown'}}编辑器后,保留内容可能会出现不兼容现象,确定要切换吗?</div> <div class="btn-box flex center"> <div class="nimbus-btn gray mr20" @click="switchShow = false">取消</div> <div class="nimbus-btn bold" @click="confirmSwitch">确定</div> </div> </a-modal> </div> </template> <script> import MavonEditor from './mavonEditor' import WEditor from './wEditor' export default { data () { return { isMarkdown: true, switchShow: false } }, props: { htmlValue: { type: String, default: () => { return '' } }, mdValue: { type: String, default: () => { return '' } } }, components: { MavonEditor, WEditor }, methods: { setMdValue (html, md, editorType) { if (this.isMarkdown && editorType === 'markdown' || (!this.isMarkdown && editorType === 'richtxt')) { this.$emit('setValue', html, md, editorType) } }, confirmSwitch () { let _refs = this.$refs if (this.isMarkdown) { let _html = _refs.MavonEditor.markdownHtmlStr _refs.WEditor.setContent(_html) this.isMarkdown = false } else { let _html = _refs.WEditor.editor.txt.html() _refs.MavonEditor.setContent(_html) this.isMarkdown = true } this.switchShow = false } }, created () { // 如果md无值,且html有值时,默认采用富文本编辑器 if (this.htmlValue && !this.mdValue) this.isMarkdown = false } } </script> <style lang="stylus"> .article-mavon-editor{ height calc(100% - 60px) .mavonEditor{ height 100% .v-note-wrapper{ height 100% box-shadow none margin-bottom 0 } } .w-editor-box{ height 100% #wEditor{ height 100% .w-e-text-container{ height calc(100% - 42px) !important } } } } #reply-editor{ .w-editor-box{ height 100% #wEditor{ height 100% .w-e-text-container{ height 256px !important } } } } </style> <style lang="stylus" scoped> .mw-editor-box{ position relative } .editor-switch{ position absolute padding 0 8px height: 24px; border: 1px solid #E8EEFC; border-radius: 4px; z-index 99 right 20px top 10px background #ffffff cursor pointer font-size 12px color #757575 i{ font-size 13px color #737373 margin-right 3px } } </style>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。