一、为什么要使用tinymce框架
AI 大模型正在把"富文本编辑器"从「打字板」变成「智能办公伙伴」。
前端工程师的交付物不再只是"能打字",而是"会思考、会润色、会排版、会翻译、会合规"的 AI 文书工作台,这是LLM在应用层的一个重要方面。对于前端工程师来说要实现这样的功能富文本编辑器是必不可少的。我们为什么要选择tinymce呢?它需要满足以下几个条件
(1)稳定可靠
对于前端工程师来说,可以用的富文本编辑器简直不要太多,但是我们要选择一个高度可靠的,当前正维护的富文本编辑器,并且开源可用的,tinymce分为商用和免费两个版本,并且是正在维护的项目,使用的人多。
(2)插件丰富可扩展
tinymce有丰富的插件,并且插件可扩展,但是插件由两部分,分为免费和付费的。一般情况下免费的就足够了,对于一些简单的并且没有免费的插件,我们可以自己扩展插件。
(3)高度可定制化
一般情况下,前端富文本编辑器是一般UI:

要做智能文书我们就需要富文本编辑器的UI高度可定制化,例如:妙笔平台的ai文书平台:

不但需要UI样式可定制化,而且需要toolbar高度可定制化,并且还可以支持侧边栏功能。
二、tinymce使用经验记录
tinymce的一个重要的问题是没有中文文档,并且分为收费和免费两个版本。我们应该如何使用呢?
(1)本地化部署
到:tinymce官网,选择免费版本,当然你也可以使用收费版本,更加好用哦。
下载好后就可以在本地的vue3 项目中引入,放到public文件夹下,然后添加引用。

(2)toolbar配置
虽然文档都是英文的,不过结构清晰,其实是很好用的:

例如我们想实现,像妙笔那样的效果:就用以下代码:
php
tinymce.init({
selector: 'textarea#default',
promotion: false, //取消付费推广信息
branding: false, //取消付费推广信息
language: 'zh_CN',
toolbar_mode: 'sliding',
menubar: false,
skin: 'tinymce-5',
placeholder: '请输入内容...',
license_key: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', //你自己申请的licenese
height: 'calc(100vh - 66px)', //自定义高度
content_css: '/tinymce/tiny-scroll.css', //一些自定义样式
inline: false, // 设置为true时,编辑器会自动将内容包裹在<div>标签中,并添加一些默认样式。要模仿他们的,放到单独的iframe中。
content_style: `
body {
font-family: "宋体";
font-size: 16px;
padding:1in 0.8in 1in 1in;
}
`, //也可以放到自定义样式里面。
plugins: 'table fullscreen wordcount',
color_cols: 5,
toolbar: 'undo redo removeformat | fontfamily fontsize | bold italic underline strikethrough forecolor backcolor| h1 h2 h3 h4 | sideName',
setup: function (editor) {}, //做一些页面的设置
init_instance_callback: (newEditor) => {} //初始化文成后的回调。
});
这样我们就可以自定义UI的样式,实现例如a4纸的效果。
(3)toolbar私有化配置
如果我们想自定义toolbar,并且点击后要打开右边的侧边栏需要怎么做呢? 其实很简单,注册图标icon --> 注册sidebar,并且应该自定义图标 这个时候我们就可以看看api文档了。
虽然都是英文但是也很好理解。 下面直接看代码(注意下面的代码都要放到setup方法里面):
xml
editor.ui.registry.addIcon(
'youicon',
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" ></svg>');
这里有一点需要注意,如果你的图标有文字,那么把文字和图片都放到svg里面就行了。 然后注册sidebar:
javascript
editor.ui.registry.addSidebar('sideName', {
icon: 'youicon', //你注册的图标
onSetup: (api) => {},
onShow: (api) => {},
onHide: (api) => {}
})
这样就注册了一个空的sidebar,名称为:sideName,将这个sidebar添加到toolbar上就行了。这样就实现了自定义toolbar,并且绑定到sidebar上。
(4)深入融合vue3组件
如果我们想在sidebar上添加自定义vue3组件怎么办呢?其实答案就藏在问题里面:在sidebar的onshow方法里面,我们再手动挂载个vue3 组件就行了。然后传递给他一些属性和方法,方法可以用于回调。其实就是两个vue实例都和tinymce交互,这样就是实现了组件之间的交互,下面是一个简单的例子,注意再onshow方法里面:
javascript
import youComponents from 'xxxxxxxxxxxxxxxxxxxx';
......
const callbarFunction = () => {
} //交互回调。
......
onShow: (api) => {
const mountPoint = document.createElement('div');
api.element().appendChild(mountPoint);
const vueApp = createApp(youComponents, {
editor, // 将编辑器实例传递给组件
callbarFunction: callbarFunction,
});
vueApp.mount(mountPoint);
},
这样就把你的vue对象挂载到对应的sidebar上面,并且将editor实例传递过去,还有回调函数。能完成数据交互。
(5)tinymce事件系统
所有的事件都要放到setup函数里面进行定义。
5.1鼠标键盘事件
如果想在tinymce监听鼠标键盘事件。直接看代码:
javascript
setup: function (editor) {
editor.on('keyup', function (e) {
//键盘事件
});
editor.on('mousedown', function (e) {
//鼠标事件
});
editor.on('init', function (e) {
//初始化事件
});
editor.on('change', function (e) {
//文档改变事件
});
}
tinymce支持那些事件呢?看文档:www.tiny.cloud/docs/tinymc... 。 这里有你所需要的大部分事件。
5.2自定义命令
如果我们想自定义一些命令,然后手动触发它怎么做呢?
javascript
editor.on('ExecCommand', (e) => {
console.log(e.value) //自定义指令的名称
})
这个是用来监听自定义指令的。如果我们想自动打开sidebar,然后传递个参数进去:
arduino
tinymce.activeEditor.execCommand('ToggleSidebar', false, 'params');
你也可以查看参考文档:www.tiny.cloud/docs/tinymc...
(6)tinymce的前端安全
这是一个老生常谈的问题了。凡是富文本就离不开前端安全问题。当然我们的老熟人DOMPurify要用起来。
6.1使用v-safe-html代替v-html
javascript
app.directive('safe-html', {
beforeMount(el, binding) {
el.innerHTML = DOMPurify.sanitize(binding.value)
},
updated(el, binding) {
el.innerHTML = DOMPurify.sanitize(binding.value)
}
})
6.2保存到服务端的数据要进行转义
arduino
const clean = DOMPurify.sanitize(content)//将clean保存到服务端。
将转义后的内容保存到服务端。
项目心得
(1)前端保存docx需要注意事项
以前的项目都是服务端生成docx然后下载就行了,如果前端自己将页面生成docx然后下载需要注意什么呢? docx支持p,table,ul,li等少数几个标签,并且样式要写到style里面。并且并不支持所有的css样式,例如常用的display:flex。如果你是个老前端,知道为什么有flex布局,不是用flex如何布局。
(2)踩过的坑。
这个项目全部都采用tinymce结构,不能有的地方用,有的地方不用。这样容易出错,可以参考别人做过的,大体结构要按照别人的写。