首页 > 编程学习 > 基于Vue2.0对Tinymce Editor富文本编辑器进行封装与使用

一、TinyMCE介绍
TinyMCE is a rich-text editor that allows users to create formatted content within a user-friendly interface.

官网:https://www.tiny.cloud/
文档:https://www.tiny.cloud/docs/tinymce/6/
中文文档:http://tinymce.ax-z.cn/
GitHub:https://github.com/tinymce

二、本地托管的Tinymce编辑器
进入官网的自托管版本下载页面(https://www.tiny.cloud/get-tiny/self-hosted/),下载到本地。解压缩后打开该文件夹,发现里面有 bin、js、modules、patches 等文件夹和文件,然后双击打开 js 文件夹,发现里面有 tinymce 文件夹,将这个 tinymce 文件夹整个复制到项目的 public 文件夹内,进行本地引用。

父组件:index.vue

<template>
  <div>
    <Tinymce
      ref="tinymceRef1"
      :editorParams="editorParams1"
    />

    <Tinymce
      ref="tinymceRef2"
      :editorParams="editorParams2"
    />

    <p align="center">
      <el-button type="primary" @click="handleSubmitClick">完成</el-button>
    </p>
  </div>
</template>

<script>
import Tinymce from "./components/tinymce";

export default {
  components: {
    Tinymce
  },
  data: () => ({
    // editorParams1 必填非空的编辑器参数
    editorParams1: {
      content: '',// 编辑器内容
      uploadImageUrl: 'http://IP:Port/api/uploadFileAction',// 编辑器上传图片的地址
      height: 250,// 编辑器高度
      disabled: false,// 编辑器是否禁用
    },

    // editorParams2 必填非空的编辑器参数
    editorParams2: {
      content: 'HelloWorld!',// 编辑器内容
      placeholder: '请填写内容',// 编辑器占位内容
      uploadImageUrl: 'http://IP:Port/api/uploadFileAction',// 编辑器上传图片的地址
      height: 300,// 编辑器高度
      disabled: true,// 编辑器是否禁用
    }
  }),
  methods: {
    /**
     * 获取编辑器内容
     */
    async handleSubmitClick() {  
      let refs = await this.$refs;
 
      let tinymceRef1 = refs.tinymceRef1;
      if (tinymceRef1 != null) {
        console.log(tinymceRef1.content);
      }

      let tinymceRef2 = refs.tinymceRef2;
      if (tinymceRef2 != null) {
        console.log(tinymceRef2.content);
      }
    }
  }
}
</script>

子组件:tinymce.vue

<template>
  <div class="t-e-for-vue">
    <editor
      v-model='content'
      :init='init'
      :disabled='disabled'
    />
  </div>
</template>

<script>
/**
 * 官网:https://www.tiny.cloud/
 * 
 * 中文文档:http://tinymce.ax-z.cn/
 */
import tinymce from 'tinymce/tinymce';
import Editor from '@tinymce/tinymce-vue';

/**
 * 引入本地托管版本的 tinymce 资源
 * 
 * 进入官网的自托管版本下载页面(https://www.tiny.cloud/get-tiny/self-hosted/),下载到本地
 * 解压缩后打开该文件夹,发现里面有 bin、js、modules、patches 等文件夹和文件
 * 然后双击打开 js 文件夹,发现里面有 tinymce 文件夹
 * 将这个 tinymce 文件夹整个复制到项目的 public 文件夹内,进行本地引用,即资源位置:public/tinymce
 */
import 'tinymce/themes/silver'; // 引入编辑器主题
import 'tinymce/icons/default/icons'; // 引入编辑器图标

import 'tinymce/plugins/preview'; // 引入预览插件
import 'tinymce/plugins/searchreplace'; // 引入查找替换插件
import 'tinymce/plugins/autolink'; // 引入自动链接插件
import 'tinymce/plugins/directionality';
import 'tinymce/plugins/visualblocks';
import 'tinymce/plugins/visualchars';
import 'tinymce/plugins/fullscreen'; // 引入全屏显示插件
import 'tinymce/plugins/image'; // 引入图片插件
import 'tinymce/plugins/link'; // 引入超链接插件
import 'tinymce/plugins/media'; // 引入视频插件
import 'tinymce/plugins/template';
import 'tinymce/plugins/code'; // 引入代码插件
import 'tinymce/plugins/codesample';
import 'tinymce/plugins/table'; // 引入表格插件
import 'tinymce/plugins/charmap'; // 引入特殊字符插件
import 'tinymce/plugins/hr';
import 'tinymce/plugins/nonbreaking';
import 'tinymce/plugins/insertdatetime';
import 'tinymce/plugins/advlist'; // 引入高级列表插件
import 'tinymce/plugins/lists'; // 引入列表插件
import 'tinymce/plugins/wordcount'; // 字数统计插件
import 'tinymce/plugins/imagetools';
import 'tinymce/plugins/textpattern'; // 引入快速排版插件
import 'tinymce/plugins/autosave'; // 引入自动存稿插件
import 'tinymce/plugins/autoresize';
import 'tinymce/plugins/toc'; // 引入目录生成器插件
import 'tinymce/plugins/contextmenu'; // 引入上下文菜单插件
import 'tinymce/plugins/textcolor'; // 引入字体颜色插件
import 'tinymce/plugins/colorpicker'; // 引入颜色选择器插件
/* / 引入本地托管版本的 tinymce 资源 */

/* 定义编辑器字体 */
const fontFormats = [
  '宋体=宋体',
  '苹果苹方=PingFang SC',
  '微软雅黑=微软雅黑',
  '新宋体=新宋体',
  '黑体=黑体',
  '楷体=楷体',
  '隶书=隶书',
  'Courier New=courier new,courier',
  'AkrutiKndPadmini=Akpdmi-n',
  'Andale Mono=andale mono,times',
  'Arial=arial,helvetica,sans-serif',
  'Arial Black=arial black,avant garde',
  'Book Antiqua=book antiqua,palatino',
  'Comic Sans MS=comic sans ms,sans-serif',
  'Courier New=courier new,courier',
  'Georgia=georgia,palatino',
  'Helvetica=helvetica',
  'Impact=impact,chicago',
  'Symbol=symbol',
  'Tahoma=tahoma,arial,helvetica,sans-serif',
  'Terminal=terminal,monaco',
  'Times New Roman=times new roman,times',
  'Trebuchet MS=trebuchet ms,geneva',
  'Verdana=verdana,geneva',
  'Webdings=webdings',
  'Wingdings=wingdings,zapf dingbats'
];
/* / 定义编辑器字体 */

export default {
  components: {
    Editor
  },
  props: {
    /* **** 编辑器必须参数 **** */
    editorParams: {

      // 编辑器内容
      content: {
        type: String,
        default: ''
      },

      // 编辑器上传图片的地址
      uploadImageUrl: {
        type: String,
        default: ''
      },

      // 编辑器高度
      height: {
        type: Number,
        default: 600
      },

      // 编辑器是否禁用
      disabled: {
        type: Boolean,
        default: false
      },
    },
    /* **** / 编辑器必须参数 **** */

    /* **** 编辑器插件 **** */
    plugins: {
      type: [String, Array],
      default:
      'preview searchreplace autolink directionality visualblocks visualchars fullscreen \
      image link media template code codesample table charmap hr nonbreaking insertdatetime advlist \
      lists wordcount imagetools textpattern autosave autoresize'
    },
    /* **** / 编辑器插件 **** */

    /* **** 编辑器工具栏,根据需要写上对应的工具名称、顺序、分割线等 **** */
    toolbar: {
      type: [String, Array],
      default:
        'undo redo restoredraft | \
        styleselect fontselect fontsizeselect | \
        cut copy paste pastetext | \
        forecolor backcolor bold italic underline strikethrough link codesample | \
        alignleft aligncenter alignright alignjustify outdent indent lineheight formatpainter | \
        bullist numlist | \
        blockquote subscript superscript removeformat | \
        table image media charmap hr pagebreak insertdatetime | \
        bdmap fullscreen preview | \
        toc contextmenu textcolor colorpicker'
    }
    /* **** / 编辑器工具栏,根据需要写上对应的工具名称、顺序、分割线等 **** */
  },
  data() {
    return {
      // 编辑器必须参数
      content: this.content, // 编辑器内容
      uploadImageUrl: '', // 编辑器上传图片的地址
      height: 600, // 编辑器高度
      disabled: false, // 编辑器是否禁用

      // 编辑器初始化参数
      init:{
        selector: '', // DOM选择器
        language: 'zh-Hans', // 设置编辑器语言类型
        language_url: 'tinymce/langs/zh-Hans.js', // 设置编辑器汉化脚本地址,具体位置:public/tinymce/langs/zh-Hans.js,下载地址:https://www.tiny.cloud/get-tiny/language-packages/
        skin_url: 'tinymce/skins/ui/oxide', // 设置编辑器皮肤类型,具体位置:public/tinymce/skins/ui/oxide
        content_css: '/tinymce/skins/content/default/content.css',// 设置编辑区域的样式,具体位置:public/tinymce/skins/content/default/content.css

        branding: false,// 是否显示品牌
        min_height: this.height, // 设置编辑器最小高度
        max_height: null, // 设置编辑器最大高度
        toolbar: this.toolbar, // 设置编辑器工具栏
        plugins: this.plugins,// 需要用到的功能插件,如链接,列表等等

        placeholder: '请输入内容~', // 设置编辑器占位内容
        content_style: 'body, td, pre { font-size: 14px; }', // 设置编辑区域自定义样式,如:设置编辑器默认字体大小
        font_formats: fontFormats.join(';'),
        fontsize_formats: '12px 14px 16px 18px 20px 22px 24px 28px 32px 36px 48px 56px 72px', // 设置编辑器字体大小
        lineheight_formats: '0.5 0.8 1 1.2 1.5 1.75 2 2.5 3 4 5', // 设置编辑器行高,也可配置成 '12px 14px 16px 20px' 的形式
        letterspacing_formats: '0 0.5pt 1pt 1.5pt 2pt 3pt 5pt 10pt 15pt 20pt 30pt', // 设置编辑器文字间距

        toolbar_mode: 'sliding', // 设置编辑器工具栏模式,取值:floating / sliding / scrolling / wrap
        draggable_modal: true, // 设置编辑器模态窗口是否可拖动
        paste_data_images: true, // 设置编辑器允许粘贴图像,图片粘贴自动上传功能需要用到此配置
        relative_urls : false, // 设置将当前域名中的所有URL转换为相对URL,参考见:http://tinymce.ax-z.cn/configure/url-handling.php#relative_urls
        remove_script_host : false, // 设置是否删除URL的域名部分,参考见:http://tinymce.ax-z.cn/configure/url-handling.php#remove_script_host
        convert_urls : true, // 设置是否自动转换URL,参考见:http://tinymce.ax-z.cn/configure/url-handling.php#convert_urls

        /**
         * 初始化开始前执行
         */
        setup: (editor) => {
          console.log('ID =>', editor.id, '的 Tinymce 编辑器即将初始化~');
        },

        /**
         * 初始化结束后执行
         */
        init_instance_callback: (editor) => {
          // 监听编辑器 INPUT 事件
          editor.on('input',(e) => {
            if (e && e.target && e.target.innerHTML) {
              let html = e.target.innerHTML;
              console.log('Tinymce input =>', html);
            }
          });

          // 监听编辑器 CHANGE 事件
          editor.on('change',(e) => {
            if (e && e.target && e.target.innerHTML) {
              let html = e.target.innerHTML;
              console.log('Tinymce change =>', html);
            }
          });

          // 监听编辑器 FOCUS 事件
          editor.on('focus',(e) => {
            if (e && e.target && e.target.innerHTML) {
              let html = e.target.innerHTML;
              console.log('Tinymce focus =>', html);
            }
          });
        },

        /**
         * 图片上传自定义实现
         * 
         * 参考见:http://tinymce.ax-z.cn/configure/file-image-upload.php#images_upload_handler
         */
        images_upload_handler: (blobInfo, success, failure, progress) => {
          // 1、基于 base64 编码方式上传图片
          // const img = "data:image/jpeg;base64," + blobInfo.base64();
          // success(img);

          // 2、基于 Ajax 异步方式上传图片
          var xhr, formData;
          xhr = new XMLHttpRequest();
          xhr.withCredentials = false;
          xhr.open('POST', this.uploadImageUrl);

          xhr.upload.onprogress = function(e) {
            progress(e.loaded / e.total * 100);
          }

          xhr.onload = function() {
            var json;
            if (xhr.status == 403) {
              failure('HTTP Error: ' + xhr.status, { remove: true });
              return;
            }

            if (xhr.status < 200 || xhr.status >= 300 ) {
              failure('HTTP Error: ' + xhr.status);
              return;
            }

            json = JSON.parse(xhr.responseText);
            if (!json) {
              failure('Invalid JSON: ' + xhr.responseText);
              return;
            }

            for (let i in json.data) {
              success(json.data[i].url);
            }
          };

          xhr.onerror = function () {
            failure('Image upload failed due to a XHR Transport error. Code: ' + xhr.status);
          }

          formData = new FormData();
          formData.append('files', blobInfo.blob(), blobInfo.filename());
          xhr.send(formData);
        }
      },
    }
  },
  watch: {
    /**
     * 深度监听编辑器必需参数
     */
    'editorParams': {
        handler: function(newVal, oldVal) {
          if (newVal != null) {
            console.log('editorParams =>', this.editorParams);
            this.content = newVal.content;
            this.disabled = newVal.disabled;
            this.uploadImageUrl = newVal.uploadImageUrl;
            this.init.min_height = newVal.height;
          }
        },
        immediate: true,
        deep: true
    },
  },
  created() {

  },
  mounted() {
    tinymce.init({});// 再次初始化编辑器,参数为空键值对
  },
  methods: {

  },
}
</script>

<style lang="less" scoped>
  /* Tinymce 编辑器 */
  .t-e-for-vue {
    width: 800px;
    margin: 100px auto;

    /deep/ .tox-tinymce {
      border-radius: unset;
    }
  }
  /* / Tinymce 编辑器 */
</style>

<style lang="less">
  /* Tinymce 编辑器模态框弹窗 */
  .tox-dialog {

    .tox-dialog__body {

      table {
        th {
          font-size: 15px;
          text-align: center;
          font-weight: normal !important;
        }
        td {
          font-size: 14px;
          text-align: center;
        }
      }

      .tox-button--secondary {
        font-weight: normal !important;
      }
    }

    .tox-dialog__footer {

      .tox-button {
        font-weight: normal !important;
        padding: 2px 15px 3px 15px !important;
        letter-spacing: 1px !important;
        font-size: 13px !important;
      }
    }
  }
  /* / Tinymce 编辑器模态框弹窗 */

  /* Tinymce 编辑器下拉框菜单弹窗 */
  .tox-tiered-menu {

    .tox-menu {
      max-height: 300px !important; // 设置下拉框菜单弹窗最大高度

      /* 滚动条 */
      &::-webkit-scrollbar {
        width: 8px;
        height: 8px;
        background-color: #f8f8f8;
      }
      // 滚动条轨道
      &::-webkit-scrollbar-track {
        background-color: #f2f2f2;
        border-bottom: 1px solid #ebeef5;
        border-left: 1px solid #ebeef5;
        border-right: 1px solid #ebeef5;
      }

      // 滑块
      &::-webkit-scrollbar-thumb {
        box-shadow: inset 0 0 6px rgba(0,0,0,.3);
        background-color:#999;
        border-radius: 5px;
      }

      // 滑块hover
      &::-webkit-scrollbar-thumb:hover {
        box-shadow: inset 0 0 6px rgba(0,0,0,.3);
        background-color:#666;
      }
      /* / 滚动条 */
    }
  }
  /* / Tinymce 编辑器下拉框菜单弹窗 */
</style>

参考:

前端富文本编辑器 vue + tinymce超简单使用方法_前端小白۞的博客-CSDN博客_vue富文本编辑器tinymce

tinymce--一款非常好用的富文本编辑器 VUE如何集成tinymce编辑器_coder的自我修养的博客-CSDN博客_tinymce编辑器

效果如下~

Copyright © 2010-2022 dgrt.cn 版权所有 |关于我们| 联系方式