vue 富文本图片上传处理

此处以 vue-quill-editor 进行示范,其他富文本大体都差不多
可以粘贴直接从文件夹里复制出来的图片,也可以粘贴截图;


import { quillEditor, Quill } from "vue-quill-editor";
import { container, ImageExtend, QuillWatch } from 'quill-image-extend-module'
Quill.register('modules/ImageExtend', ImageExtend)

export default {
  components: {
    quillEditor,
  },
  data () {
    return {
      content: '',
      editorOption: {
        placeholder: "请在这里输入",
        modules: {
          toolbar: {
            container: container,
            handlers: {
              'image': function (value) {
                if (value) {
                  // 绑定上传图片按钮  自带的图片上传,我没用过,应该问题不大
                 document.getElementById('enclosureInput').children[0].children[0].children[0].click()
                } else {
                  this.quill.format('image', false)
                }
              }
            }
          }
        }
      },
      oldIndex: 0, // 记录光标位置
      imgArr: [], // 用来存放图片的 base64 字符串、图片上传后的 url 和 图片上传状态
      subDisabled: false, // 判断是否可以提交
    }
  },
  methods: {
    //组件的change事件 非重点,不做介绍
    imgChange (fileList) {
    },
    onEditorReady (editor) {
    },
    onEditorBlur () {

    },
    // 失去焦点事件
    onEditorFocus () { console.log('获得焦点'); }, // 获得焦点事件
    // 内容改变事件
    onEditorChange () {
      this.oldIndex = this.$refs.myQuillEditor.quill.selection.savedRange.index;
    },
    pasteListener () {
      // 清空数组,防止脏数据
      this.imgArr.length = 0;
      // 监听粘贴事件
      document.getElementsByClassName('quill-editor')[0].removeEventListener("paste", this.listenerFn);
      document.getElementsByClassName('quill-editor')[0].addEventListener('paste', this.listenerFn);
    },
    // 取消监听事件
    removeListener () { document.getElementsByClassName('quill-editor')[0].removeEventListener("paste", this.listenerFn) },

    listenerFn (e) {
      let quill = this.$refs.myQuillEditor.quill;
      // 获取光标初始位置
      this.oldIndex = quill.selection.savedRange.index;
      if (!(e.clipboardData && e.clipboardData.items)) return;
      e.clipboardData.items.forEach(async item => {
        // 字符串
        if (item.kind == 'string') item.getAsString(str => { console.log(str); })
        else if (item.kind == 'file') {
          // 获取文件
          let file = await item.getAsFile();
          // 对指定格式的图片进行操作
          if (/\.(jpg|jpeg|png|GIF|JPG|PNG)$/.test(file.name)) {
            this.translateBlobToBase64(file, async baseStr => {

              // 判断,如果是直接粘贴的文件,光标不会移动,需要手动往里面塞
              if (this.oldIndex == quill.selection.savedRange.index) {
                quill.insertEmbed(this.oldIndex, 'image', baseStr);
                // 光标 +1
                quill.setSelection(this.oldIndex + 1);
              }

              // 将base64转换为文件后,不同的图片放到数组中,然后进行上传操作;
              // 通过上面的塞值操作之后,没粘贴一张图片,富文本中都会有对应的 base64 字符串去,通过字符串切割成数组的方式,判断是否有粘贴相同的图片,相同的图片 base64 编码是一样的,这样只需要上传一次即可
              if (this.content.indexOf(baseStr) == -1 || (((this.content.split(baseStr)).length - 1) == 1)) {
                let imgObj = {
                  baseStr, // base64 字符串,用于匹配和替换
                  state: false, // 上传状态,用来判断图片是否上传成功
                  url: undefined, // 图片上传成功后的地址
                }
                this.subDisabled = true;
                // 图片上传,此处前端直接传 阿里oss,不同需求单独处理
                let rsp = await uploadOSS({ file }, this.$refs.ossUpload.Aliyun, 'img');
                if (rsp) {
                  // 上传成功之后,更改状态,并放到 imgArr 数组中,再遍历该数组,判断是否所有图片上传状态;
                  let obj = this.$refs.ossUpload.transformObj(rsp);
                  imgObj.state = true;
                  imgObj.url = obj.url;

                  this.imgArr.push(imgObj);
                  this.subDisabled = false;
                  this.imgArr.forEach(item => {
                    if (!item.state) this.subDisabled = true;
                  })
                }
              }
              // 重新获取光标位置
              this.oldIndex = quill.selection.savedRange.index;
            })
          }
        }
      })
    },
    // 将base64 全部替换成 url 
    // 此方法在提交数据之前执行
    imgChaec () {
      if (this.imgArr.length > 0) this.imgArr.forEach(item => this.content = this.content.replaceAll(item.baseStr, item.url));
    },
  }
}

总结:

  1. 使用了发布订阅者的设计思想,每个图片单独处理,互不影响;
  2. 可以粘贴文件夹里复制的图片;
  3. 传给后端的图片时 url 连接,减小数据库字段长度;
  4. 粘贴图片的时候,如果是截取的图片,组件自己会将截取的图片的 base64 编码放到组件数据中,因为数据不好判断,也不好获取数据中的 base64 编码,所以也手动根据 file 对象重新生成了 base64 编码,后续可以优化;
  5. 图片上传失败没有做处理,后续有时间优化;

你可能感兴趣的:(vue 富文本图片上传处理)