<script src="https://****.com/tinymce/tinymce.min.js"></script>
<template>
<div style="display:flex;">
<div
:class="{ fullscreen: fullscreen }"
class="tinymce-container"
:style="{ width: containerWidth }"
>
<textarea :id="tinymceId" :class="tinymceId" class="tinymce-textarea" />
</div>
<!-- <componentMaterial
v-if="show"
@insertMaterial="insertMaterialAction"
style="margin-left:10%"
></componentMaterial> -->
</div>
</template>
<script>
/**
* docs:
* https://panjiachen.github.io/vue-element-admin-site/feature/component/rich-editor.html#tinymce
*/
import componentMaterial from "../ComponentMaterial";
import {
_api_carcloud,
} from "@/service/axios";
import plugins from "./plugins";
import toolbar from "./toolbar";
import load from "./dynamicLoadScript";
import { mp01, mp02, mp03 } from "./mp";
// why use this cdn, detail see https://github.com/PanJiaChen/tinymce-all-in-one
// const tinymceCDN ="https://unpkg.com/tinymce-all-in-one@4.9.3/tinymce.min.js";
// 导入 tinymce
//
// const tinymceCDN = 'https://.com/tinymce/tinymce.min.js'
export default {
name: "Tinymce",
components: { componentMaterial },
props: {
ifPreview: {
type: Boolean,
default: false,
},
tinymceId: {
type: String,
default: "tinymce",
},
value: {
type: String,
default: "",
},
toolbar: {
type: Array,
required: false,
default() {
return [];
},
},
menubar: {
type: String,
default: "file edit insert view table",
},
height: {
type: [Number, String],
required: false,
default: 800,
},
width: {
type: [Number, String],
required: false,
default: 414,
},
show: {
type: Boolean,
default: true
}
},
data() {
return {
uploadImgsUrl: "/api/v1.0/backend/file/upload/image/9",
hasChange: false,
hasInit: false,
//tinymceId: "vuetinymce",
fullscreen: false,
languageTypeList: {
en: "en",
zh: "zh_CN",
es: "es_MX",
ja: "ja",
},
};
},
computed: {
containerWidth() {
if (this.ifPreview) {
return "414px";
}
const width = this.width;
if (/^[\d]+(\.[\d]+)?$/.test(width)) {
// matches `100`, `'100'`
return `${width}px`;
}
return width;
},
},
watch: {
value: {
handler(val) {
console.log("需返显内容++", val)
this.$emit("change", val);
if (!this.hasChange && this.hasInit) {
console.log("需返显内容___", val)
window.tinymce?.get(this.tinymceId).setContent(val || "")
}
},
immediate: true,
deep: true,
},
},
mounted() {
console.log("富文本生命周期");
this.initData();
},
activated() {
if (window.tinymce) {
this.initTinymce();
}
},
deactivated() {
this.destroyTinymce();
},
destroyed() {
this.destroyTinymce();
},
methods: {
createFocusDom() {
const foucsDom = document.createElement("div");
return foucsDom;
},
createNode(htmlStr) {
//console.log(htmlStr)
let div = document.createElement("div");
div.innerHTML = htmlStr ;
if (div.childNodes.length > 1) {
div.innerHTML = `<div>` + htmlStr + `</div>`;
}
return div.childNodes[0];
},
insertMaterialAction(DOM) {
//在富文本编辑器里插入组件动作
tinymce.activeEditor.selection
.getNode()
.appendChild(this.createNode(DOM));
tinymce.activeEditor.selection
.getNode()
.appendChild(this.createFocusDom());
this.setContent(this.getContent());
//const nowFoucsDom = document.getElementById(this.tinymceId+'_ifr');
//console.log(document.getElementsByTagName('iframe')[0].contentWindow)
// nowFoucsDom.focus();
},
initData() {
this.initTinymce();
return false;
// dynamic load tinymce from cdn
load(tinymceCDN, (err) => {
console.log("tinymceCDN err", err)
if (err) {
this.$Message.error(err.message);
return;
}
// window.tinymce.baseURL = 'https://dbzj-imgs-1258960627.cos.ap-beijing.myqcloud.com/tinymce';
this.initTinymce();
//const ifr = window.document.getElementById(this.tinymceId + "_ifr");
// console.log(ifr.document);
// let ifrHead = ifr.document.getElementsByTagName("body")[0];
// ifrHead = ifr.getElementsByTagName("body")[0];
// console.log(ifrHead);
// const scrollStyle = `
// <style type="text/css">
// body {
// SCROLLBAR-FACE-COLOR: #e8e7e7;
// SCROLLBAR-HIGHLIGHT-COLOR: #ffffff;
// SCROLLBAR-SHADOW-COLOR: #ffffff;
// SCROLLBAR-3DLIGHT-COLOR: #cccccc;
// SCROLLBAR-ARROW-COLOR: #03B7EC;
// SCROLLBAR-TRACK-COLOR: #EFEFEF;
// SCROLLBAR-DARKSHADOW-COLOR: #b2b2b2;
// SCROLLBAR-BASE-COLOR: #000000;
// }
// </style>
// `;
// ifrHead.appendChild(scrollStyle);
});
},
initTinymce() {
const _this = this;
window.tinymce.init({
baseURL: 'https://dbzj-imgs-1258960627.cos.ap-beijing.myqcloud.com/tinymce',
selector: `.${this.tinymceId}`,
body_class: "my-tinyMce-body",
schema: "html5",
fontsize_formats: "12px 14px 16px 18px 24px 36px 48px 56px 72px",
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;",
branding: false,
elementpath: false,
language: this.languageTypeList["zh"],
language_url: require("./zh_CN.js"),
height: this.height,
templates: [
{
title: "模板01",
description: "模板01",
content: mp01,
},
{
title: "模板02",
description: "模板02",
content: mp02,
},
{
title: "模板03",
description: "模板03",
content: mp03,
},
],
body_class: "panel-body",
object_resizing: true, // 此选项允许您打开/关闭图像、表或媒体对象的大小调整句柄。默认情况下,此选项处于启用状态,允许您调整表和图像的大小。您还可以指定一个CSS3选择器来选择要在其上启用大小调整的对象。
toolbar: this.toolbar.length > 0 ? this.toolbar : toolbar,
menubar: this.menubar, // 菜单栏
plugins: plugins,
end_container_on_empty_block: true, // 如果在空的内部块元素中按enter键,则此选项允许您拆分当前容器块元素
powerpaste_word_import: "clean", // https://www.tiny.cloud/docs/plugins/powerpaste/#powerpaste_word_import 此设置控制如何筛选从Microsoft Word粘贴的内容。
// code_dialog_height: 450, // 此配置选项设置对话框的内部可编辑区域高度code。 请注意,实际模态的外部尺寸将略大于设置的值。
// code_dialog_width: 1000, // 此配置选项设置对话框的内部可编辑区域宽度code。 请注意,实际模态的外部尺寸将略大于设置的值。
advlist_bullet_styles: "square", // 此选项允许您在默认bullist工具栏控件中包含特定的无序列表项标记。此选项允许您在默认bullist工具栏控件中包含特定的无序列表项标记。https://www.tiny.cloud/docs-4x/plugins/advlist/#advlist_bullet_styles
advlist_number_styles: "default", // 此选项允许您在默认的numlist工具栏控件中包含特定的有序列表项标记。
/* https://www.tiny.cloud/docs-4x/plugins/imagetools/#imagetools_cors_hosts
由于浏览器对所谓的跨域HTTP请求施加了安全性措施,因此Image Tools无法使用来自其他域的图像。为了克服这些限制,必须在指定的域上显式启用跨域资源共享(CORS)(有关更多信息,请参阅HTTP访问控制)。
可以通过imagetools_cors_hosts选件将一系列受支持的图像域(启用了CORS)提供给TinyMCE 。
注意:数组中的每个字符串都必须采用的格式mydomain.com。http://, https://域中请勿包含协议()或任何斜杠。
注: imagetools_cors_hosts在不使通过这个插件时,需要TinyMCE的云。
类型: String[]
* */
imagetools_cors_hosts: ["https://image.baidu.com/", "codepen.io"], //
// 自定义上传逻辑
images_upload_handler: (blobInfo, success, failure) => {
// const img = 'data:image/jpeg;base64,' + blobInfo.base64()
// success(img)
const file = blobInfo.blob(); // 转化为易于理解的file对象
const formData = new FormData();
formData.append("file", file);
new Promise((resolve, reject) => {
_api_carcloud
.post(this.uploadImgsUrl, formData)
.then((res) => {
const result = res.data;
if (result.code === "000000") {
const data = result.data;
resolve(data.filePathUrl);
} else {
reject(res.data.description);
}
});
})
.then((data) => {
success(data);
})
.catch((data) => {
failure(data);
});
},
// default_link_target: "_blank", // 使用此选项,您可以在插入/编辑链接时为链接设置默认目标
link_title: false, // 此选项使您可以禁用对话框中的链接title输入字段link。
/*
* 使用此选项,您可以 在用户按下键盘tab键时强制TinyMCE插入三个实体。
请务必注意,这不会更改菜单和工具栏控件的行为, 当nonbreaking_force_tabvalue 为时,菜单和工具栏控件将继续插入单个实体true。
但是,true条件确实捕获了Tab键并将其包含在可编辑区域中,而当设置为falseTab键的默认状态时,按下该键会将光标移动到下一个可编辑区域(例如,当前页面上的浏览器网址栏或表单字段) )。*/
nonbreaking_force_tab: true, // inserting nonbreaking space need Nonbreaking Space Plugin 插入非中断空间需要非中断空间插件
/* 使用init_instance_callback选项,您可以指定每次初始化编辑器实例时要执行的函数名称。该函数的格式为initInstance(editor)哪里editor是编辑器实例对象引用。
* */
init_instance_callback: (editor) => {
if (_this.value) {
editor.setContent(_this.value);
}
_this.hasInit = true;
console.log("初始化完成", _this.value)
setTimeout(()=>{
window.tinymce?.get(this.tinymceId).setContent(_this.value || "")
}, 800)
editor.on("NodeChange Change KeyUp SetContent", () => {
this.hasChange = true;
this.$emit("input", editor.getContent());
});
},
/*
* 此选项使您可以指定在渲染TinyMCE编辑器实例之前将执行的回调。
要指定设置回调,请为该setup选项提供JavaScript函数。该函数应该有一个参数,该参数是对正在设置的编辑器的引用。
此设置的常见用例是将编辑器事件新增到TinyMCE。例如,如果您想向TinyMCE新增点击事件,则可以通过设置配置设置来新增它。
* */
setup(editor) {
editor.on("FullscreenStateChanged", (e) => {
console.log(e);
_this.fullscreen = e.state;
});
},
});
},
destroyTinymce() {
const tinymce = window.tinymce?.get(this.tinymceId);
console.log("tinymce", tinymce)
if (this.fullscreen) {
tinymce.execCommand("mceFullScreen");
}
if (tinymce) {
tinymce.destroy();
tinymce.remove()
tinymce.execCommand('mceRemoveControl',true, "#"+this.tinymceId);
}
},
setContent(value) {
window.tinymce?.get(this.tinymceId).setContent(value);
},
getContent() {
return window.tinymce?.get(this.tinymceId).getContent();
},
imageSuccessCBK(arr) {
const _this = this;
arr.forEach((v) => {
window.tinymce?.get(_this.tinymceId)
.insertContent(`<img class="wscnph" src="${v.url}" >`);
});
},
},
};
</script>
<style lang="less" scoped>
.img-class {
width: 100% !important;
}
.tinymce-container {
position: relative;
line-height: normal;
}
.tinymce-container .mce-fullscreen {
z-index: 10000;
}
#my-tinyMce-body {
::-webkit-scrollbar {
height: 10px;
width: 10px;
background: transparent;
border-radius: 10px;
}
::-webkit-scrollbar-button {
display: none;
}
::-webkit-scrollbar-track {
background-color: transparent;
}
::-webkit-scrollbar-track-piece {
background: transparent;
}
::-webkit-scrollbar-thumb {
width: 8px;
min-height: 15px;
background: #c1c1c1;
border-radius: 4px;
}
::-webkit-scrollbar-thumb:active,
::-webkit-scrollbar-thumb:hover {
background: #7d7d7d;
}
}
.tinymce-textarea {
visibility: hidden;
z-index: -1;
}
.editor-custom-btn-container {
position: absolute;
right: 4px;
top: 4px;
/*z-index: 2005;*/
}
.fullscreen .editor-custom-btn-container {
z-index: 10000;
position: fixed;
}
.editor-upload-btn {
display: inline-block;
}
</style>
富文本 tinymce.js(version: 5.4.0)的使用实践
于 2022-08-30 15:52:34 首次发布