首页 > 其他分享 >vue 富文本编辑器 wangeditor 自定义上传图片 以及 解决 复制粘贴 word 没有图片的情况

vue 富文本编辑器 wangeditor 自定义上传图片 以及 解决 复制粘贴 word 没有图片的情况

时间:2023-12-02 15:24:26浏览次数:48  
标签:文本编辑 return 自定义 html let editor 粘贴 图片

本人比较喜欢用这一款编辑器,官方文档:(用于 Vue React | wangEditor),很详细。我主要来说说怎么使用customPaste 自定义粘贴的,怎么解决 复制粘贴 word ,没有图片的情况

主要是关于 wangeditor 在 vue2 的使用

效果图:

CHV)HRXJ[I744C32NX0]NL1.png

先把完整代码放这里:

<template>
  <div class="addpost_course">
    <div id="editor" style="border: 1px solid #ccc; width: 534px">
      <Toolbar
        style="border-bottom: 1px solid #ccc"
        :editor="editor"
        :defaultConfig="toolbarConfig"
        :mode="mode"
      />
      <Editor
        style="height: 200px; overflow-y: hidden"
        v-model="content"
        :defaultConfig="editorConfig"
        :mode="mode"
        @onCreated="onCreated"
        @onChange="onChange"
        @onDestroyed="onDestroyed"
        @onMaxLength="onMaxLength"
        @onFocus="onFocus"
        @onBlur="onBlur"
        @customAlert="customAlert"
        @customPaste="customPaste"
      />
    </div>
  </div>
</template>

<script>
import { Editor, Toolbar } from "@wangeditor/editor-for-vue";
import { Loading, Message } from "element-ui";
export default {
  components: { Editor, Toolbar },
  data() {
    return {
      // 富文本
      editor: null,
      // 富文本里面的所有内容(带标签的)
      content: "",
      toolbarConfig: {},
      editorConfig: {
        placeholder: "请输入内容...",
        // 所有的菜单配置,都要在 MENU_CONF 属性下
        MENU_CONF: {
          //配置上传图片
          uploadImage: {
            // 自定义上传图片 方法
            customUpload: this.uploadImg,
            // 自定义插入图片 方法
            customInsert: this.insertImg,
            //server必须要配置正确,我这里因为上传图片有点特殊,在下面方法配置了,所以此处不用配置地址
            // server: 'https://xwbdzzz.haiyan.gov.cn:10002/form/temp/update/ajax/img',

            maxFileSize: 4 * 1024 * 1024, // 1M
            // 最多可上传几个文件,默认为 100
            maxNumberOfFiles: 100,
            // 选择文件时的类型限制,默认为 ['image/*'] 。如不想限制,则设置为 []
            allowedFileTypes: [],
            // 自定义上传参数,例如传递验证的 token 等。参数会被添加到 formData 中,一起上传到服务端。
            fieldName: "file",
            meta: {
              //官网中把token放到了这里,但是请求的时候会看不到token
            },
            headers: {
              //所以token放这里
              // token: window.sessionStorage.token,
            },
            // 将 meta 拼接到 url 参数中,默认 false
            metaWithUrl: false,
            // 跨域是否传递 cookie ,默认为 false
            withCredentials: false,
            // 超时时间,默认为 10 秒
            timeout: 5 * 1000, // 5 秒

            // 上传之前触发
            // onBeforeUpload(file) {
            //   console.log(file);    // JS 语法
            //     // file 选中的文件,格式如 { key: file }
            //     return file

            //     // 可以 return
            //     // 1. return file 或者 new 一个 file ,接下来将上传
            //     // 2. return false ,不上传这个 file
            // },

            // // 上传进度的回调函数

            // onProgress(progress) {       // JS 语法
            //     // progress 是 0-100 的数字
            //     console.log('progress', progress)
            // },

            // // 单个文件上传成功之后

            // onSuccess(file, res) {          // JS 语法
            //     console.log(`${file.name} 上传成功`, res)
            // },

            // // 单个文件上传失败

            // onFailed(file, res) {           // JS 语法
            //     console.log(`${file.name} 上传失败`, res)
            // },

            // // 上传错误,或者触发 timeout 超时

            // one rror(file, err, res) {               // JS 语法
            //     console.log(`${file.name} 上传出错`, err, res)
            // },
          },
          // 配置上传视频(同上传图片)
          uploadVideo: {},
        },
      },
      mode: "default", // or 'simple'


  methods: {

    // 富文本
    onCreated(editor) {
      this.editor = Object.seal(editor); // 一定要用 Object.seal() ,否则会报错
      console.log(this.editor);
    },
    onChange(editor) {
      console.log("onChange", editor.children, this.content);
    },
    onDestroyed(editor) {
      console.log("onDestroyed", editor);
    },
    onMaxLength(editor) {
      console.log("onMaxLength", editor);
    },
    onFocus(editor) {
      console.log("onFocus", editor);
    },
    onBlur(editor) {
      console.log("onBlur", editor);
    },
    customAlert(info, type) {
      window.alert(`customAlert in Vue demo\n${type}:\n${info}`);
    },

    //重点来了: 自定义粘贴。可阻止编辑器的默认粘贴,实现自己的粘贴逻辑。(可以实现复制粘贴 word ,有图片)
    customPaste(editor, event, callback) {
      console.log("ClipboardEvent 粘贴事件对象", event);
      let html = event.clipboardData.getData("text/html"); // 获取粘贴的 html
      // let text = event.clipboardData.getData('text/plain') // 获取粘贴的纯文本
      let rtf = event.clipboardData.getData("text/rtf"); // 获取 rtf 数据(如从 word wsp 复制粘贴)
      var that = this;
      if (html && rtf) {

        // 列表缩进会超出边框,直接过滤掉
        html = html.replace(/text\-indent:\-(.*?)pt/gi, "");

        // 从html内容中查找粘贴内容中是否有图片元素,并返回img标签的属性src值的集合
        const imgSrcs = that.findAllImgSrcsFromHtml(html);

        // 如果有
        if (imgSrcs && Array.isArray(imgSrcs) && imgSrcs.length) {
          // 从rtf内容中查找图片数据
          const rtfImageData = that.extractImageDataFromRtf(rtf);

          // 如果找到
          if (rtfImageData.length) {
            // TODO:此处可以将图片上传到自己的服务器上

            // 执行替换:将html内容中的img标签的src替换成ref中的图片数据,如果上面上传了则为图片路径
            html = that.replaceImagesFileSourceWithInlineRepresentation(
              html,
              imgSrcs,
              rtfImageData
            );
            editor.dangerouslyInsertHtml(html);
          }
        }

        // 阻止默认的粘贴行为
        event.preventDefault();
        return false;
      } else {
        return true;
      }
    },


    //自定义上传图片
    uploadImg(file, insertFn) {
      let imgData = new FormData();
      console.log(file);
      imgData.append("file", file);
      //调用上传图片接口,上传图片
      this.$api.post("/form/temp/update/ajax/img", imgData).then((res) => {
          console.log(res);
          // 插入后端返回的url
          insertFn(res[0].url);
          this.$message({
            type: "success",
            message: "上传成功",
          });
        })
        .catch((error) => {
          this.$message("上传失败,请重新上传");
        });
    },

    // 自定义插入图片
    insertImg(file) {
      console.log(file);
    },


    /**
     * 从html代码中匹配返回图片标签img的属性src的值的集合
     * @param htmlData
     * @return Array
     */
    findAllImgSrcsFromHtml(htmlData) {
      let imgReg = /<img.*?(?:>|\/>)/gi; //匹配图片中的img标签
      let srcReg = /src=[\'\"]?([^\'\"]*)[\'\"]?/i; // 匹配图片中的src

      let arr = htmlData.match(imgReg); //筛选出所有的img
      if (!arr || (Array.isArray(arr) && !arr.length)) {
        return false;
      }

      let srcArr = [];
      for (let i = 0; i < arr.length; i++) {
        let src = arr[i].match(srcReg);
        // 获取图片地址
        srcArr.push(src[1]);
      }

      return srcArr;
    },
    /**
     * 从rtf内容中匹配返回图片数据的集合
     * @param rtfData
     * @return Array
     */
    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;
    },
    /**
     * 将html内容中img标签的属性值替换
     * @param htmlData html内容
     * @param imageSrcs html中img的属性src的值的集合
     * @param imagesHexSources rtf中图片数据的集合,与html内容中的img标签对应
     * @param isBase64Data 是否是Base64的图片数据
     * @return String
     */
    replaceImagesFileSourceWithInlineRepresentation(
      htmlData,
      imageSrcs,
      imagesHexSources,
      isBase64Data = true
    ) {
      if (imageSrcs.length === imagesHexSources.length) {
        for (let i = 0; i < imageSrcs.length; i++) {
          const newSrc = isBase64Data
            ? `data:${
                imagesHexSources[i].type
              };base64,${this._convertHexToBase64(imagesHexSources[i].hex)}`
            : imagesHexSources[i];

          htmlData = htmlData.replace(imageSrcs[i], newSrc);
        }
      }

      return htmlData;
    },

    /**
     * 十六进制转base64
     */
    _convertHexToBase64(hexString) {
      return btoa(
        hexString
          .match(/\w{2}/g)
          .map((char) => {
            return String.fromCharCode(parseInt(char, 16));
          })
          .join("")
      );
    },



  // 销毁富文本
  beforeDestroy() {
    const editor = this.editor;
    if (editor == null) return;
    editor.destroy(); // 组件销毁时,及时销毁编辑器
  },
};
</script>

//记得引入wangeditor样式
<style src="@wangeditor/editor/dist/css/style.css"></style>
<style lang="scss" scoped>
@import "../mystyle/addpost_course";
</style>

上传图片我这里有点特殊,就不细说了

自定义粘贴:就几点:

  • 实现方法:通过 wangEditor 的编辑器配置 API 中的  customPaste 自定义粘贴
  • 实现步骤: 注意获取粘贴的 html 和获取 rtf 数据 ,用 let,不能用 const,下面会发生修改 -特别注意:允许默认粘贴行为 既可以复制图片,也可以复制文本,还可以图片文本;阻止默认的粘贴行为,只能粘贴复制文字带图片的文本
// 自定义粘贴。可阻止编辑器的默认粘贴,实现自己的粘贴逻辑。(可以实现复制粘贴 word ,有图片)
    customPaste(editor, event, callback) {
      console.log("ClipboardEvent 粘贴事件对象", event);
      let html = event.clipboardData.getData("text/html"); // 获取粘贴的 html,
      // let text = event.clipboardData.getData('text/plain') // 获取粘贴的纯文本
      let rtf = event.clipboardData.getData("text/rtf"); // 获取 rtf 数据(如从 word wsp 复制粘贴)

      var that = this;

      if (html && rtf) {
        // 该条件分支即表示要自定义word粘贴

        // 列表缩进会超出边框,直接过滤掉
        html = html.replace(/text\-indent:\-(.*?)pt/gi, "");

        // 从html内容中查找粘贴内容中是否有图片元素,并返回img标签的属性src值的集合
        const imgSrcs = that.findAllImgSrcsFromHtml(html);

        // 如果有
        if (imgSrcs && Array.isArray(imgSrcs) && imgSrcs.length) {
          // 从rtf内容中查找图片数据
          const rtfImageData = that.extractImageDataFromRtf(rtf);

          // 如果找到
          if (rtfImageData.length) {
            // TODO:此处可以将图片上传到自己的服务器上

            // 执行替换:将html内容中的img标签的src替换成ref中的图片数据,如果上面上传了则为图片路径
            html = that.replaceImagesFileSourceWithInlineRepresentation(
              html,
              imgSrcs,
              rtfImageData
            );
            editor.dangerouslyInsertHtml(html);
          }
        }

        // 阻止默认的粘贴行为 //此处非常关键 允许默认行为 既可以复制图片,也可以复制文本,还可以图片文本
        // event.preventDefault();
        //event.preventDefault();
        return false;
      } else {
        return true;
      }
    },
  • 以上实现步骤涉及到几个方法:
/**
     * 从html代码中匹配返回图片标签img的属性src的值的集合
     * @param htmlData
     * @return Array
     */
    findAllImgSrcsFromHtml(htmlData) {
      let imgReg = /<img.*?(?:>|\/>)/gi; //匹配图片中的img标签
      let srcReg = /src=[\'\"]?([^\'\"]*)[\'\"]?/i; // 匹配图片中的src

      let arr = htmlData.match(imgReg); //筛选出所有的img
      if (!arr || (Array.isArray(arr) && !arr.length)) {
        return false;
      }

      let srcArr = [];
      for (let i = 0; i < arr.length; i++) {
        let src = arr[i].match(srcReg);
        // 获取图片地址
        srcArr.push(src[1]);
      }

      return srcArr;
    },
/**
     * 从rtf内容中匹配返回图片数据的集合
     * @param rtfData
     * @return Array
     */
    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;
    },
 /**
     * 将html内容中img标签的属性值替换
     * @param htmlData html内容
     * @param imageSrcs html中img的属性src的值的集合
     * @param imagesHexSources rtf中图片数据的集合,与html内容中的img标签对应
     * @param isBase64Data 是否是Base64的图片数据
     * @return String
     */
    replaceImagesFileSourceWithInlineRepresentation(
      htmlData,
      imageSrcs,
      imagesHexSources,
      isBase64Data = true
    ) {
      if (imageSrcs.length === imagesHexSources.length) {
        for (let i = 0; i < imageSrcs.length; i++) {
          const newSrc = isBase64Data
            ? `data:${
                imagesHexSources[i].type
              };base64,${this._convertHexToBase64(imagesHexSources[i].hex)}`
            : imagesHexSources[i];

          htmlData = htmlData.replace(imageSrcs[i], newSrc);
        }
      }

      return htmlData;
    },
/**
     * 十六进制转base64
     */
    _convertHexToBase64(hexString) {
      return btoa(
        hexString
          .match(/\w{2}/g)
          .map((char) => {
            return String.fromCharCode(parseInt(char, 16));
          })
          .join("")
      );
    },

原理:可以去看看他的文章,不错 ( wangEditor 粘贴从 word 复制的带图片内容的最佳实践_ wangeditor 粘贴 word 图片)

标签:文本编辑,return,自定义,html,let,editor,粘贴,图片
From: https://www.cnblogs.com/wp-leonard/p/17871636.html

相关文章

  • wangeditor 富文本 使用及 上传本地图片的方法
    文章标题:Vue组件实现富文本编辑器文章摘要:本文介绍了如何使用Vue和Wangeditor插件实现富文本编辑器组件,并详细解释了组件中的各个部分和功能。Vue组件实现富文本编辑器在Web开发中,富文本编辑器是一个非常常见的功能,它能够让用户以所见即所得的方式编辑和排版文本内容。......
  • vue3使用富文本编辑器wangEditor 5,增加自定义下拉框,并动态改变下拉框内容
    官方资料wangEditor官网效果展示准备工作这里按照wangEditor官网提供的Vue3Demo操作就行,下面的内容可以直接跳过安装yarnadd@wangeditor/editor#或者npminstall@wangeditor/editor--saveyarnadd@wangeditor/editor-for-vue@next#或者npminstall@w......
  • SpringBoot自定义注解导出Excel
    先定义一个注解importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;@Retention(RetentionPolicy.RUNTIME)public@interfaceExcelHander{Stringvalue()default"";StringlinkFiled()default"";Cel......
  • Linux 下使用命令将图片反色
    #单张图片反色convert-negateimage.pngimage_ne.png#单张图片反色(替换)convert-negateimage.pngimage.png#单张图片反色,修复格式不兼容convertimage.pngimage.png&&convert-negateimage.pngimage_ne.png#单张图片反色,修复格式不兼容(替换)convertimage.p......
  • 推荐一个免费的图片素材管理软件
    推荐一个免费的图片素材管理软件Billfish。网址: Billfish素材管家--创意设计必备素材管理工具......
  • 记一个Python脚本--将webp图片转jpg格式
    什么是WebP图片格式?如何在线转换WebP格式?我们有时候从互联网上下载图片会发现图片是WebP格式而不是常见的JPEG或者是PNG格式,用自带的图片处理软件无法打开,那么什么是WebP格式呢?我们该如何打开WebP格式的图片文件?需要进行图片处理的时候怎么样才能在线转换WebP格式为常用的JPG格式......
  • 有什么可以自动保存微信收到的图片和视频的方法么
    8-1在一些有外勤工作的公司里,经常会需要在外面工作的同事把工作情况的图片发到指定微信或者指定的微信群里,以记录工作进展等,或者打卡等,对于外勤人员来说,也就发个图片的事,但是对于在公司里收图片的人来说,可能是个麻烦的活。因为有可能每天要保存上千张图片,或者视频,要是都靠人工保存......
  • 在Unity中模块化管理自定义功能和资源
    之前在做Unity项目时,有时会遇到多个项目共用同一部分代码或资源的情况。而当被共用的部分需要更新的时候,手动复制替换非常麻烦,并且可能会有遗漏。对于这个问题,一个很好的解决办法是将可复用的文件打包为自定义包(CustomPackage),使用git等版本控制工具来管理每个包的内容。什么是Pa......
  • 1、自定义上传组件实现动态指定action
    1、增加ynamicAction:String2、修改constuploadImgUrl=ref(props.dynamicAction||import.meta.env.VITE_APP_BASE_API+"/common/upload");//上传的图片服务器地址<el-uploadmultiple:action="uploadImgUrl"3、父组件<el-form-itemlab......
  • Golang中如何自定义时间类型进行xml、json的序列化/反序列化
    在日常开发工作中,我们进行会遇到将struct序列化json字符串以及将json字符串反序列化为struct的场景,大家也对此十分熟悉。最近工作中,遇到了需要将struct序列化xml字符串以及将xml字符串反序列化为struct的场景,对于普通类型的字段,比如int、string等类型,直接......