Tinymce的使用-vue3
·
安装:npm install --save "@tinymce/tinymce-vue@^5"
文档:https://www.tiny.cloud/docs/tinymce/latest/vue-cloud/
下载语言包放在静态资源里进行引入:
import "@/assets/tinymce/langs/zh_CN.js"; //下载后的语言包
封装组件:
<template>
<div>
<Editor
v-model="myValue"
class="custom-edit"
:init="init"
:disabled="disabled"
:placeholder="placeholder"
:id="tinymceId"
:noEidtclass="noEidtclass"
></Editor>
</div>
</template>
基础配置:
<script setup lang="ts">
import { reactive, ref, onMounted, watch } from "vue";
import { apiImgaUpload } from "@/api/emails";
import { ElMessage, ElMessageBox } from "element-plus";
import type {ApiResponse } from "@/hooks/type";
import { AxiosResponse } from "axios";
import tinymce from "tinymce/tinymce";
import Editor from "@tinymce/tinymce-vue";
import "tinymce/themes/silver";
import "tinymce/themes/silver/theme";
import "tinymce/models/dom";
import "tinymce/icons/default";
import "tinymce/icons/default/icons";
// 引入编辑器插件
import "tinymce/plugins/code"; //编辑源码
import "@/assets/tinymce/plugins/image"; //插入编辑图片、
// import "@/assets/tinymce/plugins/codeSelect"; //编辑源代码选项
import "tinymce/plugins/media"; //插入视频
import "tinymce/plugins/link"; //超链接
import "tinymce/plugins/preview"; //预览
// import "tinymce/plugins/template"; //模板
import "tinymce/plugins/table"; //表格
import "tinymce/plugins/pagebreak"; //分页
import "tinymce/plugins/emoticons"; // emoji表情
import 'tinymce/plugins/emoticons/js/emojis';
import "tinymce/plugins/lists"; //列
import "tinymce/plugins/advlist"; //列
import "tinymce/plugins/fullscreen";
import "tinymce/plugins/autoresize"; // 自动调整编辑器大小
import "tinymce/plugins/quickbars"; //快速工具条
import "tinymce/plugins/wordcount"; // 字数统计插件
import "@/assets/tinymce/langs/zh_CN.js"; //下载后的语言包
import "@/assets/tinymce/skins/content/default/content.css";
const emits = defineEmits(["update:value"]);
const props = defineProps({
value: {
type: String,
default: "",
},
placeholder: {
type: String,
default: "",
},
minHeight:{
type: Number,
default: 500,
},
height: {
type: Number,
default: 500,
},
disabled: {
type: Boolean,
default: false,
},
isDelectContent:{
type:Boolean,
default:false
},
plugins: {
type: [String, Array],
default:
"code image media link preview table quickbars pagebreak lists advlist emoticons fullscreen quickbars wordcount",
},
toolbar: {
type: [String, Array],
default:
"undo redo | blocks fontfamily fontsize | headings formatselect alignleft aligncenter alignright alignjustify | link unlink | image | forecolor backcolor | bold italic underline strikethrough | indent outdent | numlist bullist | superscript subscript | hr emoticons fullscreen | customButton "
// "undo redo codesample bold italic underline strikethrough link alignleft aligncenter alignright alignjustify \
// bullist numlist outdent indent removeformat forecolor backcolor |formatselect fontselect fontsizeselect | \
// blocks fontfamily fontsize pagebreak lists image media table template preview | code selectall",
},
editRoot:{
type:Boolean,
default:true
},//根节点是否可编辑
placeholderCustom:{
type:String,
default:''
},
noEidtclass:{
type:String,
default:''
},
templates: {
type: Array,
default: () => [],
},
options: {
type: Object,
default: () => {},
},
contentCss:{
type:String,
default:''
},
selectTemplateList: {
type: Array,
default: () => [],
},
isExpandStatus:{
type:Boolean,
default:false,//高度变化的状态
}
});
//用于接收外部传递进来的富文本
const myValue = ref(props.value);
const tinymceId = ref(
"vue-tinymce-" + +new Date() + ((Math.random() * 1000).toFixed(0) + "")
);
const codeContentType = ref(null)
const init = reactive({
selector: "#" + tinymceId.value, //富文本编辑器的id,
language_url: "/tinymce/langs/zh_CN.js", // 语言包的路径,具体路径看自己的项目,文档后面附上中文js文件
language: "zh_CN", //语言
skin_url: "/manage/tinymce/skins/ui/oxide", // skin路径,具体路径看自己的项目 /manage
content_css: "/manage/tinymce/skins/content/default/content.css",
icons_url: '/manage/tinymce/icon/customIcon.js', // 指向你的自定义图标JS文件
icons: 'customIcons', // 使用自定义图标集
content_style: 'div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, abbr, address, cite, code, del, dfn, em, img, ins, kbd, q, samp, small, strong, sub, sup, var, b, i, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, figcaption, figure, footer, header, hgroup, menu, nav, section, summary, time, mark, audio, video {margin: 0;padding: 0;border: 0;}ul,ol{padding-left: 20px;}body{max-width:600px;box-sizing: border-box;} .tox .tox-form__group{margin-bottom:14px}',//img{max-width: 100%;height:auto}
menubar: false, //顶部菜单栏显示
statusbar: true, // 底部的状态栏
plugins: props.plugins,
toolbar: props.toolbar,
toolbar_mode: 'wrap',//"sliding" 工具栏的展示形态
font_formats:
"Arial=arial,helvetica,sans-serif; 宋体=SimSun; 微软雅黑=Microsoft Yahei; Impact=impact,chicago;", //字体
font_size_formats: "12px 14px 16px 18px 22px 24px 36px 72px", //文字大小
height: window.innerHeight - 290, //编辑器高度
autoresize_min_height: window.innerHeight - 290,
placeholder: props.placeholderCustom,
branding: false, //是否禁用“Powered by TinyMCE”
promotion: false,//禁用升级提醒
// image_dimensions: false, //去除宽高属性
paste_webkit_styles: "all",
paste_merge_formats: true,
nonbreaking_force_tab: false,
paste_auto_cleanup_on_paste: false,
file_picker_types: "file",
resize: true,
elementpath: false,
quickbars_selection_toolbar:
"forecolor backcolor bold italic underline strikethrough link",
quickbars_image_toolbar: "alignleft aligncenter alignright | rotateleft rotateright | imageoptions",
editimage_toolbar:
"rotateleft rotateright | flipv fliph | editimage imageoptions",
quickbars_insert_toolbar: false,
// image_class_list: [ //图像的预定义类列表
// { title: 'None', value: '' },
// { title: 'No border', value: 'img_no_border' },
// { title: 'Green border', value: 'img_green_border' },
// { title: 'Blue border', value: 'img_blue_border' },
// { title: 'Red border', value: 'img_red_border' }
// ],
object_resizing:true,
autoresize_bottom_margin: 20,
// autoresize_overflow_padding: 16,
min_height: props.minHeight,
image_caption: false,
image_description:false,//去掉图片描述
image_advtab: false,//图片高级选项
convert_urls: false,
a_plugin_option:true,
invalid_elements: 'iframe',
// editable_root: false,
// editable_class: 'mceEditable',//可编辑区域的类名
noneditable_class: props.noEidtclass,//不可编辑区域的类名
editable_root: props.editRoot,
editable_class: 'editable',
images_upload_handler: function (blobInfo,success,failure, progress) {
return new Promise((resolve, reject) => {
// 限制图片大小为 20MB
const maxSize = 20 * 1024 * 1024;
if (blobInfo.blob().size > maxSize) {
// ElMessage.success('Image size exceeds the maximum allowed size of 5MB');
reject("图像大小不能超过20MB");
return false;
}
const data = new FormData();
data.append("file", blobInfo.blob(), blobInfo.filename());
apiImgaUpload(data)
.then((res: AxiosResponse & ApiResponse) => {
// resolve(res?.data);
if (res?.code == '00') {
ElMessage.success('上传成功')
resolve(res?.data);
} else {
// ElMessage.error('上传失败!'+res?.msg)
reject('上传失败!'+res?.msg);
}
})
.catch(() => {
reject("图片上传失败!");
});
});
},
setup: function (editor) {
editor.on("init", function () {
this.getBody().style.fontSize = "14px";
});
},
...props.options,
});
const editorContent = ref('')
//监听外部传递进来的的数据变化
watch(
() => props.value,
() => {
myValue.value = props.value;
emits("update:value", myValue.value);
}
);
watch(
() => props.isExpandStatus,
(val) => {
var element = document.querySelector('.tox-tinymce');
if(val){
element.style.height = window.innerHeight - 191 +'px'
}else{
element.style.height = window.innerHeight - 283 +'px'
}
}
);
//监听富文本中的数据变化
watch(
() => myValue.value,
() => {
emits("update:value", myValue.value);
}
);
watch(
() => editorContent.value,
() => {
console.log('editorContent.value',editorContent.value);
emits("update:value", editorContent.value);
}
)
//在onMounted中初始化编辑器
onMounted(() => {
tinymce.init({
});
});
</script>
自定义插件(例如添加一个按钮):
//添加按钮的弹窗
const dialogButtonConfig = {
title: '插入button',
body: {
type: 'panel',
items: [
{
type: 'input',
name: 'btnText',
label: '文本内容'
},
{
type: 'input',
name: 'btnLink',
label: '链接地址'
},
{
type: 'colorinput', // component type
name: 'colorBg', // identifier
label: '背景颜色' // text for the label
},
{
type: 'colorinput', // component type
name: 'colorText', // identifier
label: '文字颜色' // text for the label
},
]
},
buttons: [
{
type: 'cancel',
name: 'closeButton',
text: '取消'
},
{
type: 'submit',
name: 'submitButton',
text: '插入',
buttonType: 'primary'
}
],
//设置默认值
initialData: {
colorBg: '#67C23A',
colorText: '#ffffff',
},
onSubmit: (api) => {
const data = api.getData();
// const pet = data.isdog ? 'dog' : 'cat';
if (data.btnLink.indexOf("http") !== 0){
ElMessage.warning("链接必须以 http/https 开头")
return false;
}
tinymce.activeEditor.execCommand('mceInsertContent', false, `<a style="border-radius:4px;background-color:${data.colorBg};color:${data.colorText};cursor: pointer;text-decoration: none;display: inline-flex;align-items: center;justify-content: center;padding: 10px 20px;line-height: 1.4;
font-size: 14px;" href="${data.btnLink}" target="_blank">${data.btnText}</a>`);
tinymce.activeEditor.execCommand('mceInsertContent', false, `<span> </span>`)
api.close();
}
};
在tinymce的setup里注册一个工具栏按钮:
setup: function (editor) {
editor.ui.registry.addButton('customButton', {
tooltip: '插入button',
// icon: '', //图标
text:'插入button',
onAction : () =>{
editor.windowManager.open(dialogButtonConfig)
}
});
}
将 customButton 配置到 toolbar 就会在工具栏看见自定义的组件
如果需要下拉菜单样式的组件中,则按照以下方法:
editor.ui.registry.addMenuButton('inserPlaceholder', {
text: '下拉菜单',
fetch: (callback) => {
const items = [
{
type: 'menuitem',
text: '菜单1',
onAction: () => editor.insertContent('<span></span> ')
},
{
type: 'menuitem',
text: '菜单2',
onAction: () => editor.insertContent('<span></span> ')
},
];
callback(items)
}
})
使用组件:
<TinymceEditor :toolbar="toolbar" :value="tinyContent" @update:value="getContentInput" :placeholderCustom="'请输入正文内容'"/>
import TinymceEditor from '@/components/TinymceEditor.vue'
let tinyContent = ref('')
const getContentInput = (val) =>{
tinyContent.value = val
}
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)