tinymce富文本简单配置及word内容粘贴图片上传

先上最终效果图:

 

一、tinymce初始化的一些配置

          1、首先需要引入下面两个插件

"@tinymce/tinymce-vue": "^3.2.0"
"tinymce": "^5.6.2"

 注:tinymce 6 与tinymce 5 在配置属性名发生过改变,详细参考官网
附上官网地址:https://www.tiny.cloud/docs/
至于这里为什么用5,不用6,因为6在火狐76版本中兼容性存在一定问题,富文本初始化有问题,所以这里采用5

2、当下载好插件后,需要到node_modules中找到tinymce文件夹复制出来放到vue项目的public文件夹中

   3、需要汉化的还需下载一个语言包,将插件翻译成中文,下载地址如下

官网地址:Language Packages | Trusted Rich Text Editor | TinyMCE

将下载好的语言包,放置在vue的public文件夹中,然后在代码中引入进来

下面是代码及引入的一些插件和一些基础配置

data() {
    return {
      //初始化配置
      myValue: this.value,
      init: {
        selector: "#" + this.tinymceId,
        language_url: "/tinymce/langs/zh-Hans.js", //汉化路径是自定义的,一般放在public或static里面
        language: "zh-Hans",
        skin_url: "/tinymce/skins/ui/oxide", //皮肤:浅色
        // skin_url: "/tinymce/skins/ui/oxide-dark", //皮肤:暗色
        plugins: this.plugins, //插件
        toolbar: this.toolbar,
        // font_size_formats: '12px 14px 16px 18px 24px 36px 48px', // 字体大小
        // font_family_formats: '微软雅黑=\'微软雅黑\';宋体=\'宋体\';黑体=\'黑体\';仿宋=\'仿宋\';楷体=\'楷体\';隶书=\'隶书\';幼圆=\'幼圆\';Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;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;sans-serif',
        fontsize_formats: '12pt 14pt 16pt 18pt 24pt 36pt 48pt', // 字体大小
        font_formats: '微软雅黑=\'微软雅黑\';宋体=\'宋体\';黑体=\'黑体\';仿宋=\'仿宋\';楷体=\'楷体\';隶书=\'隶书\';幼圆=\'幼圆\';Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;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;sans-serif',
        plugin_preview_width: 375, // 预览宽度
        plugin_preview_height: 668,
        toolbar_location: "/",
        content_style: "img {max-width:100%;}", // 直接自定义可编辑区域的css样式
        // toolbar_drawer: "sliding",
        menubar: this.menubar, //隐藏菜单栏
        statusbar: this.statusbar, // 最下方的元素路径和字数统计那一栏是否显示
        elementpath: this.elementpath, //元素路径是否显示
        branding: false, // 隐藏品牌标识
        paste_data_images: this.paste_data_images, // 设置为“true”即允许粘贴图像,而将其设置为“false”则不允许粘贴图像。
        resize: this.resize, //编辑器宽高是否可变,false-否,true-高可变,'both'-宽高均可,注意引号
        height: this.height, //高度
        paste_webkit_styles: "all",
        paste_merge_formats: true,
        nonbreaking_force_tab: false,
        paste_auto_cleanup_on_paste: false,
        placeholder: this.placeholder,
        //链接打开方式
        default_link_target: "_blank",

    };
  },

配置好以上内容,页面如下效果

4.图片上传功能

data() {
    return {
      //初始化配置
      myValue: this.value,
      init: {
       
        init_instance_callback: editor => {
          editor.on("paste", evt => {
            // 监听粘贴事件
            this.onPaste(evt);
          });
        },

    };
  },

图片上传并替换Url

methods: {
    onPaste(event, success, failure) {
      let self = this;
      const rtf = event.clipboardData.getData('text/rtf');
      // 提取图片信息
      const hexStrings = this.extractImageDataFromRtf(rtf);
      // 获取base64图片数据
      const base64Images = hexStrings.map((hexObj) => {
        return this.convertHexToBase64(hexObj.hex);
      })
      // 调用上传方法
      for (let i = 0; i < base64Images.length; i++) {
        this.picUpload(`data:image/png;base64,${base64Images[i]}`,base64Images.length, i)
      }

    },
    
    /**
     * data: 图片Base64 编码
     * len : 上传图片数量
     */
     async picUpload(data, len, i) {
      ++this.uploadNum
      let file = this.base64toFile(data);
      var formData = new FormData();
      formData.append('file', file);
      await axios({
        ContentType:'multipart/form-data',
        url: '/api/file/Uploader',
        method: 'post',
        data: formData
      }).then(res => {
        this.uploadPicList.push({ url: res.data.data.url, index: i })
        if (this.uploadNum === len) {
          setTimeout(() => {
            this.setResponseUrl(res.data.data.url)
          },1000)
        }
      })
    },
}

附上完整代码

<template>
  <div class="tinymce-editor">
    <Editor
      :init="init"
      :tinymceId="tinymceId"
      :disabled="disabled"
      v-model="myValue"
      @input="tinymcechange"
    ></Editor>
  </div>
</template>
 
<script>
import tinymce from "tinymce/tinymce"; //tinymce默认hidden,不引入不显示
import Editor from "@tinymce/tinymce-vue"; //编辑器引入
import "tinymce/themes/silver/theme"; //编辑器主题
// import "tinymce/icons/default"; //引入编辑器图标icon,不引入则不显示对应图标
// 引入编辑器插件(基本免费插件都在这儿了)
// import "tinymce/plugins/advlist"; //高级列表
// import "tinymce/plugins/autolink"; //自动链接
// import "tinymce/plugins/link"; //超链接
// import "tinymce/plugins/image"; //插入编辑图片
// import "tinymce/plugins/lists"; //列表插件
// import "tinymce/plugins/charmap"; //特殊字符
// import "tinymce/plugins/media"; //插入编辑媒体/
// import "tinymce/plugins/wordcount"; // 字数统计
// import "tinymce/plugins/colorpicker"; //选择颜色插件
// import "tinymce/plugins/textcolor"; //文本颜色插件
// import "tinymce/plugins/print"; //打印
import "tinymce/plugins/preview"; //预览
// import "tinymce/plugins/paste";
import "tinymce/plugins/fullscreen";
// import "tinymce/plugins/searchreplace";
// import "tinymce/plugins/save";
// import "tinymce/plugins/autosave";
// import "tinymce/plugins/imagetools";
// import "tinymce/plugins/table";
// import "tinymce/plugins/codesample";
// import "tinymce/plugins/hr";
// import "tinymce/plugins/emoticons";
// import "tinymce/plugins/anchor";
// import "tinymce/plugins/directionality";
// import "tinymce/plugins/pagebreak";
// import "tinymce/plugins/quickbars";
// import "tinymce/plugins/nonbreaking";
// import "tinymce/plugins/visualblocks";
// import "tinymce/plugins/visualchars";
// import "tinymce/plugins/powerpaste"; //粘贴图片
// import "tinymce/plugins/emoticons/js/emojis";
// import "tinymce/plugins/uploadImg"; //这里需要将插件引入
// import "tinymce/plugins/autoresize"; //编辑器高度自适应,注:plugins里引入此插件时,Init里设置的height将失效
import store from "@/store";
// import api from "@/api/";
import axios from "axios";
// import "tinymce/plugins/powerpaste/";
export default {
  components: {
    Editor
  },
  props: {
    reportIndex: {
      type: Number,
      default: null
    },
    //内容
    value: {
      type: String,
      default: ""
    },
    // 用于区分单个tinymce
    tinymceId: {
      type: String,
      default: "tinymce"
    },
    tid: {
      type: String,
      default: 'my-tinymce-' + new Date().getTime() + parseInt(Math.random(100))
    },
    disabled: {
      type: Boolean,
      default: false
    },
    //插件
    plugins: {
      type: [String, Array],
      default:"fullscreen preview"
        //"preview searchreplace autolink directionality visualblocks visualchars fullscreen image link media template code codesample table charmap hr nonbreaking insertdatetime advlist lists wordcount imagetools textpattern autosave bdmap autoresize lineheight"
    },
    //工具栏
    toolbar: {
      type: [String, Array],
      default: ()=>{ 
        return ['removeformat undo redo | underline strikethrough code bold italic | blockquote | bullist numlist | outdent indent | forecolor | h2 p  media link | alignleft aligncenter alignright | fullscreen preview | fontsizeselect | fontselect | blocks fontfamily fontsize | lineheight']
      } 
    },
    // 隐藏菜单栏
    menubar: {
      type: Boolean,
      default: false
    },
    // 最下方的元素路径和字数统计那一栏是否显示
    statusbar: {
      type: Boolean,
      default: false
    },
    // 元素路径是否显示
    elementpath: {
      type: Boolean,
      default: false
    },
    // 设置为“true”即允许粘贴图像,而将其设置为“false”则不允许粘贴图像。
    paste_data_images: {
      type: Boolean,
      default: true
    },
    // 编辑器宽高是否可变,false-否,true-高可变,'both'-宽高均可,注意引号
    resize: {
      type: [Boolean, String],
      default: "both"
    },
    // 高度
    height: {
      type: [Number, String],
      default: 400
    },
    //  插入word文档需要该属性
    paste_convert_word_fake_lists: {
      type: Boolean,
      default: true
    },
    placeholder: {
      type: String,
      default: "在这里输入内容"
    },
    // 双向绑定并触发change事件
    model: {
      prop: "value",
      event: "tinymcechange"
    },
    // 上传图片地址
    imgUploadUrl: {
      type: String,
      default: ""
    }
  },
  data() {
    return {
      //初始化配置
      myValue: this.value,
      init: {
        selector: "#" + this.tinymceId,
        language_url: "/tinymce/langs/zh-Hans.js", //汉化路径是自定义的,一般放在public或static里面
        language: "zh-Hans",
        skin_url: "/tinymce/skins/ui/oxide", //皮肤:浅色
        // skin_url: "/tinymce/skins/ui/oxide-dark", //皮肤:暗色
        plugins: this.plugins, //插件
        toolbar: this.toolbar,
        // line_height_formats:[1,1.5,2,3,4],
        // font_size_formats: '12px 14px 16px 18px 24px 36px 48px', // 字体大小
        // font_family_formats: '微软雅黑=\'微软雅黑\';宋体=\'宋体\';黑体=\'黑体\';仿宋=\'仿宋\';楷体=\'楷体\';隶书=\'隶书\';幼圆=\'幼圆\';Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;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;sans-serif',
        fontsize_formats: '12pt 14pt 16pt 18pt 24pt 36pt 48pt', // 字体大小
        font_formats: '微软雅黑=\'微软雅黑\';宋体=\'宋体\';黑体=\'黑体\';仿宋=\'仿宋\';楷体=\'楷体\';隶书=\'隶书\';幼圆=\'幼圆\';Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;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;sans-serif',
        plugin_preview_width: 375, // 预览宽度
        plugin_preview_height: 668,
        toolbar_location: "/",
        content_style: "img {max-width:100%;}", // 直接自定义可编辑区域的css样式
        // toolbar_drawer: "sliding",
        menubar: this.menubar, //隐藏菜单栏
        statusbar: this.statusbar, // 最下方的元素路径和字数统计那一栏是否显示
        elementpath: this.elementpath, //元素路径是否显示
        branding: false, // 隐藏品牌标识
        paste_data_images: this.paste_data_images, // 设置为“true”即允许粘贴图像,而将其设置为“false”则不允许粘贴图像。
        resize: this.resize, //编辑器宽高是否可变,false-否,true-高可变,'both'-宽高均可,注意引号
        height: this.height, //高度
        // paste_retain_style_properties: "all",
        // paste_word_valid_elements: "*[*]", //word需要它
        // paste_convert_word_fake_lists: this.paste_convert_word_fake_lists, // 插入word文档需要该属性
        paste_webkit_styles: "all",
        paste_merge_formats: true,
        nonbreaking_force_tab: false,
        paste_auto_cleanup_on_paste: false,
        placeholder: this.placeholder,
        //链接打开方式
        default_link_target: "_blank",
        // 粘贴内容时的回调
        paste_postprocess: (plugin, args) => { 
        },
        // 图片上传三个参数,图片数据,成功时的回调函数,失败时的回调函数
        images_upload_handler: (blobInfo, success, failure) => {
          this.handleImgUpload(blobInfo, success, failure);
        },
        init_instance_callback: editor => {
          if (this.value) {
            editor.setContent(this.value);
          }
          this.hasInit = true;
          editor.on("NodeChange Change KeyUp SetContent", (e) => {
            this.hasChange = true;
            // const val = editor.getContent().replace(/<p><img\s?src="data.*?<\/p>/g, '')
            this.$emit("input", editor.getContent());
          });
 
          editor.on("paste", evt => {
            // 监听粘贴事件
            this.onPaste(evt);
          });
        },
        // 自定义工具栏功能
        setup: function(editor) {
          editor.ui.registry.addButton("myimg", {
            // text: "插入推荐的链接",
            icon: "image",
            onAction: () => {
            }
          });
        }
      },
      uploadTimeOut: null,
      removeFn: null,
      uploadNum:0,// 记录上传次数
      uploadPicList:[] // 图片URL list
    };
  },
 
  mounted() {
    tinymce.init({});
  },
  methods: {
    onPaste(event, success, failure) {
      let self = this;
      const rtf = event.clipboardData.getData('text/rtf');
      // 提取图片信息
      const hexStrings = this.extractImageDataFromRtf(rtf);
      // 获取base64图片数据
      const base64Images = hexStrings.map((hexObj) => {
        return this.convertHexToBase64(hexObj.hex);
      })
      // 调用上传接口
      for (let i = 0; i < base64Images.length; i++) {
        this.picUpload(`data:image/png;base64,${base64Images[i]}`,base64Images.length, i)
      }


    },
    // change触发事件
    tinymcechange(e) {
      this.$emit("eventHandle", {
        type: "change",
        data: e,
        reportIndex: this.reportIndex
      });
    },
    //可以添加一些自己的自定义事件,如清空内容
    clear() {
      this.myValue = "";
    },
    handleImgUpload(blobInfo, success, failure) {
      
    },
     /**
     *
     * rtf中提取图片信息
     * 利用正则从rtf内容中提取到图片的核心信息,得到数组。其中数组中保存的信息有
        {
        type: ‘’, //图片类型
        hex: ‘’ // hex字符串
        }
     * @param {*} rtfData
     */
     extractImageDataFromRtf(rtfData) {
      if (!rtfData) {
        return [];
      }
      const regexPictureHeader = /{\\pict[\s\S]+?({\\\*\\blipuid\s?[\da-fA-F]+)[\s}]*/
      const regexPicture = new RegExp('(?:(' + regexPictureHeader.source + '))([\\da-fA-F\\s]+)\\}', 'g');
      const images = rtfData.match(regexPicture);
      const result = [];
      if (images) {
        for (const image of images) {
          let imageType = false;

          if (image.includes('\\pngblip')) {
            imageType = 'image/png';
          } else if (image.includes('\\jpegblip')) {
            imageType = 'image/jpeg';
          }

          if (imageType) {
            result.push({
              hex: image.replace(regexPictureHeader, '').replace(/[^\da-fA-F]/g, ''),
              type: imageType
            });
          }
        }
      }
      return result;
    },
    // 讲hex格式转化为base64
    convertHexToBase64(hexString) {
      return btoa(hexString.match(/\w{2}/g).map(char => {
        return String.fromCharCode(parseInt(char, 16));
      }).join(''));
    },
    /**
     * data: 图片Base64 编码
     * len : 上传图片数量
     */
     async picUpload(data, len, i) {
      ++this.uploadNum
      let file = this.base64toFile(data);
      var formData = new FormData();
      formData.append('file', file);
      await axios({
        ContentType:'multipart/form-data',
        url: '/api/file/Uploader',
        method: 'post',
        data: formData
      }).then(res => {
        this.uploadPicList.push({ url: res.data.data.url, index: i })
        if (this.uploadNum === len) {
          setTimeout(() => {
            this.setResponseUrl(res.data.data.url)
          },1000)
        }
      })
    },
    setResponseUrl() {
      this.$nextTick(() => {
        // 获取富文本中的所有img标签
        const imgs = tinymce.activeEditor.dom.select("img");
        // 循环所有img标签
        imgs.forEach(async (item, i) => {
          this.uploadPicList.forEach((it) => {
            // 根据对相应顺序 替换url 图片地址
            // if (i === it.index) tinymce.activeEditor.dom.setAttribs(item, { src: this.define.comUrl + it.url });
            if (i === it.index) tinymce.activeEditor.dom.setAttrib(item, { src: this.define.comUrl + it.url });
          })
        }); 
      })
    },
    base64toFile(dataurl, filename = 'file') {
      let arr = dataurl.split(',')
      let mime = arr[0].match(/:(.*?);/)[1]
      let suffix = mime.split('/')[1]
      let bstr = atob(arr[1])
      let n = bstr.length
      let u8arr = new Uint8Array(n)
      while (n--) {
        u8arr[n] = bstr.charCodeAt(n)
      }
      let file = new File([u8arr], `${filename}.${suffix}`, {
        type: mime
      })
      return file
    },
    tinymceEditorId() { 
      return 'tinymce' + Date.parse(new Date())
    }
  },
  watch: {
    //监听内容变化
    value(newValue) {
      this.myValue = newValue == null ? "" : newValue;
    },
    myValue(newValue) {
      if (this.triggerChange) {
        this.$emit("change", newValue);
      } else {
        this.$emit("input", newValue);
      }
    }
  }
};
</script>

  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Tinymce提供了一些配置选项,可以实现从Word中复制粘贴内容并保留格式和图片的功能。具体实现步骤如下: 1. 加载tinymce的源文件和plugins目录下的插件文件。例如: ```html <script src="/path/to/tinymce.min.js"></script> <script src="/path/to/plugins/paste/plugin.min.js"></script> <script src="/path/to/plugins/paste/plugin.min.js"></script> ``` 2. 设置tinymce的选项,包括使用的主题、插件和工具栏等。例如: ```javascript tinymce.init({ selector: 'textarea', plugins: 'paste image', toolbar: 'undo redo | bold italic | image', paste_data_images: true, images_upload_url: '/path/to/upload', images_upload_handler: function (blobInfo, success, failure) { // 处理图片上传 } }); ``` 其中,paste_data_images选项用于启用从Word粘贴图片的功能,images_upload_url和images_upload_handler用于处理上传的图片。 3. 在服务器端处理上传的图片,并返回图片的URL地址。例如,使用PHP实现图片上传功能: ```php <?php if ($_FILES['file']['error'] === UPLOAD_ERR_OK) { $file_tmp = $_FILES['file']['tmp_name']; $file_name = $_FILES['file']['name']; $file_ext = pathinfo($file_name, PATHINFO_EXTENSION); $file_path = '/path/to/images/' . uniqid() . '.' . $file_ext; move_uploaded_file($file_tmp, $file_path); echo json_encode(['location' => $file_path]); } else { echo json_encode(['error' => '上传失败']); } ``` 以上就是使用Tinymce实现从Word中复制粘贴内容并保留格式和图片的方法。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值