TinyMCE中文文档
什么是富文本编辑器
在了解TinyMCE富文本编辑器之前,我们先来了解一下什么是富文本编辑器吧。
富文本编辑器是一种用于创建和编辑富文本内容的工具或应用程序。富文本内容是指包含文本、图像、链接、样式和其他格式化元素的内容,允许用户以更具交互性和美观性的方式编辑文档或文本。
人话就是,富文本编辑器允许用户编写文章时,给文字以各种样式,也能进行各种格式化操作和图片链接的插入。可以让一篇文章更加的美观。
vue项目中使用TinyMCE富文本编辑器
1,安装并引入
下载
npm install tinymce @tinymce/tinymce-vue -S
引入
把node_modules/tinymec下的skins文件夹复制到public文件夹下
最后下载中文语言包,把里面的zh_CN.js也放进
中文语言包下载地址
2,封装富文本组件
<template>
<editor v-html="modelValue" v-model="content" tag-name="div" :init="init" placeholder="请输入正文..." />
</template>
<script >
import { upload } from "@/api/ArticleApis.js";
import tinymce from "tinymce/tinymce";
import Editor from "@tinymce/tinymce-vue";
//根据需要引入
import "tinymce/themes/silver/theme"; // 引用主题文件
import "tinymce/icons/default"; // 引用图标文件
import "tinymce/plugins/link";
import "tinymce/plugins/code";
import "tinymce/plugins/table"; //插入表格
import "tinymce/plugins/autoresize";
import "tinymce/plugins/charmap"; //特殊字符
import "tinymce/plugins/code"; //查看源码
import "tinymce/plugins/codesample"; //插入代码1
import "tinymce/plugins/directionality"; //
import "tinymce/plugins/fullscreen"; //全屏
import "tinymce/plugins/image"; //图片
import "tinymce/plugins/imagetools"; //图片工具
import "tinymce/plugins/importcss"; //图片工具
import "tinymce/plugins/media"; //媒体插入
import "tinymce/plugins/preview"; //预览
import "tinymce/plugins/quickbars"; //快捷菜单
import "tinymce/plugins/searchreplace"; //查询替换
import "tinymce/plugins/tabfocus"; //
import "tinymce/plugins/textpattern"; //
import "tinymce/plugins/emoticons"; //
// import "tinymce/plugins/numlist";
import { onMounted, ref, watch } from "vue";
export default {
props: ["modelValue"],
components: {
editor: Editor,
},
emits: { "update:modelValue": null },
// props: {
// modelValue: String,
// },
setup(props, context) {
const init = {
language_url: "/tinymce/langs/zh_CN.js", // 中文语言包路径
language: "zh_CN",
skin_url: "/tinymce/skins/ui/oxide", // skin路径
content_css: "/tinymce/skins/ui/oxide/content.css", //以css文件方式自定义可编辑区域的css样式,css文件需自己创建并引入
menubar: false, // 隐藏菜单栏
autoresize_bottom_margin: 50,
// max_height: 500,
// min_height: 350,
skin: 'oxide-dark',
// height: 320,
toolbar_mode: "none",
plugins:
"textpattern tabfocus searchreplace quickbars preview media importcss imagetools image fullscreen emoticons directionality codesample code charmap link code table autoresize link", // 插件需要import进来
toolbar:
"formats fontsizeselect fontselect media image aligncenter alignleft alignright alignjustify underline quicklink h2 h3 blockquote numlist bullist table forecolor backcolor bold italic strikethrough charmap link codesample code ",//工具栏展示的
content_style: "p {margin: 5px 0; font-size: 14px} body { color: #fff; } iframe { border: 1px solid #fff; }",
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,
resize: false, // 禁止改变大小
statusbar: false, // 隐藏底部状态栏
// paste_data_images: true, // 允许粘贴图像
content_style: '#515767', // 设置默认文字颜色
file_picker_types: "file image media",
//文件上传
file_picker_callback: function (callback, value, meta) {
//文件分类
var filetype =
".pdf, .txt, .zip, .rar, .7z, .doc, .docx, .xls, .xlsx, .ppt, .pptx, .mp3, .mp4";
//为不同插件指定文件类型
switch (meta.filetype) {
case "image":
filetype = ".jpg, .jpeg, .png, .gif";
break;
case "media":
filetype = ".mp3, .mp4";
break;
case "file":
default:
}
//模拟出一个input用于添加本地文件
var input = document.createElement("input");
input.setAttribute("type", "file");
input.setAttribute("accept", filetype);
input.click();
input.onchange = function (v) {
console.log('v', v)
var file = v.target.files[0];
var formData = new FormData();
formData.append("image", file, file.name);
upload(formData).then((res) => {
callback(res.data.data, { title: file.name });
console.log(res.data.data);
});
};
},
// 图片上传
images_upload_handler: async (blobInfo, success) => {
//改写,通过自己的upload接口,返回的图片时正常的url格式
const blob = blobInfo.blob();
const filename = blobInfo.filename();
var form = new FormData();
form.append("image", blob, filename);
console.log('form', form);
upload(form).then((res) => {
success(res.data.data);
console.log(res.data.data);
});
},
codesample_languages: [
{ text: 'HTML/XML', value: 'markup' },
{ text: 'JavaScript', value: 'javascript' },
{ text: 'CSS', value: 'css' },
{ text: 'PHP', value: 'php' },
{ text: 'Ruby', value: 'ruby' },
{ text: 'Python', value: 'python' },
{ text: 'Java', value: 'java' },
{ text: 'C', value: 'c' },
{ text: 'C#', value: 'csharp' },
{ text: 'C++', value: 'cpp' }
],
codesample_content_css: '/static/prism.css',
};
const content = ref('');
onMounted(() => {
// console.log('props.modelValue', props.modelValue);
// content.value = props.modelValue;
});
watch(
() => content.value,
() => {
console.log('变化了');
imgsize(content.value);
revert_data(content.value);
}
);
watch(
() => props.modelValue,
() => {
content.value = props.modelValue;
}
);
// 匹配所有img图片,把所有宽度大于750px的图片等比缩小到750px
const imgsize = function (img) {
// 使用正则表达式匹配<img>标签
const regex = /<img[^>]*\b(?:width|height)\s*=\s*["']?(\d{4,}|[1-9][0-4]\d{2,})["']?\s*[^>]*>/g;
const matchedImages = img.match(regex);
console.log('matchedImages', matchedImages);
if (matchedImages) {
for (const imgTag of matchedImages) {
// 匹配width和height属性的值
const widthMatch = imgTag.match(/\bwidth\s*=\s*["']?(\d+)/);
const heightMatch = imgTag.match(/\bheight\s*=\s*["']?(\d+)/);
if (widthMatch && heightMatch) {
const width = parseInt(widthMatch[1]);
const height = parseInt(heightMatch[1]);
if (width > 850) {
// 计算等比缩小后的新高度,保持宽度等于750px
const aspectRatio = width / height;
const newWidth = 850;
const newHeight = Math.round(850 / aspectRatio);
// 修改width和height属性的值,确保宽度为750px
const updatedImgTag = imgTag.replace(`width="${width}"`, `width="${newWidth}"`).replace(`height="${height}"`, `height="${newHeight}"`);
// 替换原始的<img>标签
content.value = content.value.replace(imgTag, updatedImgTag);
}
}
}
}
}
const revert_data = (content) => {
//实现数据的双向绑定
context.emit("update:modelValue", content);
};
tinymce.init; // 初始化
return {
init,
revert_data,
content,
};
},
};
</script>
2,使用富文本组件
<div class="form_wrapper">
<form class="form_main" method="post">
<div class="tit">
//如果富文本编辑器有标题栏需要多写一个input框,因为富文本编辑器只有内容框
<input type="text" placeholder="请输入标题……" v-model="form.title" @input="titL"
@keydown.enter="handleEnterKey" />
</div>
//引入并使用富文本组件
<TEditor ref="editor" :modelValue=richTextContent v-model="state.content" />
</form>
</div>
<script setup>
let state = reactive({
content: '', //获取TEditor的内容
});
watch(
() => state.content,
() => {
//监控TEditor内容的变化
console.log(state.content);
form.content = state.content;
}
);
</script>
几个使用中的重难点
1,如何进行上传图片的操作
详细请看http://tinymce.ax-z.cn/general/upload-images.php
TinyMCE为我们提供了 images_upload_handler配置项
images_upload_handler是一个用于处理上传图片的回调函数。它接收三个参数:
- blobInfo:表示要上传的图片的信息,包括文件名、大小等。
- succFun:表示上传成功时的回调函数。
- failFun:表示上传失败时的回调函数。
例如
// 图片上传
images_upload_handler: async (blobInfo, success) => {
//改写,通过自己的upload接口,返回的图片时正常的url格式
const blob = blobInfo.blob();
const filename = blobInfo.filename();
var form = new FormData();
form.append("image", blob, filename);
console.log('form', form);
upload(form).then((res) => {
success(res.data.data);
console.log(res.data.data);
});
},
blobInfo表示要上传的图片的信息,包括图片的二进制数据和文件名。
blobInfo.blob()用于获取图片的二进制数据。
blobInfo.filename()用于获取图片的文件名。
然后创建一个FormData对象,将图片的二进制数据和文件名添加到表单中。再调传入上传图片的api接口,调用success函数并传递上传后的图片URL作为参数。这将通知TinyMCE编辑器上传成功,并将图片URL插入到编辑器中。
2,如何自定义上传后的图片大小
TinyMCE上传完图片后,图片的大小默认为原图片的大小
例如
打开控制台可以看到图片的实际宽高
可以看到图片已经超过了富文本框的宽度,那么怎么让上传后的图片等比例缩小为符合富文本框宽度的图片呢?
看到控制台我们知道图片之所以那么大,是因为富文本编辑器默认把图片真实大小也就是1920px设置成了这个img标签的宽度。
所以我们只需要改变这个width和height就行了
实现图片等比例缩小的方法,传入一个富文本,它会匹配这个富文本里的img标签,并查看这个img标签的宽度是否超过了850px,如果超过了就会缩小为850px
// 匹配所有img图片,把所有宽度大于850px的图片等比缩小到850px
const imgsize = function (img) {
// 使用正则表达式匹配<img>标签
const regex = /<img[^>]*\b(?:width|height)\s*=\s*["']?(\d{4,}|[1-9][0-4]\d{2,})["']?\s*[^>]*>/g;
const matchedImages = img.match(regex);
console.log('matchedImages', matchedImages);
if (matchedImages) {
for (const imgTag of matchedImages) {
// 匹配width和height属性的值
const widthMatch = imgTag.match(/\bwidth\s*=\s*["']?(\d+)/);
const heightMatch = imgTag.match(/\bheight\s*=\s*["']?(\d+)/);
if (widthMatch && heightMatch) {
const width = parseInt(widthMatch[1]);
const height = parseInt(heightMatch[1]);
if (width > 850) {
// 计算等比缩小后的新高度,保持宽度等于850px
const aspectRatio = width / height;
const newWidth = 850;
const newHeight = Math.round(850 / aspectRatio);
// 修改width和height属性的值,确保宽度为850px
const updatedImgTag = imgTag.replace(`width="${width}"`, `width="${newWidth}"`).replace(`height="${height}"`, `height="${newHeight}"`);
// 替换原始的<img>标签
content.value = content.value.replace(imgTag, updatedImgTag);
}
}
}
}
}
我们监听这个富文本是否改动,改动就调用imgsize方法
watch(
() => content.value,
() => {
console.log('变化了');
imgsize(content.value);
}
);
可以看到图片已经等比例缩小了