TinyMCE是一款易用、且功能强大的所见即所得的富文本编辑器。同类程序有:UEditor、Kindeditor、Simditor、CKEditor、wangEditor、Suneditor、froala等等。
配置灵活,界面简洁,支持自定义插件。
TinyMCE中文手册:http://tinymce.ax-
一、安装环境
1、安装需要的包
我使用的是v5版本的,需要搭配tinymce-vue包来使用
highlighter- apache
npm install tinymce/tinymce-vue@3.2.2
和
highlighter- apache
npm install tinymce@5.7.1
2、将安装的tinymce包copy放在public下
highlighter- awk
cp ./node_modules/tinymce ./public/tinymce
3、下载语言包
打开链接http://tinymce.ax-/static/tiny/langs/zh_CN.js,将zh_CN.js 另存到 public/tinymce/langs下,没有langs文件夹的可以手动创建一下
二、代码引用
附上我封装的组件,可以作为参考,上传文件的逻辑需要结合业务逻辑自行实现
cnb-over-length-code-block cnb-over-length-code-block-expanded highlighter- xml
<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和cssexport 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><spanclass="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的文件选择inputlet 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; //得到的数据赋值给lawsthis.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>折叠
在页面中引用
highlighter- javascript
<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}}}