先放张效果图

第一步:安装依赖 npm install tinymce@5.0.12

第二步:在项目中的public文件夹中新建tinymce文件夹(因为我的项目是脚手架创建的,所以公共文件夹是public);在node_modules中找到skins文件夹复制到tinymce文件夹中

第三步:因为tinymce是英文版本的,所以要下载汉化包中文js,放到上面的tinymce文件夹中

下载网址:https://www.tiny.cloud/get-tiny/language-packages/

翻到最底部,找到中文语言点击Download按钮,放到和上一步的skins文件夹同级目录的tinymce文件夹中

第四步:封装tinymce组件,这里就直接上代码了;

Tip: 1. <script>里面import的这些都是富文本编辑器的工具栏,需要什么就引入什么;

2. 运行项目可能会有引入的工具报错,针对报错的工具再npm install即可;

第三方插件的引入(数学公式、导入word):

1. 最后两行是另外下载的插件(数学公式、导入word),上面的二和三步的tinymce文件夹里面新建plugins文件夹,下载的两个插件就丢这里面;

数学公式: kityformula-editor 下载地址: http://tinymce.ax-z.cn/more-plugins/kityformula-editor.php

导入word:importword 下载地址:https://github.com/Five-great/tinymce-plugins

2.代码中的toolbar数组里面就是工具的名称;| 符号就是编辑器工具栏的分隔符,界面的工具摆放顺序就是根据代码中的工具摆放顺序

上传视频工具:

1. 编辑器初始化中的“file_picker_callback”方法里面写的是上传视频的内容

2. validateVideo 方法是在文件后面的方法中定义的,目的是验证上传的视频是否符合,验证通过定义了一个全局的遮罩loading,再调用定义的uploadFile方法,调用后台接口把视频源传到后台进行业务的交互,上传成功后必须要写这行代码才能将视频显示在富文本编辑器中:callback(域名 + 代理地址 + 后台返回的路径)

<template>
  <div class="tinymce-editor">
    <editor v-model="myValue" :init="init" :disabled="disabled" @onClick="onClick">
    </editor>
  </div>
</template>
<script>
import tinymce from "tinymce/tinymce";
import "tinymce/themes/silver/theme";
import Editor from "@tinymce/tinymce-vue";
// import "tinymce/icons/default/icons";
// import "tinymce/models/dom/model";
import "tinymce/plugins/print";
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/pagebreak";
import "tinymce/plugins/nonbreaking";
import "tinymce/plugins/anchor";
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/help";
import "tinymce/plugins/emoticons";
import "tinymce/plugins/autosave";
// import "tinymce/plugins/bdmap";
// import "tinymce/plugins/indent2em";
import "tinymce/plugins/autoresize";
// import "tinymce/plugins/formatpainter";
// import "tinymce/plugins/axupimgs";
import "../../../../../public/tinymce/plugins/kityformula-editor/plugin.min.js";
import "../../../../../public/tinymce/plugins/importword/plugin.min.js";
import { uploadvideo } from "@admin/api/Settings/Secureaccess/Onlinelearning";
export default {
  components: {
    Editor,
  },
  props: {
    //传入一个value,使组件支持v-model绑定
    value: {
      type: String,
      default: "",
    },
    disabled: {
      type: Boolean,
      default: false,
    },
    plugins: {
      type: [String, Array],
      // default: "lists image media table textcolor wordcount contextmenu",
      default:
        "print preview searchreplace autolink directionality visualblocks visualchars fullscreen image link media template code codesample table charmap hr pagebreak nonbreaking anchor insertdatetime advlist lists wordcount imagetools textpattern help emoticons autosave  autoresize kityformula-editor importword",
    },
    fontsize_formats: "12px 14px 16px 18px 24px 36px 48px 56px 72px",
    toolbar: {
      type: [String, Array],
      // default: "undo redo |  formatselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | lists image media table | removeformat",
      default:
        "code undo redo restoredraft | cut copy paste pastetext | forecolor backcolor bold italic underline strikethrough link anchor | alignleft aligncenter alignright alignjustify outdent indent | \
    styleselect formatselect fontselect fontsizeselect | bullist numlist | blockquote subscript superscript removeformat | \
    table image media charmap hr pagebreak insertdatetime print preview | fullscreen | bdmap indent2em lineheight formatpainter axupimgs | kityformula-editor importword",
    },
    fontFormats: {
      type: String,
      default:
        "微软雅黑=Microsoft YaHei,Helvetica Neue,PingFang SC,sans-serif;宋体=simsun, serif;苹果苹方=PingFang SC, Microsoft YaHei, sans- serif;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;Symbol = symbol;Tahoma = tahoma, arial, helvetica, sans - serif;Terminal = terminal, monaco;Times New Roman = times new roman, times;Verdana = verdana, geneva",
    },
  },
  data() {
    return {
      //初始化配置
      init: {
        language_url: "/tinymce/zh_CN.js",
        language: "zh_CN",
        height: 650,
        skin_url: "/tinymce/skins/ui/oxide",
        plugins: this.plugins,
        toolbar: this.toolbar,
        // branding: false,
        // menubar: true,
        // quickbars_selection_toolbar:
        //   "removeformat | bold italic underline strikethrough | fontsizeselect forecolor backcolor",
        font_formats: this.fontFormats,
        fontsize_formats: this.fontsize_formats,
        //此处为图片上传处理函数,这个直接用了base64的图片形式上传图片,
        //如需ajax上传可参考https://www.tiny.cloud/docs/configure/file-image-upload/#images_upload_handler
        images_upload_handler: (blobInfo, success, failure) => {
          const img = "data:image/jpeg;base64," + blobInfo.base64();
          success(img);
        },
        file_picker_types: "media",
        media_live_embeds: true,
        file_picker_callback: (callback, value, meta) => {
          if (meta.filetype === "media") {
            const input = document.createElement("input");
            input.setAttribute("type", "file");
            const that = this; // 为 Vue 构造函数中的 this,指向 Vue 实例对象
            input.onchange = async function () {
              const file = this.files[0]; // 为 HTMLInputElement 构造函数中的 this,指向 input 实例对象
              const isValid = await that.validateVideo(file);

              if (isValid) {
                const loading = that.$loading({
                  lock: true,
                  text: "Loading",
                  spinner: "el-icon-loading",
                  background: "rgba(0, 0, 0, 0.7)",
                });
                that.uploadFile(file, "video");
                setTimeout(() => {
                  loading.close();
                  if (that.videomsg.url != "") {
                    callback(that.location + "lab/" + that.videomsg.url);
                  }
                  // console.log(that.location + "lab/" + that.videomsg.url);
                }, 2000);
                // const { url } = await that.uploadFile(file, "video");
                // callback(url);
              } else {
                callback();
              }
            };

            input.click();
          }
        },
        // video_template_callback: (data) => {
        //   console.log(data);
        //   let source = that.location + "lab/" + data.source1;
        //   console.log(source);
        //   return `<video width="745" height="420" controls="controls" src=${data.source} />`;
        // },
      },
      myValue: "",
      videomsg: {
        url: "",
        name: "",
      },
      location: "http://" + window.location.host + "/",
    };
  },
  mounted() {
    tinymce.init({});
  },
  methods: {
    //添加相关的事件,可用的事件参照文档=> https://github.com/tinymce/tinymce-vue => All available events
    //需要什么事件可以自己增加
    onClick(e) {
      this.$emit("onClick", e, tinymce);
    },
    //可以添加一些自己的自定义事件,如清空内容
    clear() {
      this.myValue = "";
    },
    //校验视频格式和内存大小
    validateVideo(file) {
      const isMP4 = file.type === "video/mp4";
      const isLt3M = file.size / 1024 / 1024 < 5;

      if (!isMP4) {
        this.$message.error("上传视频必须为 MP4 格式!");

        return false;
      }

      if (!isLt3M) {
        this.$message.error("上传视频大小限制 5M 以内!");

        return false;
      }

      const duration = this.getVideoDuration(file);
      if (duration > 60) {
        this.$message.error("上传视频时长不能超过 60 秒!");

        return false;
      }

      return true;
    },
    //获取视频源
    getVideoDuration(file) {
      return new Promise((resolve) => {
        const videoElement = document.createElement("video");
        videoElement.src = URL.createObjectURL(file);

        videoElement.addEventListener("loadedmetadata", () => {
          resolve(videoElement.duration);
        });
      });
    },
    //上传视频
    uploadFile(file, folder = "images") {
      const formData = new FormData();
      formData.append("file", file);
      uploadvideo(formData).then((res) => {
        if (res.code == 0) {
          this.videomsg.url = res.data;
          this.videomsg.name = file.name;
          // return {
          //   url: res.data,
          //   name: file.name,
          // };
        } else {
          this.$message({
            message: res.message,
            type: "error",
            duration: "5000",
          });
        }
      });
    },
  },
  watch: {
    value(newValue) {
      this.myValue = newValue;
      // console.log(this.myValue);
    },
    myValue(newValue) {
      this.$emit("input", newValue);
    },
  },
};
</script>
<style>
.tox-tinymce-aux {
  z-index: 3000 !important;
}
.el-loading-mask {
  z-index: 4000 !important;
}
</style>

第五步:在页面中调用上面封装好的组件

Tip: 1.<el-dialog>中使用TinymceEditor要注意延迟显示,延迟在弹窗显示之后再显示富文本编辑器,因为弹窗元素未加载出来时,编辑器找不到父级元素就无法正常显示

2.赋值和清除富文本编辑器的内容只要针对绑定的值进行清空和赋值操作就行

<template>
<div>
<TinymceEditor
        v-model="diaForm.content"
        :disabled="disabled"
        @onClick="onClick"
        ref="editor"
        v-if="openEditor"
      >
      </TinymceEditor>
</div>
</template>
<script>
import TinymceEditor from "@admin/components/tinymce/index";
export default {
  data() {
    return {
      
    }
  },
  components: {
    TinymceEditor
  }
}

</script>

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐