author: jwensh
date: 2021.06.17
在项目开发用到富文本框,期间使用过
Tinymce
和toast-ui/editor
(一款支持Markdown、WYSIWYG模式的编辑器),最后选择了toast-ui
,这里不介绍两者的区别,主要感觉它好用。遇到的问题是:富文本编辑内容时,相关资源(img、video等)如何上传到指定服务器上?
Toast UI Editor 版本 "@toast-ui/editor": "2.5.2"
VUE2
工程里使用组件和插件
npm install @toast-ui/editor
图片上传
、截图粘贴
、图片拖入
toast-ui
怎么处理上面三个操作的?默认使用
addImageBlobHook
监听事件将图片转换成Base64
追加到文本中
Toast UI Editor
支持三种图片上传方式:弹窗选择
、拖拽
、截屏粘贴
;三种上传方式最终都会被 addImageBlobHook
监听,并处理为 Base64
格式的图片;
addImageBlobHook
监听事件,位于 src/js/importManager.js 中 第:124 行 /**
* Initialize default image importer
* @private
*/
_initDefaultImageImporter() {
this.eventManager.listen('addImageBlobHook', (blob, callback) => {
const reader = new FileReader();
reader.onload = event => {
callback(event.target.result);
};
reader.readAsDataURL(blob);
});
}
removeEventHandler(typeStr, handler)
第 210行/**
* Remove event handler from given event type
* @param {string} typeStr Event type name
* @param {function} [handler] - registered event handler
*/
removeEventHandler(typeStr, handler) {
const { type, namespace } = this._getTypeInfo(typeStr);
if (type && handler) {
this._removeEventHandlerWithHandler(type, handler);
} else if (type && !namespace) {
// dont use dot notation cuz eslint
this.events.delete(type);
} else if (!type && namespace) {
this.events.forEach((eventHandlers, eventType) => {
this._removeEventHandlerWithTypeInfo(eventType, namespace);
});
} else if (type && namespace) {
this._removeEventHandlerWithTypeInfo(type, namespace);
}
}
addImageBlobHook
监听事件// 删除默认监听事件
this.editor.eventManager.removeEventHandler('addImageBlobHook')
addImageBlobHook
监听事件// 添加自定义监听事件
this.editor.eventManager.listen('addImageBlobHook', (blob, callback) => {
// 此处填写自己的上传逻辑,url为上传后的图片地址
this.uploadFile(blob, url => {
callback(url)
})
})
后端服务实现一个可以上传文件的接口,单文件上传即可;语言自行选择,静态文件可以使用 nginx
挂载
<template>
<div :id="id" />
</template>
<script>
/**
* @author jwensh
* 这里使用 @toast-ui/editor@v2.5.2 版本的markdown编辑器
*/
import 'codemirror/lib/codemirror.css' // Editor's Dependency Style
import '@toast-ui/editor/dist/toastui-editor.css' // Editor's Style
import Editor from '@toast-ui/editor'
import defaultOptions from './default-options'
export default {
name: 'MarddownEditor',
props: {
value: {
type: String,
default: ''
},
id: {
type: String,
required: false,
default() {
return 'markdown-editor-' + +new Date() + ((Math.random() * 1000).toFixed(0) + '')
}
},
options: {
type: Object,
default() {
return defaultOptions
}
},
mode: {
type: String,
default: 'wysiwyg'
},
height: {
type: String,
required: false,
default: '300px'
},
language: {
type: String,
required: false,
default: 'en_US'
}
},
data() {
return {
editor: null
}
},
computed: {
editorOptions() {
const options = Object.assign({}, defaultOptions, this.options)
options.initialEditType = this.mode
options.height = this.height
options.language = this.language
return options
}
},
watch: {
value(newValue, preValue) {
if (newValue !== preValue && newValue !== this.editor.getHtml()) {
this.editor.setHtml(newValue)
}
},
language(val) {
this.destroyEditor()
this.initEditor()
},
height(newValue) {
this.editor.height(newValue)
},
mode(newValue) {
this.editor.changeMode(newValue)
}
},
mounted() {
this.initEditor()
},
methods: {
initEditor() {
this.editor = new Editor({
el: document.getElementById(this.id),
...this.editorOptions
})
if (this.value) {
this.editor.setHtml(this.value)
}
this.editor.on('blur', () => {
this.$emit('updateContent', this.editor.getHtml())
})
// 删除默认监听事件后,添加自定义监听事件
this.editor.eventManager.removeEventHandler('addImageBlobHook')
this.editor.eventManager.listen('addImageBlobHook', (blob, callback) => {
// 此处填写自己的上传逻辑,url为上传后的图片地址
const formData = new FormData()
formData.append('files', blob)
const ajax = new XMLHttpRequest()
ajax.open('POST', 'http://***/v1/uploadfiles?user_id=jwensh', true)
ajax.send(formData)
ajax.onreadystatechange = function() {
if (ajax.readyState === 4) {
if ((ajax.status >= 200 && ajax.status < 300) || ajax.status === 304) {
const obj = JSON.parse(ajax.responseText)
if (obj.code && obj.code === 'true') {
callback(obj.result.root_path + obj.result.url)
}
}
}
}
})
}
}
}
</script>
到此算是解决三种方式(Popup、Drag、Screenshot)按照自己的上传逻辑进行图片上传