Skip to content

使用 vue2-editor 遇到的问题

自定义图片上传

vue2-editor 是个不错的富文本编辑器,但是美中不足的是他把图片全部作为 BASE64 上传了,但我们一般是存 URL,这有利于数据库优化和异步加载,所以才自己包装了一下。

关于文件异步上传,可以看我另一篇文章:https://wolfx.cn/html5-file-api/

包装如下:

html
<template>
  <div style="position: relative;">
    <vue-editor
      ref="editor"
      :editor-toolbar="customToolbar"
      :value="value"
      @input="onChange"
      :useCustomImageHandler="true"
      @image-added="handleImageAdded"
    ></vue-editor>
    <span v-if="limit > 0" class="count-div"> {{ count }}/{{ limit }} </span>
  </div>
</template>
typescript
{
  /**
    * 自定义图片上传
    * @param file
    * @param Editor
    * @param cursorLocation
    * @param resetUploader
    */
  handleImageAdded(file: File, Editor: Quill, cursorLocation: number, resetUploader: Function) {
    if (!this.action) {
      console.error("action 未设置");
      return;
    }

    uploadAsync(file, {
      uploadProgress: this.uploadProgress,
      uploadComplete: (event: ProgressEvent<any>) => {
        try {
          const res = JSON.parse(event.target.responseText);
          if (res.code === 200) {
            if (Array.isArray(res.data) && res.data.length > 0) {
              res.data.forEach((img: string) => {
                Editor.insertEmbed(cursorLocation, "image", img);
              });
            } else if (typeof res.data === "string") {
              Editor.insertEmbed(cursorLocation, "image", res.data);
            }
            resetUploader();
          } else {
            console.error(res.msg);
          }
        } catch (e) {
          console.error("JSON解析异常");
        }
      },
      uploadFailed: this.uploadFailed,
      uploadCanceled: this.uploadCanceled,
    });
  },
}

参考文档:https://www.vue2editor.com/examples/#custom-image-handler

自定义插入元素(例子支持 section 元素的插入)

typescript
import Quill from "quill";

const Size = Quill.import("attributors/style/size");
const BlockEmbed = Quill.import("blots/block/embed");
export const fontSizeList = ["12px", "14px", "16px", "18px", "20px", "22px", "24px", "26px", "28px", "30px"];
Size.whitelist = fontSizeList;
Quill.register(Size, true);

// 自定义插入元素
class SectionEmbed extends BlockEmbed {
  static create(value: string) {
    const node = super.create(value);
    node.setAttribute("contenteditable", "false");
    node.setAttribute("width", "100%");
    //   设置自定义html
    node.innerHTML = this.transformValue(value);
    return node;
  }

  static transformValue(value: string) {
    let handleArr = value.split("\n");
    handleArr = handleArr.map((e) => e.replace(/^[\s]+/, "").replace(/[\s]+$/, ""));
    return handleArr.join("");
  }

  // 返回节点自身的value值 用于撤销操作
  static value(node: Element) {
    return node.innerHTML;
  }
}
// blotName
SectionEmbed.blotName = "Section";
// class名将用于匹配blot名称
SectionEmbed.className = "embed-section";
// 标签类型自定义
SectionEmbed.tagName = "section";
Quill.register(SectionEmbed, true);

自定义支持 CTRL+V 插入图片

typescript
{
  mounted() {
    const $editor: any = this.$refs.editor;
    this.handleImagePaste($editor.quill);
    if (this.limit) {
      this.applyLimit();
    }
  },

  /**
    * 自定义粘贴图片功能
    * @param quill
    */
  handleImagePaste(quill: Quill) {
    quill.root.addEventListener(
      "paste",
      (evt: ClipboardEvent) => {
        if (evt.clipboardData && evt.clipboardData.files && evt.clipboardData.files.length) {
          evt.preventDefault();
          for (let i = 0; i < evt.clipboardData.files.length; i++) {
            const file = evt.clipboardData.files[i];
            if (!file.type.match(/^image\/(gif|jpe?g|a?png|bmp)/i)) {
              return;
            }
            this.handleImageAdded(file, quill, quill.getSelection()?.index || 0, () => {});
          }
        }
      },
      false,
    );
  },

}

最后编辑时间:

Version 4.2 (core-1.3.4)