效果图:
VUE项目引入tinymce
1、tinymce安装以及下载
npm install @tinymce/tinymce-vue -save
npm install tinymce -save
①、安装完成以后,在安装目录node_modules 中找到 tinymce/skins 目录,然后将 skins 目录拷贝到 public(cli3是public目录,cli2应该是static目录) 目录下。
②、tinymce默认是英文界面,需要下载一个中文包,选择对应的中文包进行下载。把这个中文包也放在public或static目录下面。
下载地址:Language Packages | Trusted Rich Text Editor | TinyMCE
我的项目使用版本:cli3+tinymce5,即放入public目录下并加了层名为tinymce的子目录,如下图:
2、封装成项目组件:vl-tinymce.vue
①、使用tinymce组件
<template>
<div class="tinymce-editor">
<editor :id="tinymceId" v-model="myValue" :init="init" v-bind="$attrs" v-on="inputListeners">
</editor>
</div>
</template>
②、引入包
import tinymce from 'tinymce/tinymce'
import Editor from '@tinymce/tinymce-vue'
import 'tinymce/themes/silver'
发现编辑器图片找不到,引入图标
import 'tinymce/icons/default/icons'
③、注册组件
tinymce/tinymce是一个组件,引入组件注册,直接使用
//注册组件
components: {
Editor
}
④、初始化配置
mounted() {
tinymce.init({})
},
init: {
language_url: '/tinymce/zh_CN.js', //指定中文包
language: 'zh_CN',//中文
skin_url: '/tinymce/skins/ui/oxide',//编辑器皮肤,
height: 500,//高度
browser_spellcheck: true, // 拼写检查
branding: false, // 去水印
elementpath: false, // 禁用编辑器底部的状态栏
statusbar: false, // 隐藏编辑器底部的状态栏
paste_data_images: true, // 允许粘贴图像
menubar: false // 隐藏最上方menu
}
⑤、引用插件
⑥、完善组件
问题一:页面实现数据回显时,tinymce无法回显绑定值
分析原因:数据赋值与tinymce组件渲染顺序导致
解决方案:
1)、当因当前页面刷新导致时
将需要回显值作为默认值添加上,考虑使用init》setup属性
init: {
setup: (editor) => {
// 初次化编辑器
editor.on('init', () => {
editor.setContent(this.value)
})
},
},
2)、当因页面跳转导致时
考虑使用watch监听回显值
watch: {
value(val) {
this.$nextTick(() => tinymce.get(this.tinymceId).setContent(val))
},
},
3)、以上2点考虑都是基于所使用的组件最初使用时,所以我们采用一个标识记录一下当前组件是否最初使用,达到一次回显的目的即可。
我采用的标识名称为“isInit”,请参考我的代码分析。
问题二:通常我们设置内容后,光标会跑到最前面
解决方案:
加入一下代码
editor.selection.select(editor.getBody(),true);
editor.selection.collapse(false);
⑦、我的代码
<template>
<div class="tinymce-editor">
<editor :id="tinymceId" v-model.trim="myValue" :init="init" v-bind="$attrs" v-on="inputListeners">
</editor>
</div>
</template>
<script>
// 文档 http://tinymce.ax-z.cn/
// 引入组件
import tinymce from 'tinymce/tinymce' // tinymce默认hidden,不引入不显示
import Editor from '@tinymce/tinymce-vue'
// 引入富文本编辑器主题的js和css
import 'tinymce/skins/content/default/content.css'
import 'tinymce/themes/silver'
import 'tinymce/icons/default/icons.min.js' // 解决了icons.js 报错Unexpected token '<'
// 编辑器插件plugins
// 更多插件参考:https://www.tiny.cloud/docs/plugins/
import 'tinymce/plugins/image' // 插入上传图片插件
import 'tinymce/plugins/media' // 插入视频插件
import 'tinymce/plugins/table' // 插入表格插件
import 'tinymce/plugins/lists' // 列表插件
import 'tinymce/plugins/wordcount' // 字数统计插件
import 'tinymce/plugins/link'
import 'tinymce/plugins/code'
import 'tinymce/plugins/preview'
import 'tinymce/plugins/fullscreen'
import 'tinymce/plugins/help'
export default {
inheritAttrs: false,
components: {
Editor,
},
name: 'Tinymce',
props: {
id: {
type: String,
default: function () {
return new Date().getTime() + ''
},
},
value: {
type: String,
default: '',
},
height: {
type: Number,
default: 300,
},
plugins: {
type: [String, Array],
default: 'link lists image code table wordcount media preview fullscreen help',
},
toolbar: {
type: [String, Array],
default:
'bold italic underline strikethrough | fontsizeselect | formatselect | forecolor backcolor | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent blockquote | undo redo | link unlink code lists table image media | removeformat | fullscreen preview',
},
},
data() {
const that = this
return {
// 自动生成的id
tinymceId: this.id,
init: {
selector: `#${this.tinymceId}`,
language_url: '/tinymce/langs/zh_CN.js', // 语言包的路径
language: 'zh_CN', //语言
skin_url: '/tinymce/skins/ui/oxide', // skin路径
height: this.height, //编辑器高度
branding: false, //是否禁用“Powered by TinyMCE”
menubar: false, //顶部菜单栏显示
statusbar: false, //是否显示底部的状态栏
plugins: this.plugins,
toolbar: this.toolbar, // (自定义工具栏)
// 此处为图片上传处理函数,这个直接用了base64的图片形式上传图片,
// 如需ajax上传可参考https://www.tiny.cloud/docs/configure/file-image-upload/#images_upload_handler
images_upload_handler: (blobInfo, success, failure) => {
const img = 'data:image/jpeg;base64,' + blobInfo.base64()
success(img)
},
setup: (editor) => {
// 初次化编辑器
editor.on('init', () => {
that.isInit = true //告知是初始化\
if (that.value) {
editor.setContent(that.value)
that.isInit = false
}
editor.on('input change undo redo', () => {
// this.value = editor.getContent()
//此处设置为false很好解决光标前置问题
that.isInit = false
})
})
},
},
myValue: this.value,
isInit: false, //是否初始化
}
},
computed: {
inputListeners() {
const newListeners = {
...this.$listeners,
input: (event) => {
this.$emit('input', event)
},
}
return newListeners
},
},
mounted() {
tinymce.init({})
},
beforeDestroy: function () {
tinymce.remove(this.tinymceId)
},
methods: {
/**
* 获取富文本text类型内容
*/
getTextContent(){
const editor = tinymce.get(this.tinymceId)
return editor.getContent({'format' : 'text'})
}
},
watch: {
value(val) {
//用户vue绑定回显
if (this.isInit) {
this.isInit = false
this.$nextTick(() => {
const editor = tinymce.get(this.tinymceId)
editor.setContent(val)
})
}
},
},
}
</script>