VueJS 集成 Medium Editor的示例代码 (自定义编辑器按钮)
0x00 前言
vuejs 社区里面关于富文本编辑器的集成也不少了,但是之前小调研了一下,基本上就是 quill,medium-editor,因为之前用 angularjs 用过 medium-editor,并且需要自定义某些按钮,而且最好还是选中弹出式的,所以就决定用 medium-editor。
社区里面 star 较多的就是这个了:vue-medium-editor,但是打开它官网,看了看文档,越看越别扭,来看看它用法:
<!-- index.html --> <medium-editor :text='mytext' :options='options' custom-tag='h2' v-on:edit='applytextedit'>
gosh,传这么多参数,我只想要一个简单的 editor 啊
打开源码一看,就 62 行,所以决定自己动手来一个简单点的
0x01 最简版
最简版,其实就在 vue 组件中实例化一下 medium-editor 就可以了
<template> <div class="texteditor" @input="handleinput"> </div> </template> <script> /* eslint-disable no-new */ import mediumeditor from 'medium-editor' export default { props: { value: string, options: { type: object, default: () => ({}) } }, data () { return { editor: null // 用来存放 editor } }, watch: { // refer: https://github.com/franzskuffka/vue-medium-editor/blob/master/index.js value (newval, oldval) { if (newval !== this.$el.innerhtml) { // 用 $el.innerhtml 来解决 v-html 的光标跳到行首的问题 this.$el.innerhtml = newval || '' } } }, methods: { handleinput (e) { this.$emit('input', e.target.innerhtml) } }, mounted () { // 处理初始值的情况 this.$el.innerhtml = this.value // 这里当然可以自定义 options 啦 this.editor = new mediumeditor(this.$el, object.assign({}, this.options)) // medium-editor 的 api,监听内容改变化 this.editor.subscribe('editableinput', this.handleinput) }, beforedestroy () { this.editor.unsubscribe('editableinput', this.handleinput) this.editor.destroy() } } </script>
完成~,是不是很简单~~哈哈,最简版是一个 v-html 控制的,但是会有自动跳转到首行的问题,所以这里是最终版,细节问题看注释啦
0x02 用法
咋用呢?很简单,在其他组件中这样:
<text-editor v-model="vm.richtext"></text-editor>
当然 你首先得安装 medium-editor的 js 和 css了
0x03 自定义 button
下面是我项目中用到的自定义 button 的相关代码,是一个 buttonbuilder:
import mediumeditor from 'medium-editor' import rangy from 'rangy/lib/rangy-core.js' import 'rangy/lib/rangy-classapplier' import 'rangy/lib/rangy-highlighter' import 'rangy/lib/rangy-selectionsaverestore' import 'rangy/lib/rangy-textrange' import 'rangy/lib/rangy-serializer' const phash = { p1: { name: 'p1', class: 'fs-36' }, p2: { name: 'p2', class: 'fs-30' }, p3: { name: 'p3', class: 'fs-24' }, p4: { name: 'p4', class: 'fs-18' }, p5: { name: 'p5', class: 'fs-14' }, p6: { name: 'p6', class: 'fs-12' } } function pbuttoncreator (p) { return mediumeditor.extension.extend({ name: p.name, init: function () { this.classapplier = rangy.createclassapplier(p.class, { elementtagname: 'span', normalize: false }) this.button = this.document.createelement('button') this.button.classlist.add('medium-editor-action') this.button.innerhtml = p.name this.button.title = p.class this.on(this.button, 'click', this.handleclick.bind(this)) }, getbutton: function () { return this.button }, clearfontsize: function () { mediumeditor.selection.getselectedelements(this.document).foreach(function (el) { if (el.nodename.tolowercase() === 'span' && el.hasattribute('class')) { el.removeattribute('class') } }) }, handleclick: function (event) { this.clearfontsize() this.classapplier.toggleselection() // ensure the editor knows about an html change so watchers are notified // ie: <textarea> elements depend on the editableinput event to stay synchronized this.base.checkcontentchanged() } }) } export default { p1: pbuttoncreator(phash['p1']), p2: pbuttoncreator(phash['p2']), p3: pbuttoncreator(phash['p3']), p4: pbuttoncreator(phash['p4']), p5: pbuttoncreator(phash['p5']), p6: pbuttoncreator(phash['p6']) }
简单来说就是给选中的文字加一些 class (上面是 fs-xx 之类的),其中需要引一个鼠标选中的库 rangy,挺烦人的也是,然后在 text-editor 中这样用:
先实例化
import buttonbuilder from './buttonbuilder' var editoroptions = { toolbar: { buttons: ['bold', 'italic', 'underline', 'removeformat', 'p3', 'p4', 'p5', 'p6'] }, buttonlabels: 'fontawesome', // use font-awesome icons for other buttons extensions: { p3: new buttonbuilder.p3(), p4: new buttonbuilder.p4(), p5: new buttonbuilder.p5(), p6: new buttonbuilder.p6() }, placeholder: false }
再放到 editor 上
this.editor = new mediumeditor(this.$el, object.assign({}, editoroptions, this.options))
当然上面实例化的步骤不一定要写到这个组件里面,配置 options 也可以从组件外传入
0x04 细节和坑
1、这里用到了 v-model 的自定义实现,详见官方文档:v-model
简单来说呢就是 props: value ,和 this.$emit('input', model) 就可以实现在组件中模拟 v-model 啦
2、多个 editor 使用的自定义button 实例的问题。由于我自己应用的时候有两个挨着的 <text-editor>,用的上面的代码会导致两个 editor 实例用的是同一个 button 实例,这会导致一个很严重的问题:即编辑下面编辑器的内容,可能会修改的上面的编辑器!!
要解决这个也很简单,修改这一行:
this.editor = new mediumeditor(this.$el, object.assign({}, _.clonedeep(editoroptions), this.options))
将自定义的 options 深复制一下,这里借助了 lodash 的函数。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。