富文本编辑器
Tinymce
注意事项
可安装对应的 Vue/React 组件直接作为组件使用,也可直接下载进行原生操作。
若报找不到文件错误,需将 tinymce 复制到 public 文件夹内。
安装
安装 Vue 模块的组件:
pnpm i @tinymce/tinymce-vue若为 React 项目则改为 @tinymce/tinymce-react。
安装 tinymce 包:
pnpm i tinymce使用
引入富文本编辑器:
import tinymce from 'tinymce'通过 tinymce.init() 方法的 selector 属性传入一个 id 选择器或类选择器,将富文本编辑器挂载到目标元素上。
注意
由于需获取元素,必须等待元素加载完毕。Vue2 项目中将方法写在 mounted 生命周期上,Vue3 项目将方法写在 onMounted 钩子上。
<script setup>
import { onMounted } from 'vue'
onMounted(() => {
tinymce.init({
selector: '#mytinymce',
})
})
</script>
<template>
<div id="mytinymce"></div>
</template>复制文件到 public 目录
编辑器加载后会请求其 JavaScript 文件和 CSS 文件,需将 node_modules 内 tinymce 的完整内容复制到 public 目录中,否则会报错。
项目打包上线后,同样需将这些资源放置到服务器的静态资源目录中。
常见界面需求
隐藏不需要的部分
组件方式:
vue<script setup> import { Editor } from '@tinymce/tinymce-vue' const initObj = { selector: '#mytinymce', menubar: false, toolbar: true, statusbar: true, } </script> <Editor :init="initObj" />直接使用
tinymce方式:menubar控制菜单,toolbar控制工具栏,statusbar控制状态栏。设为false即可隐藏对应部分。jsonMounted(() => { tinymce.init({ selector: '#mytinymce', menubar: false, toolbar: true, statusbar: true, }) })
自定义样式
skin 控制皮肤,可通过 skin_url 导入自定义皮肤;通过 content_css 定义内容区域样式;通过 icons_url 导入图标。
皮肤位于 public 目录中 skin 文件夹下的 UI 子目录内,填写对应文件夹名称即可,本质是加载该目录下的 skin.css 文件。
自定义皮肤则在 public 下找到自定义文件夹(如示例中的 myskin),其中包含 content.min.css 和 skin.min.css 文件。
onMounted(() => {
tinymce.init({
selector: '#mytinymce',
menubar: false,
toolbar: true,
statusbar: true,
skin: 'oxide',
skin_url: 'myskin',
})
})中文化
下载中文语言包,将 langs 文件夹解压到 public 目录内,在配置项中通过 language 属性指定语言文件名。
onMounted(() => {
tinymce.init({
selector: '#mytinymce',
menubar: false,
toolbar: true,
statusbar: true,
skin: 'oxide',
skin_url: 'myskin',
language: 'zh-Hans',
})
})进阶需求
获取内容
通过 tinymce.activeEditor.getContent() 获取 HTML 富文本内容。若需获取纯文本,传入 { format: 'text' }。
设置内容
通过 tinymce.activeEditor.setContent() 设置内容,参数为 HTML 字符串,如 '<p>daodao</p>'。
也可对选中内容执行替换:先选中目标内容,再调用 tinymce.activeEditor.selection.setContent('hello') 将其替换为指定内容。
二次开发
自带插件
安装目录下的 plugins 文件夹包含内置插件,如 image、link、code、table 等。
使用方式:在 plugins 属性中声明插件名称,在 toolbar 中添加对应按钮。
onMounted(() => {
tinymce.init({
selector: '#mytinymce',
menubar: false,
plugins: 'code image',
toolbar:
'undo redo | styles | bold italic | fontfamily fontsize fontcolor | code image',
statusbar: true,
skin: 'oxide',
skin_url: 'myskin',
language: 'zh-Hans',
})
})引入第三方插件时,将其复制到 plugins 文件夹下,并在 plugins 属性和 toolbar 属性中配置文件名即可。
开发自定义工具栏按钮
通过 setup 函数注册自定义按钮。该函数的形参为 Editor 对象,调用 editor.ui.registry.addButton 方法创建按钮。
该方法接收两个参数:
- 参数一:按钮名称(字符串)
- 参数二:配置对象,包含
icon(图标)、tooltip(提示文本)、onAction(点击回调函数)
onMounted(() => {
tinymce.init({
selector: '#mytinymce',
menubar: false,
plugins: 'code image',
toolbar:
'undo redo | styles | bold italic | fontfamily fontsize fontcolor | code image',
statusbar: true,
skin: 'oxide',
skin_url: 'myskin',
language: 'zh-Hans',
setup(editor) {
// 将选中内容标红
editor.ui.registry.addButton('red', {
icon: 'highlight-bg-color',
tooltip: '字体标红',
onAction: () => {
const text = editor.selection.getContent()
editor.selection.setContent(`<span class="red-text">${text}</span>`)
},
})
// 将选中内容展示在页面中
editor.ui.registry.addButton('show', {
icon: 'preview',
tooltip: '展示选中内容',
onAction: () => {
const text = editor.selection.getContent()
popShow.value = true
popValue.value = text
},
})
},
content_css: '/mycontent.css',
})
})大段 CSS 样式可在 public 文件夹中创建独立的样式文件进行定义。
vue-quill-editor
详细文档参考:vue-quill-editor
安装
pnpm install vue-quill-editor该插件基于 Quill,安装时会自动引入所需依赖,无需单独安装 Quill。
引入
全局引入:
在
main.js中引入插件:jsimport VueQuillEditor from 'vue-quill-editor' import 'quill/dist/quill.core.css' import 'quill/dist/quill.snow.css' import 'quill/dist/quill.bubble.css' Vue.use(VueQuillEditor)局部引入:
jsimport 'quill/dist/quill.core.css' import 'quill/dist/quill.snow.css' import 'quill/dist/quill.bubble.css' import { quillEditor } from 'vue-quill-editor' export default { components: { quillEditor }, }
全局引入使用默认导出的 VueQuillEditor,局部引入使用具名导出的 quillEditor。源码中同时提供了默认导出和包含 quillEditor 的具名导出。
使用
<template>
<div class="local-quill-editor">
<quill-editor
ref="myLQuillEditor"
v-model="content"
:options="editorOption"
class="editor"
@blur="onEditorBlur"
@focus="onEditorFocus"
@change="onEditorChange"
/>
</div>
</template>
<script>
const toolbarOptions = [
['bold', 'italic', 'underline', 'strike'],
['blockquote', 'code-block'],
[{ header: 1 }, { header: 2 }],
[{ list: 'ordered' }, { list: 'bullet' }],
[{ script: 'sub' }, { script: 'super' }],
[{ indent: '-1' }, { indent: '+1' }],
[{ direction: 'rtl' }],
[{ size: ['small', false, 'large', 'huge'] }],
[{ header: [1, 2, 3, 4, 5, 6, false] }],
[{ color: [] }, { background: [] }],
[{ font: [] }],
[{ align: [] }],
['clean'],
['image', 'video'],
]
export default {
name: 'LocalQuillEditor',
data() {
return {
content: '',
editorOption: {
modules: {
toolbar: toolbarOptions,
},
theme: 'snow',
placeholder: '请输入正文',
},
}
},
methods: {
onEditorBlur(e) {
console.log('onEditorBlur: ', e)
},
onEditorFocus(e) {
console.log('onEditorFocus: ', e)
},
onEditorChange(e) {
console.log('onEditorChange: ', e)
},
},
}
</script>
<style scoped lang="scss">
.editor {
height: 500px;
}
</style>图片上传到服务器
默认情况下,富文本编辑器将图片转为 Base64 格式内联存储。单张图片转码后约 234KB,少量图片即可导致数据库字段溢出。因此需将图片上传至服务器,使用返回的 URL 作为渲染路径。
官方文档:quill-image-extend-module
安装
pnpm install quill-image-extend-module --save-dev导入
import { quillEditor, Quill } from 'vue-quill-editor'
import { container, ImageExtend, QuillWatch } from 'quill-image-extend-module'
Quill.register('modules/ImageExtend', ImageExtend)使用
<template>
<div class="quill-wrap">
<quill-editor
v-model="content"
ref="myQuillEditor"
:options="editorOption"
/>
</div>
</template>
<script>
import { quillEditor, Quill } from 'vue-quill-editor'
import { container, ImageExtend, QuillWatch } from 'quill-image-extend-module'
Quill.register('modules/ImageExtend', ImageExtend)
export default {
components: { quillEditor },
data() {
return {
content: '',
editorOption: {
modules: {
ImageExtend: {
loading: true,
name: 'img',
action: updateUrl,
response: (res) => res.info,
},
toolbar: {
container: container,
handlers: {
image: function () {
QuillWatch.emit(this.quill.id)
},
},
},
},
},
}
},
}
</script>完整配置项
editorOption: {
modules: {
ImageExtend: {
name: "img", // 图片参数名
size: 3, // 图片大小限制,单位 MB
action: updateUrl, // 服务器上传地址,为空则使用 Base64
response: (res) => res.info, // 返回图片地址
headers: (xhr) => {}, // 设置请求头
sizeError: () => {}, // 超出大小回调
start: () => {}, // 开始上传回调
end: () => {}, // 上传结束回调
error: () => {}, // 上传失败回调
success: () => {}, // 上传成功回调
change: (xhr, formData) => {} // 每次选择图片触发
},
toolbar: {
container: container,
handlers: {
image: function () {
QuillWatch.emit(this.quill.id);
},
},
},
},
}注意事项
由于不同服务器返回的数据结构不同,response 函数必须正确返回图片地址。
示例:若服务器返回格式为:
{
"code": 200,
"status": true,
"result": {
"img": "http://placehold.it/xx.jpg"
}
}则配置为:
response: (res) => res.result.img,与其他模块配合使用(以 resize-module 为例)
<template>
<div class="quill-wrap">
<quill-editor
v-model="content"
ref="myQuillEditor"
:options="editorOption"
/>
</div>
</template>
<script>
import { quillEditor, Quill } from 'vue-quill-editor'
import { container, ImageExtend, QuillWatch } from 'quill-image-extend-module'
import ImageResize from 'quill-image-resize-module'
Quill.register('modules/ImageExtend', ImageExtend)
Quill.register('modules/ImageResize', ImageResize)
export default {
components: { quillEditor },
data() {
return {
content: '',
editorOption: {
modules: {
ImageResize: {},
ImageExtend: {
name: 'img',
size: 2,
action: updateUrl,
headers: (xhr) => {},
response: (res) => res.info,
},
toolbar: {
container: container,
handlers: {
image: function () {
QuillWatch.emit(this.quill.id)
},
},
},
},
},
}
},
}
</script>