基于VUE接入TinyMCE富文本编辑器 漂亮简洁 封装成组件随用随调
TinyMCE是一款易用、且功能强大的所见即所得的富文本编辑器。同类程序有:UEditor、Kindeditor、Simditor、CKEditor、wangEditor、Suneditor、froala等等。
配置灵活,界面简洁,支持自定义插件。
TinyMCE中文手册:http://tinymce.ax-z.cn
一、安装环境
1、安装需要的包
我使用的是v5版本的,需要搭配tinymce-vue包来使用
npm install tinymce/tinymce-vue@3.2.2
和
npm install tinymce@5.7.1
2、将安装的tinymce包copy放在public下
cp ./node_modules/tinymce ./public/tinymce
3、下载语言包
打开链接 http://tinymce.ax-z.cn/static/tiny/langs/zh_CN.js ,将zh_CN.js 另存到 public/tinymce/langs下,没有langs文件夹的可以手动创建一下
二、代码引用
附上我封装的组件,可以作为参考,上传文件的逻辑需要结合业务逻辑自行实现
<template>
<Editor :id="editorId" v-if="reFresh" v-model="newData" :init="init" />
</template>
<script>
// 引入组件
import tinymce from "tinymce/tinymce";
import Editor from "@tinymce/tinymce-vue";
import 'tinymce/themes/silver/theme'
import "tinymce/skins/ui/oxide/skin.min.css";
// 引入富文本编辑器主题的js和css
export default {
name: "TinymceEditor",
components: { Editor },
data() {
return {
init: { // 配置文件
base_url: '/tinymce',
menubar: false,
external_plugins: { //引入需要的插件
anchor: "/tinymce/plugins/anchor/plugin.min.js",
code: "/tinymce/plugins/code/plugin.min.js",
print: "/tinymce/plugins/print/plugin.min.js",
preview: "/tinymce/plugins/preview/plugin.min.js",
searchreplace: "/tinymce/plugins/searchreplace/plugin.min.js",
autolink: "/tinymce/plugins/autolink/plugin.min.js",
directionality: "/tinymce/plugins/directionality/plugin.min.js",
visualblocks: "/tinymce/plugins/visualblocks/plugin.min.js",
visualchars: "/tinymce/plugins/visualchars/plugin.min.js",
fullscreen: "/tinymce/plugins/fullscreen/plugin.min.js",
image: "/tinymce/plugins/image/plugin.min.js",
link: "/tinymce/plugins/link/plugin.min.js",
media: "/tinymce/plugins/media/plugin.min.js",
template: "/tinymce/plugins/template/plugin.min.js",
codesample: "/tinymce/plugins/codesample/plugin.min.js",
table: "/tinymce/plugins/table/plugin.min.js",
charmap: "/tinymce/plugins/charmap/plugin.min.js",
pagebreak: "/tinymce/plugins/pagebreak/plugin.min.js",
nonbreaking: "/tinymce/plugins/nonbreaking/plugin.min.js",
insertdatetime: "/tinymce/plugins/insertdatetime/plugin.min.js",
advlist: "/tinymce/plugins/advlist/plugin.min.js",
lists: "/tinymce/plugins/lists/plugin.min.js",
wordcount: "/tinymce/plugins/wordcount/plugin.min.js",
imagetools: "/tinymce/plugins/imagetools/plugin.min.js",
textpattern: "/tinymce/plugins/textpattern/plugin.min.js",
help: "/tinymce/plugins/help/plugin.min.js",
emoticons: "/tinymce/plugins/emoticons/plugin.min.js",
autosave: "/tinymce/plugins/autosave/plugin.min.js",
iframe: "/tinymce/plugins/iframe/plugin.min.js",
hr: "/tinymce/plugins/hr/plugin.min.js",
// formatpainter: "/tinymce/plugins/formatpainter/plugin.min.js",
},
language_url:
"/tinymce/langs/zh_CN.js", //语言文件
language: "zh_CN",
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;",
toolbar: [
"template code undo redo restoredraft cut copy paste pastetext forecolor backcolor bold italic underline strikethrough link unlink anchor alignleft aligncenter alignright alignjustify outdent indent formatselect fontselect fontsizeselect bullist numlist blockquote subscript superscript removeformat table image media charmap emoticons pagebreak insertdatetime print preview fullscreen formatpainter iframe hr",
],
templates: [],
// content_css : ['/layui/css/layui.css','/css/public.css?v=1'],
content_css: [],
height: 800, //编辑器高度
min_height: 200,
max_height: 600,
branding: false,
paste_data_images: true, // 允许粘贴图像
file_picker_types: "file image media",
images_upload_handler: (blobInfo, success) => {
var xhr, formData;
var file = blobInfo.blob();//转化为易于理解的file对象
xhr = new XMLHttpRequest();
xhr.withCredentials = false;
xhr.open("POST",`${process.env.VUE_APP_API_URL}/file/upload`); //上传文件的地址,需要替换成自己的
xhr.setRequestHeader("Authorization",window.localStorage.escourse_token);
xhr.onload = function () {
var json;
json = JSON.parse(xhr.responseText);
};
formData = new FormData();
formData.append("file", file, file.name);
formData.append("module", "post");
formData.append("file_source", "1");
// formData.append("token",window.localStorage.escourse_token)
xhr.send(formData);
xhr.onload = function () {
let json = JSON.parse(xhr.responseText);
if (json.code == 200) {
success(process.env.VUE_APP_API_URL + json.data.file.path)
return
}
};
},
media_url_resolver: function (data, resolve) {
try {
let videoUri = encodeURI(data.url);
let embedHtml = `<p>
<span
class="mce-object mce-object-video"
data-mce-selected="1"
data-mce-object="video"
data-mce-p-width="100%"
data-mce-p-height="auto"
data-mce-p-controls="controls"
data-mce-p-controlslist="nodownload"
data-mce-p-allowfullscreen="true"
data-mce-p-src=${videoUri} >
<video src=${data.url} width="100%" height="auto" controls="controls" controlslist="nodownload">
</video>
</span>
</p>
<p style="text-align: left;"></p>`;
resolve({ html: embedHtml });
} catch (e) {
resolve({ html: "" });
}
},
file_picker_callback(cb, value, meta) {
//当点击meidia图标上传时,判断meta.filetype == 'media'有必要,因为file_picker_callback是media(媒体)、image(图片)、file(文件)的共同入口
console.log('meta', meta);
//创建一个隐藏的type=file的文件选择input
let input = document.createElement("input");
input.setAttribute("type", "file");
input.onchange = function () {
var xhr, formData;
let file = this.files[0]; //只选取第一个文件。如果要选取全部,后面注意做修改
xhr = new XMLHttpRequest();
xhr.withCredentials = false;
xhr.open("POST",`${process.env.VUE_APP_API_URL}/file/upload`); //上传文件的地址,需要替换成自己的
xhr.setRequestHeader("Authorization",window.localStorage.escourse_token);
xhr.onload = function () {
var json;
json = JSON.parse(xhr.responseText);
};
formData = new FormData();
formData.append("file", file, file.name);
formData.append("module", "post");
formData.append("file_source", "1");
// formData.append("token",window.localStorage.escourse_token)
xhr.send(formData);
xhr.upload.onprogress = function () {
// 进度(e.loaded / e.total * 100)
// progress(e.loaded / e.total * 100);
};
// xhr.onerror = function () {
// //根据自己的需要添加代码
// console.log(xhr.status);
// return;
// };
xhr.onload = function () {
let json = JSON.parse(xhr.responseText);
if (json.code == 200) {
// if (meta.filetype == "media") {
cb(process.env.VUE_APP_API_URL + json.data.file.path, { text: json.data.file.name })
// }
// if (meta.filetype == "file") {
// cb(json.data.url, { text: json.data.name })
// }
return
}
};
};
//触发点击
input.click();
},
},
editorId: this.id,
newData: this.data,
reFresh:false,
laws:[]
};
},
created(){
this.getBandData();
},
mounted() {
tinymce.init({});
},
props: {
data: String,
onChange: Function // 回调函数,将新修改的内容做为第一个参数返回
},
watch: {
newData: 'updateData',
data: 'uptData',
"init.templates"(newValue) { // 如果配置发生改变,则刷新编辑器
this.reFresh = false;
this.$nextTick(() => {
this.reFresh = true;
});
},
},
methods: {
async getBandData() {
let urlResult = await getTemplates(); //这边我是自己封装了个插件,通过getTemplates接口,从后端读取模板,再显示到编辑器插件上,如果不需要可以删掉
if (urlResult.code == 200) {
let array = urlResult.data;
let arr = [];
for (let index = 0; index < array.length; index++) {
const element = array[index];
let data = {
title: element.name,
content: element.html,
description: element.html,
// description: "<img src='"+this.urlConfig.adminUrl + element.thumbnail+"'/>",
};
arr.push(data);
}
this.laws = arr; //得到的数据赋值给laws
this.init.templates = this.laws;
}
},
addContent(html) {
console.log('html', html)
this.newData = this.newData + html
},
uptData() {
this.newData = JSON.parse(JSON.stringify(this.data))
},
updateData() {
this.onChange(this.newData)
},
clear() {
this.editorValue = "";
},
},
};
</script>
在页面中引用
<template>
<Editor v-bind:data="data" :onChange="onchange"></Editor>
</template>
import Editor from "../../../components/tinymce/editor";
export default {
data() {
return {
data:''
};
},
components: {
Editor
},
methods: {
onchange(e){
this.data = e
}
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」