vue2.x 富文本组件 支持图片、文件、视频上传

<template>
  <div>
    <editor v-model="content" :init="init" :disabled="disabled"></editor>
  </div>
</template>

<script>
import tinymce from "tinymce/tinymce";
import Editor from "@tinymce/tinymce-vue";
import "tinymce/icons/default/icons";
import "tinymce/themes/silver";
import "tinymce/plugins/image";
// import "tinymce/plugins/media"; // 视频上传暂不处理
import "tinymce/plugins/table";
import "tinymce/plugins/lists";
import "tinymce/plugins/contextmenu";
import "tinymce/plugins/wordcount";
import "tinymce/plugins/colorpicker";
import "tinymce/plugins/textcolor";
import "tinymce/plugins/preview";
import "tinymce/plugins/code";
import "tinymce/plugins/link";
import "tinymce/plugins/advlist";
import "tinymce/plugins/codesample";
import "tinymce/plugins/hr";
import "tinymce/plugins/fullscreen";
import "tinymce/plugins/textpattern";
import "tinymce/plugins/searchreplace";
import "tinymce/plugins/autolink";
import "tinymce/plugins/directionality";
import "tinymce/plugins/visualblocks";
import "tinymce/plugins/visualchars";
import "tinymce/plugins/template";
import "tinymce/plugins/charmap";
import "tinymce/plugins/nonbreaking";
import "tinymce/plugins/insertdatetime";
import "tinymce/plugins/autosave";
import "tinymce/plugins/autoresize";
import {Loading} from 'element-ui';

export default {
  name: "tinymceEditor",
  components: {
    Editor
  },
  props: {
    value: {
      type: String,
      default: ""
    },
    disabled: {
      type: Boolean,
      default: false
    },
    plugins: {
      type: [String, Array],
      default:
          "preview searchreplace autolink directionality visualblocks visualchars fullscreen image link template code codesample table charmap hr nonbreaking insertdatetime advlist lists wordcount textpattern autosave autoresize"
    },
    toolbar: {
      type: [String, Array],
      default:
          "code undo redo restoredraft | cut copy paste pastetext | forecolor backcolor bold italic underline strikethrough link codesample | alignleft aligncenter alignright alignjustify outdent indent formatpainter | \
      styleselect formatselect fontselect fontsizeselect | bullist numlist | blockquote subscript superscript removeformat | \
      table image charmap hr pagebreak insertdatetime | fullscreen preview"
    }
  },
  data() {
    return {
      //初始化配置
      init: {
        menubar: false, // 顶部菜单栏显隐
        language_url: "./tinymce/langs/zh_CN.js",
        language: "zh_CN",
        skin_url: "./tinymce/skins/ui/oxide",
        height: 770,
        min_height: 770,
        max_height: 770,
        toolbar_mode: "wrap",
        plugins: this.plugins,
        toolbar: this.toolbar,
        content_style: "p {margin: 5px 0;}",
        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;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`,
        branding: false,
        // 文件上传
        file_picker_callback: function (callback, value, meta) {
          const _this = this
          //文件分类
          let filetype = '.jpg, .jpeg, .png, .pdf, .txt, .zip, .rar, .7z, .doc, .docx, .xls, .xlsx, .ppt, .pptx, .mp3, .mp4';
          //为不同插件指定文件类型及后端地址
          switch (meta.filetype) {
            case 'image':
              filetype = '.jpg, .jpeg, .png';
              break;
            case 'media':
              filetype = '.mp3, .mp4';
              break;
            case 'file':
              filetype = '.pdf';
              break;
            default:
          }
          //后端接收上传文件的地址
          let uploadUrl = `${serverConfig.NEW_MEETING_RESERVATION_BACKEND}/api/oss`;
          //模拟出一个input用于添加本地文件
          const token = sessionStorage.getItem("Admin-Token") || sessionStorage.getItem("token")
          let input = document.createElement('input');
          input.setAttribute('type', 'file');
          input.setAttribute('accept', filetype);
          input.click();
          input.onchange = function () {
            //获取上传的文件
            const file = this.files[0];
            // 定义暂存上传文件的变量
            const maxSize = 15 * 1024 * 1024
            if (file.size > maxSize) {
              _this.$message.warn('上传文件的大小不能超过15M')
              return
            }
            const loading = Loading.service({
              lock: true,
              text: "Loading",
              spinner: "el-icon-loading",
              background: "rgba(0, 0, 0, 0.7)",
            });
            let xhr, formData;
            xhr = new XMLHttpRequest();
            xhr.withCredentials = false;
            xhr.open('POST', uploadUrl);
            xhr.setRequestHeader('X-Id-Token', token)
            xhr.onload = function () {
              loading.close();
              const json = JSON.parse(xhr.responseText);
              if (xhr.status != 200) {
                _this.$message.error(json.msg)
                return;
              }
              // Provide file and text for the link dialog
              if (meta.filetype === 'file') {
                //json.data.link为预览地址,此处需要下载地址
                const url = `${serverConfig.NEW_MEETING_RESERVATION_BACKEND}/api/oss/download/${json.data.id}?idToken=${token}`
                callback(url, {text: json.data.name});
              }
              // Provide image and alt text for the image dialog
              if (meta.filetype === 'image') {
                callback(json.data.link, {text: json.data.name});
              }
              // Provide alternative source and posted for the media dialog
              if (meta.filetype === 'media') {
                callback(json.data.link, {source2: '', poster: ''});
              }
              // callback(json.location);
              input.value = ''
            };
            formData = new FormData();
            formData.append('file', file, file.name);
            xhr.send(formData);
          }
        },
      },
      content: this.value,
    }
  },
  mounted() {
    tinymce.init({});
  },
  methods: {},
  watch: {
    value(newValue) {
      this.content = newValue;
    },
    content(newValue) {
      this.$emit("input", newValue);
    }
  }
}
</script>

<style scoped lang="scss">

</style>

效果图:

相关推荐
也无晴也无风雨25 分钟前
深入剖析输入URL按下回车,浏览器做了什么
前端·后端·计算机网络
Martin -Tang1 小时前
Vue 3 中,ref 和 reactive的区别
前端·javascript·vue.js
SRY122404192 小时前
javaSE面试题
java·开发语言·面试
FakeOccupational2 小时前
nodejs 020: React语法规则 props和state
前端·javascript·react.js
无尽的大道3 小时前
Java 泛型详解:参数化类型的强大之处
java·开发语言
ZIM学编程3 小时前
Java基础Day-Sixteen
java·开发语言·windows
放逐者-保持本心,方可放逐3 小时前
react 组件应用
开发语言·前端·javascript·react.js·前端框架
曹天骄4 小时前
next中服务端组件共享接口数据
前端·javascript·react.js
一丝晨光4 小时前
编译器、IDE对C/C++新标准的支持
c语言·开发语言·c++·ide·msvc·visual studio·gcc
阮少年、4 小时前
java后台生成模拟聊天截图并返回给前端
java·开发语言·前端