插件安装及版本说明
Node.js v18.19.0
"@tinymce/tinymce-vue": "^5.1.1"
"tinymce": "^6.8.3"
npm i @tinymce/tinymce-vue@5.1.1 tinymce@6.8.3
components
Editor.vue
<script setup>
import {onMounted} from "vue";
import tinymce from 'tinymce/tinymce'; //tinymce核心文件
import 'tinymce/models/dom'; // 引入dom模块。从 Tinymce6,开始必须有此模块导入
import 'tinymce/themes/silver'; //默认主题
import 'tinymce/icons/default'; //引入编辑器图标icon,不引入则不显示对应图标
// import 'tinymce/skins/ui/oxide/skin.css' // 我这里插件引入显示异常,改为本地引用
// 以下编辑器插件功能需要的自行引入,node_modules/tinymce/plugins下存在的插件都可以引用
import 'tinymce/plugins/advlist'; //高级列表
import 'tinymce/plugins/anchor'; //锚点
import 'tinymce/plugins/autolink'; //自动链接
import 'tinymce/plugins/autoresize'; //编辑器高度自适应,注:plugins里引入此插件时,Init里设置的height将失效
import 'tinymce/plugins/autosave'; //自动存稿
import 'tinymce/plugins/charmap'; //特殊字符
import 'tinymce/plugins/code'; //编辑源码
import 'tinymce/plugins/codesample'; //代码示例
import 'tinymce/plugins/directionality'; //文字方向
import 'tinymce/plugins/emoticons'; //表情
import 'tinymce/plugins/fullscreen'; //全屏
import 'tinymce/plugins/help'; //帮助
import 'tinymce/plugins/image'; //插入编辑图片
import 'tinymce/plugins/importcss'; //引入css
import 'tinymce/plugins/insertdatetime'; //插入日期时间
import 'tinymce/plugins/link'; //超链接
import 'tinymce/plugins/lists'; //列表插件
import 'tinymce/plugins/nonbreaking'; //插入不间断空格
import 'tinymce/plugins/pagebreak'; //插入分页符
import 'tinymce/plugins/preview'; //预览
import 'tinymce/plugins/quickbars'; //快速工具栏
import 'tinymce/plugins/save'; //保存
import 'tinymce/plugins/searchreplace'; //查找替换
import 'tinymce/plugins/table'; //表格
import 'tinymce/plugins/visualblocks'; //显示元素范围
import 'tinymce/plugins/visualchars'; //显示不可见字符
import 'tinymce/plugins/wordcount'; //字数统计
const emits = defineEmits(['changeContent'])
const props = defineProps({
text: String,
})
const id = 'vue-tinymce-' + +new Date() // 这里是防止页面路由跳转tinymce会有空白异常显示
onMounted(() => {
console.log("onMounted");
tinymce.init({
selector: `#${id}`,
forced_root_block: '',
// 路径引用自定义,只要能访问到资源即可
language_url: `${import.meta.env.VITE_PUBLIC_PATH}langs/zh_CN.js`, // 本地引用langs语言文件,下面有下载地址
language: 'zh_CN',
draggable_modal: true,
promotion:false, //Upgrade是否开启
branding: false, //tiny技术支持信息是否显示
// statusbar: false, // 是否显示输入框下面的工具栏
// 路径引用自定义,只要能访问到资源即可
skin_url: `${import.meta.env.VITE_PUBLIC_PATH}skins/ui/oxide`, // 复制node_modules/tinymce/skins放在本地
content_style: "p {margin: 0px; border:0px ; padding: 0px;}", // 设置行间距
toolbar: 'undo redo forecolor | styles elect | bold italic | link image | alignleft aligncenter alignright | preview',
plugins: 'image code preview',
images_upload_handler: (blobInfo, progress) => new Promise((resolve, reject) => {
const formData = new FormData();
const file = blobInfo.blob()
if (file.size > 2 * 1024 * 1024) {
// 限制图片大小
reject('请上传小于2M的图片!');
return
}
const xhr = new XMLHttpRequest();
xhr.withCredentials = false;
xhr.open('POST', 'https://xxx/file/uploadLocalFile');
xhr.upload.onprogress = (e) => {
progress(e.loaded / e.total * 100);
};
xhr.onload = () => {
if (xhr.status === 403) {
reject({ message: 'HTTP Error: ' + xhr.status, remove: true });
return;
}
if (xhr.status < 200 || xhr.status >= 300) {
reject('HTTP Error: ' + xhr.status);
return;
}
const json = JSON.parse(xhr.responseText);
if (!json || typeof json.data != 'string') {
reject('Invalid JSON: ' + xhr.responseText);
return;
}
resolve(json.data);
};
xhr.onerror = () => {
reject('Image upload failed due to a XHR Transport error. Code: ' + xhr.status);
};
formData.append('file', file, blobInfo.filename());
xhr.send(formData);
}),
images_reuse_filename: true, // 使用图片文件实际的文件名
// style_formats: [
// {title: 'Bold text', inline: 'b'},
// {title: 'Red text', inline: 'span', styles: {color: '#ff0000'}},
// {title: 'Red header', block: 'h1', styles: {color: '#ff0000'}},
// {title: 'Example 1', inline: 'span', classes: 'example1'},
// {title: 'Example 2', inline: 'span', classes: 'example2'},
// {title: '首行缩进', block: 'p', styles: {'text-indent': '2em'}},
// {title: 'Table styles'},
// {title: 'Table row 1', selector: 'tr', classes: 'tablerow1'}
// ],
// style_formats_merge: true,
// style_formats_autohide: true,
setup(editor) {
editor.on('init', function () {
editor.setContent(props.text); // 数据回填显示,一定要在这个函数里,要不然会有数据空白显示
});
editor.on('change', function () {
emits('changeContent', { text: editor.getContent() }) // 数据更改传递出去给父组件
});
},
})
})
</script>
<template>
<textarea :id="id" :key="id"></textarea>
</template>
语言文件下载
language-packages语言文件选择
zh_CN中文版本直接点击下载
使用组件
import Editor from '/@/components/Editor.vue'
import {ref} from 'vue'
const editorVal = ref('')
// ⚠️注意是否造成editorVal循环引用
<Editor :text="editorVal" @change-content="changeContent" />
function changeContent({text}) {
console.log(text)
editorVal.value = text
}