直接上代码 然后根据自己需求来调整代码内的toolbar功能设置
另外添加两个没有plugin的插件 首行缩进 以及调整段落大小 (目前没有太细看文档 不入门也不太好看懂~) 我这是从版本4升级到6的
已解决弹窗上使用问题
后面会出个相关配置文档的
代码
<template>
<div class="tinymce-boxz">
<Editor v-model="content" :api-key="apiKey" :init="init" />
</div>
</template>
<script>
import Editor from '@tinymce/tinymce-vue';
import { reactive, ref, toRefs, watchEffect } from 'vue';
import axios from 'axios';
export default {
name: 'Tinymce',
components: {
Editor,
},
props: {
//默认值
modelValue: {
type: String,
default: '',
},
},
setup(props, context) {
const content = ref(props.modelValue);
// 监听 props.modelValue,确保父组件数据变化时同步到本地
watch(
() => props.modelValue,
(newValue) => {
content.value = newValue;
}
);
// 监听本地 content 值的变化,向父组件发出更新事件
watchEffect(() => {
context.emit('update:modelValue', content.value);
});
const uploadUrl = ref(
import.meta.env.VITE_APP_BASE_API + '/common/uploadOneFileToQiniu'
);
const tiny = reactive({
apiKey: '9i8khq1jzwwj7hda35fpc0q1e14zsb4ev23vaif34nqif0fw', //https://github.com/tinymce/tinymce-vue/blob/main/src/demo/views/Iframe.vue
init: {
language: 'zh_CN', //语言类型
placeholder: '在这里输入文字', //textarea中的提示信息
min_width: 320,
min_height: 220,
height: 500, //注:引入autoresize插件时,此属性失效
resize: 'both', //编辑器宽高是否可变,false-否,true-高可变,'both'-宽高均可,注意引号
branding: false, //tiny技术支持信息是否显示
// statusbar: false, //最下方的元素路径和字数统计那一栏是否显示
// elementpath: false, //元素路径是否显示
font_size_formats: '12px 14px 16px 18px 20px 22px 24px 28px 32px',
font_formats:
'微软雅黑=Microsoft YaHei,Helvetica Neue,PingFang SC,sans-serif;苹果苹方=PingFang SC,Microsoft YaHei,sans-serif;宋体=simsun,serif;仿宋体=FangSong,serif;黑体=SimHei,sans-serif;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;', //字体样式
plugins:
' preview searchreplace autolink directionality visualblocks visualchars fullscreen image link media template code codesample table charmap hr pagebreak nonbreaking anchor insertdatetime advlist lists wordcount textpattern autosave emoticons paragraphspacing', //插件配置 axupimgs indent2em
toolbar: [
'fullscreen undo redo restoredraft | cut copy paste pastetext | forecolor backcolor bold italic underline strikethrough link anchor | alignleft aligncenter alignright alignjustify outdent indent | bullist numlist | blockquote subscript superscript removeformat ',
'styleselect formatselect fontselect fontsizeselect | table image axupimgs media emoticons charmap hr pagebreak insertdatetime selectall visualblocks searchreplace | code print preview | lineheight formatpainter|fontsize | indent2em | paragraphspacing ',
], //工具栏配置,设为false则隐藏
external_plugins: {
indent2em: '/plugins/tinymce/indent2em.js', // 本地插件路径(相对路径)
},
indent2em_val: '2em', // 插件的默认缩进值
setup: function (editor) {
// 段落间距插件
editor.ui.registry.addMenuButton('paragraphspacing', {
text: '段落间距',
tooltip: '设置段落间距',
fetch: function (callback) {
const paragraphspacing = [
'4px',
'6px',
'8px',
'10px',
'12px',
'14px',
'16px',
'18px',
'22px',
'24px',
'26px',
'28px',
'30px',
'32px',
];
let selectedItem = '0px';
const items = paragraphspacing.map(function (item) {
return {
type: 'togglemenuitem',
text: item,
onAction: function () {
selectedItem = item;
editor.undoManager.transact(function () {
editor.focus();
editor.formatter.register({
paragraphspacing: {
block: 'p',
styles: { margin: `${item} 0` },
},
});
editor.formatter.apply('paragraphspacing', {
value: item,
});
});
},
onSetup: function (api) {
api.setActive(selectedItem === item);
},
};
});
callback(items);
},
});
},
// menubar: "file edit my1", //菜单栏配置,设为false则隐藏,不配置则默认显示全部菜单,也可自定义配置--查看 http://tinymce.ax-z.cn/configure/editor-appearance.php --搜索“自定义菜单”
// images_upload_url: '/apib/api-upload/uploadimg', //后端处理程序的url,建议直接自定义上传函数image_upload_handler,这个就可以不用了
// images_upload_base_path: '/demo', //相对基本路径--关于图片上传建议查看--http://tinymce.ax-z.cn/general/upload-images.php
paste_data_images: true, //图片是否可粘贴
//此处为图片上传处理函数
images_upload_handler: (blobInfo, success, failure) => {
// 这里用base64的图片形式上传图片,
let reader = new FileReader(); //本地预览
reader.readAsDataURL(blobInfo.blob());
reader.onloadend = function () {
const imgbase64 = reader.result;
success(imgbase64);
console.log(imgbase64, '图片地址');
};
},
file_picker_types: 'file image media', //file image media分别对应三个类型文件的上传:link插件,image和axupimgs插件,media插件。想屏蔽某个插件的上传就去掉对应的参数
// 文件上传处理函数
file_picker_callback: function (callback, value, meta) {
// meta.filetype //根据这个判断点击的是什么file image media
let filetype; //限制文件的上传类型,需要什么就添加什么的后缀
if (meta.filetype == 'image') {
filetype = '.jpg, .jpeg, .png, .gif, .ico, .svg';
} else if (meta.filetype == 'media') {
filetype = '.mp3, .mp4, .avi, .mov';
} else {
filetype =
'.pdf, .txt, .zip, .rar, .7z, .doc, .docx, .xls, .xlsx, .ppt, .pptx, .mp3, .mp4, .jpg, .jpeg, .png, .gif, .ico, .svg';
}
console.log(filetype, '类型', value, 'value');
let inputElem = document.createElement('input'); //创建文件选择
inputElem.setAttribute('type', 'file');
inputElem.setAttribute('accept', filetype);
inputElem.click();
inputElem.onchange = () => {
let file = inputElem.files[0]; // 获取文件对象
if (!file) return; // 如果未选择文件,直接返回
// 使用 FormData 上传文件
let formData = new FormData();
formData.append('file', file); // 假设服务端要求的字段名是 'file'
// 上传到服务器
axios
.post(uploadUrl.value, formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
})
.then((res) => {
console.log(res, '上传成功返回的文件 URL');
// 成功后回调,将文件 URL 插入编辑器
callback(res.data.url, { title: res.data.fileName });
});
};
},
},
});
//内容有变化,就更新内容,将值返回给父组件
watchEffect(() => {
context.emit('update:value', content.value);
});
return {
content,
...toRefs(tiny),
};
},
};
</script>
<style scoped>
.tinymce-boxz > textarea {
display: none;
}
</style>
<style>
/* 隐藏apikey没有绑定这个域名的提示 */
.tox-notifications-container .tox-notification--warning {
display: none !important;
}
.tox.tox-tinymce {
max-width: 100%;
}
/* 在el-dialog中tinymce z-index 被太小而被遮挡时要加这两句 */
.tox-tinymce-aux {
z-index: 99999 !important;
}
.tinymce.ui.FloatPanel {
z-index: 99;
}
</style>
插件文件indent2em
tinymce.PluginManager.add('indent2em', function(editor, url) {
var pluginName='首行缩进';
var global$1 = tinymce.util.Tools.resolve('tinymce.util.Tools');
var indent2em_val = editor.getParam('indent2em_val', '2em');
var doAct = function () {
var dom = editor.dom;
var blocks = editor.selection.getSelectedBlocks();
var act = '';
global$1.each(blocks, function (block) {
if(act==''){
act = dom.getStyle(block,'text-indent')==indent2em_val ? 'remove' : 'add';
}
if( act=='add' ){
dom.setStyle(block, 'text-indent', indent2em_val);
}else{
var style=dom.getAttrib(block,'style');
var reg = new RegExp('text-indent:[\\s]*' + indent2em_val + ';', 'ig');
style = style.replace(reg, '');
dom.setAttrib(block,'style',style);
}
});
};
editor.ui.registry.getAll().icons.indent2em || editor.ui.registry.addIcon('indent2em','<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="24" height="24"><path d="M170.666667 563.2v-102.4H887.466667v102.4zM170.666667 836.266667v-102.4H887.466667v102.4zM512 290.133333v-102.4H887.466667v102.4zM238.933333 341.333333V136.533333l204.8 102.4z" fill="#2c2c2c" p-id="5210"></path></svg>');
var stateSelectorAdapter = function (editor, selector) {
return function (buttonApi) {
return editor.selection.selectorChangedWithUnbind(selector.join(','), buttonApi.setActive).unbind;
};
};
editor.ui.registry.addToggleButton('indent2em', {
icon: 'indent2em',
tooltip: pluginName,
onAction: function () {
doAct();
},
onSetup: stateSelectorAdapter(editor, [
'*[style*="text-indent"]',
'*[data-mce-style*="text-indent"]',
])
});
editor.ui.registry.addMenuItem('indent2em', {
text: pluginName,
onAction: function() {
doAct();
}
});
editor.addCommand('indent2em', doAct );
return {
getMetadata: function () {
return {
name: pluginName,
url: "http://tinymce.ax-z.cn/more-plugins/indent2em.php",
};
}
};
});