wangEditor 富文本详解

前言:wangEditor 官网 。本文档讲解 wangEditor 在 vue3 中的使用。

一:快速开始

1. 安装

需要安装 @wangeditor/editor、@wangeditor/editor-for-vue@next 两个依赖

# 安装 editor
npm install @wangeditor/editor
# or
yarn add @wangeditor/editor
# or
pnpm add @wangeditor/editor

# 安装 Vue3 组件
npm install @wangeditor/editor-for-vue@next
# or
yarn add @wangeditor/editor-for-vue@next
# or
pnpm add @wangeditor/editor-for-vue@next

2. 构建组件 components/Rich.vue

<script setup lang="ts">
  import "@wangeditor/editor/dist/css/style.css"; // 引入 css
  import { onBeforeUnmount, ref, shallowRef, computed } from "vue";
  import { Editor, Toolbar } from "@wangeditor/editor-for-vue";

  const props = defineProps({
    modelValue: {
      type: String,
      required: true,
    },
    toolbarConfig: {
      type: Object,
      default: {},
    },
    editorConfig: {
      type: Object,
      default: {
        placeholder: "请输入内容...",
      },
    },
    height: {
      type: String,
      default: "300px",
    },
  });

  const emits = defineEmits(["update:modelValue"]);
  let valueHtml = computed({
    get() {
      return props.modelValue;
    },
    set(value) {
      emits("update:modelValue", value);
    },
  });

  let style: any = computed(() => {
    return {
      height: props.height,
      "overflow-y": "hidden",
    };
  });

  // 编辑器实例,必须用 shallowRef
  const editorRef = shallowRef();

  // 模式
  let mode = ref("default");

  // 组件销毁时,也及时销毁编辑器
  onBeforeUnmount(() => {
    const editor = editorRef.value;
    if (editor == null) return;
    editor.destroy();
  });

  const handleCreated = (editor: any) => {
    editorRef.value = editor; // 记录 editor 实例,重要!
  };
</script>

<template>
  <div class="editor-content-view">
    <Toolbar
      style="border-bottom: 1px solid #ccc"
      :editor="editorRef"
      :defaultConfig="toolbarConfig"
      :mode="mode"
    />
    <Editor
      :style="style"
      v-model="valueHtml"
      :defaultConfig="editorConfig"
      :mode="mode"
      @onCreated="handleCreated"
    />
  </div>
</template>

<style lang="scss" scoped>
  .editor-content-view {
    border: 1px solid #ccc;
    z-index: 999;
  }
</style>

3. 使用 Rich.vue 组件

<script setup lang="ts">
  import { ref } from "vue";
  import Rich from "@/components/Rich.vue";

  let richHtml = ref("");
</script>

<template>
  <div class="page-rich">
    <Rich v-model="richHtml" />
  </div>
</template>

<style lang="scss" scoped></style>

二:优势

使用 slate.js(但不依赖 React)为内核、强稳定性、高扩展性、有详细中文文档、可直接使用无需二次开发、不依赖框架。

三:基础

1. 定义 CSS

可自定义编辑器、工具栏的尺寸、边框、z-index 等样式。

<template>
  <!-- 边框 -->
  <div class="editor-content-view">
    <!-- 工具栏 -->
    <Toolbar style="border-bottom: 1px solid #ccc" />
    <!-- 编译器 -->
    <Editor style="height: 500px; overflow-y: hidden" />
  </div>
</template>

<style lang="scss" scoped>
  .editor-content-view {
    border: 1px solid #ccc;
    z-index: 999;
  }
</style>

2. 定义 HTML

如果想要“全屏”功能,则要求工具栏、编辑器 DOM 节点必须是同一层级,同时父级盒子设置 z-index: 999。

<template>
  <div class="editor-content-view">
    <!-- 工具栏 -->
    <Toolbar />
    <!-- 编译器 -->
    <Editor />
  </div>
</template>

<style lang="scss" scoped>
  .editor-content-view {
    z-index: 999;
  }
</style>

3. 模式

通过设置 mode 改变富文本模式,'default' 默认模式,基础所有功能。'simple' 简洁模式,仅有常用功能。

<script setup lang="ts">
  // 模式
  let mode = ref("default");
</script>

<template>
  <div class="editor-content-view">
    <Toolbar :mode="mode" />
    <Editor :mode="mode" />
  </div>
</template>

四:在 vue3 中使用

1. 配置

可通过 toolbarConfig editorConfig 来修改菜单栏和编辑器的配置。注意:编辑器配置中 onXxx 格式的生命周期函数,必须通过 Vue 事件来传递,不可以放在 editorConfig 中

<script setup lang="ts">
  const props = defineProps({
    toolbarConfig: {
      type: Object,
      default: {},
    },
    editorConfig: {
      type: Object,
      default: {
        placeholder: "请输入内容...",
      },
    },
  });

  const handleCreated = (editor: any) => {
    editorRef.value = editor; // 记录 editor 实例,重要!
  };
</script>

<template>
  <div class="editor-content-view">
    <Toolbar :defaultConfig="toolbarConfig" />
    <Editor :defaultConfig="editorConfig" @onCreated="handleCreated" />
  </div>
</template>

<style lang="scss" scoped>
  .editor-content-view {
    border: 1px solid #ccc;
    z-index: 999;
  }
</style>

2. 调用 API

当编辑器渲染完成之后,通过 editorRef.value 获取 editor 实例,即可调用它的 API 。

// 组件销毁时,也及时销毁编辑器
onBeforeUnmount(() => {
  const editor = editorRef.value;
  if (editor == null) return;
  editor.destroy();
});

五:配置和 API

下面代码例子,都是以 components/Rich.vue 作为例子的基础上扩展功能。

1. 工具栏配置

1.1 getConfig

通过 toolbar.getConfig() 查看工具栏的默认配置

import { DomEditor } from "@wangeditor/editor";

const handleCreated = (editor) => {
  const toolbar = DomEditor.getToolbar(editor);
  const result = toolbar.getConfig();
  console.log(result);
};

1.2 toolbarKeys

重新配置工具栏,显示哪些菜单,以及菜单的排序、分组。可以通过 toolbar.getConfig().toolbarKeys 查看当前的默认配置。

<script setup lang="ts">
  import { ref } from "vue";
  import Rich from "@/components/Rich.vue";

  let richHtml = ref("asdf");
  let toolbarConfig = {
    toolbarKeys: [
      // 菜单 key
      "headerSelect",

      // 分割线
      "|",

      // 菜单 key
      "bold",
      "italic",
    ],
    insertKeys: {
      index: 1, // 插入的位置,基于当前的 toolbarKeys
      keys: ["color", "bgColor"],
    },
  };
</script>

<template>
  <div class="page-rich">
    <Rich v-model="richHtml" :toolbarConfig="toolbarConfig" />
  </div>
</template>

1.3 insertKeys

在当前 toolbarKeys 的基础上继续插入新菜单,如自定义扩展的菜单。

<script setup lang="ts">
  import { ref } from "vue";
  import Rich from "@/components/Rich.vue";

  let richHtml = ref("asdf");
  let toolbarConfig = {
    toolbarKeys: [
      // 菜单 key
      "headerSelect",

      // 分割线
      "|",

      // 菜单 key
      "bold",
      "italic",
    ],
    insertKeys: {
      index: 1, // 插入的位置,基于当前的 toolbarKeys
      keys: ["color", "bgColor"],
    },
  };
</script>

<template>
  <div class="page-rich">
    <Rich v-model="richHtml" :toolbarConfig="toolbarConfig" />
  </div>
</template>

1.4 excludeKeys

排除掉某些菜单

<script setup lang="ts">
  import { ref } from "vue";
  import Rich from "@/components/Rich.vue";

  let richHtml = ref("asdf");
  let toolbarConfig = {
    toolbarKeys: [
      // 菜单 key
      "headerSelect",

      // 分割线
      "|",

      // 菜单 key
      "bold",
      "italic",
    ],
    insertKeys: {
      index: 1, // 插入的位置,基于当前的 toolbarKeys
      keys: ["color", "bgColor"],
    },
    excludeKeys: ["headerSelect"],
  };
</script>

<template>
  <div class="page-rich">
    <Rich v-model="richHtml" :toolbarConfig="toolbarConfig" />
  </div>
</template>

2. 编辑器配置

2.1 getConfig

通过 editor.getConfig() 查看工具栏的默认配置

const handleCreated = (editor) => {
  const result = editor.getConfig();
  console.log(result);
};

2.2 editorConfig 配置

<script setup lang="ts">
  import { ref } from "vue";
  import Rich from "@/components/Rich.vue";

  let richHtml = ref("");
  let editorConfig = ref({
    placeholder: "请输入",
    readOnly: false, // 是否只读,默认 false
    autoFocus: true, // 是否focus, 默认 true
    scroll: true, // 是否支持滚动,默认true。不要固定 editor-container 的高度,设置一个 min-height 即可。
    maxLength: 20, // 最高内容长度,onMaxLength 当达到限制时,触发函数
  });
</script>

<template>
  <div class="page-rich">
    <Rich v-model="richHtml" :editorConfig="editorConfig" />
  </div>
</template>

2.3 编译器方法

<script setup lang="ts">
  // 编辑器创建完毕时的回调函数。
  const handleCreated = (editor) => {
    editorRef.value = editor;
    console.log("created", editor);
  };
  // 编辑器内容、选区变化时的回调函数。
  const handleChange = (editor) => {
    console.log("change:", editor.children);
  };
  // 编辑器销毁时的回调函数。调用 editor.destroy() 即可销毁编辑器
  const handleDestroyed = (editor) => {
    console.log("destroyed", editor);
  };
  // 编辑器 focus 时的回调函数。
  const handleFocus = (editor) => {
    console.log("focus", editor);
  };
  // 编辑器 blur 时的回调函数。
  const handleBlur = (editor) => {
    console.log("blur", editor);
  };
  // 自定义编辑器 alert 。
  const customAlert = (info, type) => {
    alert(`【自定义提示】${type} - ${info}`);
  };
  // 自定义粘贴。可阻止编辑器的默认粘贴,实现自己的粘贴逻辑。
  const customPaste = (editor, event, callback) => {
    console.log("ClipboardEvent 粘贴事件对象", event);
    // const html = event.clipboardData.getData('text/html') // 获取粘贴的 html
    // const text = event.clipboardData.getData('text/plain') // 获取粘贴的纯文本
    // const rtf = event.clipboardData.getData('text/rtf') // 获取 rtf 数据(如从 word wsp 复制粘贴)

    // 自定义插入内容
    editor.insertText("xxx");

    // 返回 false ,阻止默认粘贴行为
    event.preventDefault();
    callback(false); // 返回值(注意,vue 事件的返回值,不能用 return)

    // 返回 true ,继续默认的粘贴行为
    // callback(true)
  };
</script>

<template>
  <div class="editor-content-view">
    <Toolbar />
    <Editor
      @onCreated="handleCreated"
      @onChange="handleChange"
      @onDestroyed="handleDestroyed"
      @onFocus="handleFocus"
      @onBlur="handleBlur"
      @customAlert="customAlert"
      @customPaste="customPaste"
    />
  </div>
</template>

3. 菜单配置

各个菜单项的详细配置。

3.1 通用方法

要配置哪个菜单,首先要知道这个菜单的 key 。执行 editor.getAllMenuKeys() 可获取编辑器所有菜单,从中找到自己想要的菜单 key 即可。

const handleCreated = (editor) => {
  const result = editor.getAllMenuKeys();
  console.log(result);
};

找到菜单 key 之后,可以先看看菜单的当前配置,再自行修改。

const handleCreated = (editor) => {
  const result = editor.getMenuConfig("uploadImage"); // 获取 uploadImage 的当前配置
  console.log(result);
};

3.2 颜色

<script setup lang="ts">
  import { ref } from "vue";
  import Rich from "@/components/Rich.vue";

  let richHtml = ref("");
  let editorConfig = ref({
    MENU_CONF: {
      color: {
        colors: ["#000", "#333", "#666"],
      },
    },
  });
</script>

<template>
  <div class="page-rich">
    <Rich v-model="richHtml" :editorConfig="editorConfig" />
  </div>
</template>

3.3 字号

<script setup lang="ts">
  import { ref } from "vue";
  import Rich from "@/components/Rich.vue";

  let richHtml = ref("");
  let editorConfig = ref({
    MENU_CONF: {
      fontSize: {
        fontSizeList: [
          // 元素支持两种形式
          //   1. 字符串;
          //   2. { name: 'xxx', value: 'xxx' }

          "12px",
          "16px",
          { name: "24px", value: "24px" },
          "40px",
        ],
      },
    },
  });
</script>

<template>
  <div class="page-rich">
    <Rich v-model="richHtml" :editorConfig="editorConfig" />
  </div>
</template>

3.4 字体

<script setup lang="ts">
  import { ref } from "vue";
  import Rich from "@/components/Rich.vue";

  let richHtml = ref("");
  let editorConfig = ref({
    MENU_CONF: {
      fontFamily: {
        fontFamilyList: [
          // 元素支持两种形式
          //   1. 字符串;
          //   2. { name: 'xxx', value: 'xxx' }

          "黑体",
          "楷体",
          { name: "仿宋", value: "仿宋" },
          "Arial",
          "Tahoma",
          "Verdana",
        ],
      },
    },
  });
</script>

<template>
  <div class="page-rich">
    <Rich v-model="richHtml" :editorConfig="editorConfig" />
  </div>
</template>

3.5 行高

<script setup lang="ts">
  import { ref } from "vue";
  import Rich from "@/components/Rich.vue";

  let richHtml = ref("");
  let editorConfig = ref({
    MENU_CONF: {
      lineHeight: {
        lineHeightList: ["1", "1.5", "2", "2.5"],
      },
    },
  });
</script>

<template>
  <div class="page-rich">
    <Rich v-model="richHtml" :editorConfig="editorConfig" />
  </div>
</template>

3.6 表情

<script setup lang="ts">
  import { ref } from "vue";
  import Rich from "@/components/Rich.vue";

  let richHtml = ref("");
  let editorConfig = ref({
    MENU_CONF: {
      emotion: {
        emotions: "😀 😃 😄 😁 😆 😅 😂 🤣 😊 😇 🙂 🙃 😉".split(" "),
      },
    },
  });
</script>

<template>
  <div class="page-rich">
    <Rich v-model="richHtml" :editorConfig="editorConfig" />
  </div>
</template>

3.7 链接

checkLink 校验链接、parseLinkUrl 转换链接 url

<script setup lang="ts">
  import { ref } from "vue";
  import Rich from "@/components/Rich.vue";

  let richHtml = ref("");

  // 自定义校验链接
  const customCheckLinkFn = (text, url) => {
    if (!url) {
      return;
    }
    if (url.indexOf("http") !== 0) {
      return "链接必须以 http/https 开头";
    }
    return true;

    // 返回值有三种选择:
    // 1. 返回 true ,说明检查通过,编辑器将正常插入链接
    // 2. 返回一个字符串,说明检查未通过,编辑器会阻止插入。会 alert 出错误信息(即返回的字符串)
    // 3. 返回 undefined(即没有任何返回),说明检查未通过,编辑器会阻止插入。但不会提示任何信息
  };

  // 自定义转换链接 url
  const customParseLinkUrl = (url) => {
    if (url.indexOf("http") !== 0) {
      return `http://${url}`;
    }
    return url;
  };

  let editorConfig = ref({
    MENU_CONF: {
      insertLink: {
        checkLink: customCheckLinkFn, // 也支持 async 函数
        parseLinkUrl: customParseLinkUrl, // 也支持 async 函数
      },
    },
  });
</script>

<template>
  <div class="page-rich">
    <Rich v-model="richHtml" :editorConfig="editorConfig" />
  </div>
</template>

3.8 图片

<script setup lang="ts">
  import { ref } from "vue";
  import Rich from "@/components/Rich.vue";

  let richHtml = ref("");

  // 自定义校验图片
  const customCheckImageFn = (src, alt, url) => {
    if (!src) {
      return;
    }
    if (src.indexOf("http") !== 0) {
      return "图片网址必须以 http/https 开头";
    }
    return true;

    // 返回值有三种选择:
    // 1. 返回 true ,说明检查通过,编辑器将正常插入图片
    // 2. 返回一个字符串,说明检查未通过,编辑器会阻止插入。会 alert 出错误信息(即返回的字符串)
    // 3. 返回 undefined(即没有任何返回),说明检查未通过,编辑器会阻止插入。但不会提示任何信息
  };

  // 转换图片链接
  const customParseImageSrc = (src) => {
    if (src.indexOf("http") !== 0) {
      return `http://${src}`;
    }
    return src;
  };

  let editorConfig = ref({
    MENU_CONF: {
      // 插入图片
      insertImage: {
        onInsertedImage(imageNode) {
          if (imageNode == null) return;

          const { src, alt, url, href } = imageNode;
          console.log("inserted image", src, alt, url, href);
        },
        checkImage: customCheckImageFn, // 也支持 async 函数
        parseImageSrc: customParseImageSrc, // 也支持 async 函数
      },
      // 编辑图片
      editImage: {
        onUpdatedImage(imageNode) {
          if (imageNode == null) return;

          const { src, alt, url } = imageNode;
          console.log("updated image", src, alt, url);
        },
        checkImage: customCheckImageFn, // 也支持 async 函数
        parseImageSrc: customParseImageSrc, // 也支持 async 函数
      },
    },
  });
</script>

<template>
  <div class="page-rich">
    <Rich v-model="richHtml" :editorConfig="editorConfig" />
  </div>
</template>

3.9 上传图片

3.9.1 服务端地址

必填,否则上传图片会报错。

let editorConfig = ref({
  MENU_CONF: {
    uploadImage: {
      server: "/api/upload",
    },
  },
});

正常情况要求服务端返回体如下,如果不满足可以查看自定义插入。

// 上传成功返回
{
    "errno": 0, // 注意:值是数字,不能是字符串
    "data": {
        "url": "xxx", // 图片 src ,必须
        "alt": "yyy", // 图片描述文字,非必须
        "href": "zzz" // 图片的链接,非必须
    }
}

// 上传失败返回
{
    "errno": 1, // 只要不等于 0 就行
    "message": "失败信息"
}
3.9.2 基本配置
let editorConfig = ref({
  MENU_CONF: {
    uploadImage: {
      // form-data fieldName ,默认值 'wangeditor-uploaded-image'
      fieldName: "your-custom-name",

      // 单个文件的最大体积限制,默认为 2M
      maxFileSize: 1 * 1024 * 1024, // 1M

      // 最多可上传几个文件,默认为 100
      maxNumberOfFiles: 10,

      // 选择文件时的类型限制,默认为 ['image/*'] 。如不想限制,则设置为 []
      allowedFileTypes: ["image/*"],

      // 自定义上传参数,例如传递验证的 token 等。参数会被添加到 formData 中,一起上传到服务端。
      meta: {
        token: "xxx",
        otherKey: "yyy",
      },

      // 将 meta 拼接到 url 参数中,默认 false
      metaWithUrl: false,

      // 自定义增加 http  header
      headers: {
        Accept: "text/x-json",
        otherKey: "xxx",
      },

      // 跨域是否传递 cookie ,默认为 false
      withCredentials: true,

      // 超时时间,默认为 10 秒
      timeout: 5 * 1000, // 5 秒
    },
  },
});
3.9.3 回调函数
let editorConfig = ref({
  MENU_CONF: {
    uploadImage: {
      // 上传之前触发
      onBeforeUpload(file) {
        // file 选中的文件,格式如 { key: file }
        return file;

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

      // 上传进度的回调函数
      onProgress(progress) {
        // progress 是 0-100 的数字
        console.log("progress", progress);
      },

      // 单个文件上传成功之后
      onSuccess(file, res) {
        console.log(`${file.name} 上传成功`, res);
      },

      // 单个文件上传失败
      onFailed(file, res) {
        console.log(`${file.name} 上传失败`, res);
      },

      // 上传错误,或者触发 timeout 超时
      onError(file, err, res) {
        console.log(`${file.name} 上传出错`, err, res);
      },
    },
  },
});
3.9.4 自定义插入

如果服务端的返回体,不满足所需格式,可以使用 customInsert 自定义插入。

let editorConfig = ref({
  MENU_CONF: {
    uploadImage: {
      customInsert(res, insertFn) {
        // res 即服务端的返回结果

        // 从 res 中找到 url alt href ,然后插入图片
        insertFn(url, alt, href);
      },
    },
  },
});
3.9.5 自定义上传

如果不想使用 wangEditor 自带的上传功能,例如你要上传到阿里云 OSS 。可以通过 customUpload 来自定义上传。

let editorConfig = ref({
  MENU_CONF: {
    uploadImage: {
      async customUpload(file, insertFn) {
        // file 即选中的文件
        // 自己实现上传,并得到图片 url alt href
        // 最后插入图片
        insertFn(url, alt, href);
      },
    },
  },
});
3.9.6 自定义选择图片

如果不想使用 wangEditor 自带的选择文件功能,例如你有自己的图床,或者图片选择器。可以通过 customBrowseAndUpload 来自己实现选择图片、上传图片,并插入图片。

let editorConfig = ref({
  MENU_CONF: {
    uploadImage: {
      customBrowseAndUpload(insertFn) {
        // 自己选择文件
        // 自己上传文件,并得到图片 url alt href
        // 最后插入图片
        insertFn(url, alt, href);
      },
    },
  },
});
3.9.7 base64 插入图片
let editorConfig = ref({
  MENU_CONF: {
    uploadImage: {
      // 小于该值就插入 base64 格式(而不上传),默认为 0
      base64LimitSize: 5 * 1024, // 5kb
    },
  },
});

3.10 视频

onInsertedVideo 插入视频之后的回调、checkVideo 校验视频链接、parseVideoSrc 转换视频链接

<script setup lang="ts">
  import { ref } from "vue";
  import Rich from "@/components/Rich.vue";

  let richHtml = ref("");
  // 自定义校验视频
  const customCheckVideoFn = (src, poster) => {
    if (!src) {
      return;
    }
    if (src.indexOf("http") !== 0) {
      return "视频地址必须以 http/https 开头";
    }
    return true;

    // 返回值有三种选择:
    // 1. 返回 true ,说明检查通过,编辑器将正常插入视频
    // 2. 返回一个字符串,说明检查未通过,编辑器会阻止插入。会 alert 出错误信息(即返回的字符串)
    // 3. 返回 undefined(即没有任何返回),说明检查未通过,编辑器会阻止插入。但不会提示任何信息
  };

  // 自定义转换视频
  const customParseVideoSrc = (src) => {
    if (src.includes(".bilibili.com")) {
      // 转换 bilibili url 为 iframe (仅作为示例,不保证代码正确和完整)
      const arr = location.pathname.split("/");
      const vid = arr[arr.length - 1];
      return `<iframe src="//player.bilibili.com/player.html?bvid=${vid}" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true"> </iframe>`;
    }
    return src;
  };
  let editorConfig = ref({
    MENU_CONF: {
      insertVideo: {
        onInsertedVideo(videoNode) {
          if (videoNode == null) return;

          const { src } = videoNode;
          console.log("inserted video", src);
        },
        checkVideo: customCheckVideoFn, // 也支持 async 函数
        parseVideoSrc: customParseVideoSrc, // 也支持 async 函数
      },
    },
  });
</script>

<template>
  <div class="page-rich">
    <Rich v-model="richHtml" :editorConfig="editorConfig" />
  </div>
</template>

3.11 上传视频

3.11.1 服务端地址

必填,否则上传图片会报错。

let editorConfig = ref({
  MENU_CONF: {
    uploadVideo: {
      server: "/api/upload",
    },
  },
});

正常情况要求服务端返回体如下,如果不满足可以查看自定义插入。

// 上传成功返回
{
    "errno": 0, // 注意:值是数字,不能是字符串
    "data": {
        "url": "xxx", // 视频 src ,必须
        "poster": "xxx.png" // 视频封面图片 url ,可选
    }
}

// 上传失败返回
{
    "errno": 1, // 只要不等于 0 就行
    "message": "失败信息"
}
3.11.2 基本配置
let editorConfig = ref({
  MENU_CONF: {
    uploadVideo: {
      // form-data fieldName ,默认值 'wangeditor-uploaded-video'
      fieldName: "your-custom-name",

      // 单个文件的最大体积限制,默认为 10M
      maxFileSize: 5 * 1024 * 1024, // 5M

      // 最多可上传几个文件,默认为 5
      maxNumberOfFiles: 3,

      // 选择文件时的类型限制,默认为 ['video/*'] 。如不想限制,则设置为 []
      allowedFileTypes: ["video/*"],

      // 自定义上传参数,例如传递验证的 token 等。参数会被添加到 formData 中,一起上传到服务端。
      meta: {
        token: "xxx",
        otherKey: "yyy",
      },

      // 将 meta 拼接到 url 参数中,默认 false
      metaWithUrl: false,

      // 自定义增加 http  header
      headers: {
        Accept: "text/x-json",
        otherKey: "xxx",
      },

      // 跨域是否传递 cookie ,默认为 false
      withCredentials: true,

      // 超时时间,默认为 30 秒
      timeout: 15 * 1000, // 15 秒
    },
  },
});
3.11.3 回调函数
let editorConfig = ref({
  MENU_CONF: {
    uploadVideo: {
      // 上传之前触发
      onBeforeUpload(file) {
        // file 选中的文件,格式如 { key: file }
        return file;

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

      // 上传进度的回调函数
      onProgress(progress) {
        // progress 是 0-100 的数字
        console.log("progress", progress);
      },

      // 单个文件上传成功之后
      onSuccess(file, res) {
        console.log(`${file.name} 上传成功`, res);
      },

      // 单个文件上传失败
      onFailed(file, res) {
        console.log(`${file.name} 上传失败`, res);
      },

      // 上传错误,或者触发 timeout 超时
      onError(file, err, res) {
        console.log(`${file.name} 上传出错`, err, res);
      },
    },
  },
});
3.11.4 自定义插入

如果服务端的返回体,不满足所需格式,可以使用 customInsert 自定义插入。

let editorConfig = ref({
  MENU_CONF: {
    uploadVideo: {
      customInsert(res, insertFn) {
        // res 即服务端的返回结果

        // 从 res 中找到 url poster ,然后插入视频
        insertFn(url, poster);
      },
    },
  },
});
3.11.5 自定义上传

如果不想使用 wangEditor 自带的上传功能,例如你要上传到阿里云 OSS 。可以通过 customUpload 来自定义上传。

let editorConfig = ref({
  MENU_CONF: {
    uploadVideo: {
      async customUpload(file, insertFn) {
        // file 即选中的文件
        // 自己实现上传,并得到视频 url poster
        // 最后插入视频
        insertFn(url, poster);
      },
    },
  },
});
3.11.6 自定义选择视频

如果不想使用 wangEditor 自带的选择文件功能,例如你有自己的图床,或者视频文件选择器。可以通过 customBrowseAndUpload 来自己实现选择视频、上传视频,并插入视频

let editorConfig = ref({
  MENU_CONF: {
    uploadVideo: {
      customBrowseAndUpload(insertFn) {
        // 自己选择文件
        // 自己上传文件,并得到视频 url poster
        // 最后插入视频
        insertFn(url, poster);
      },
    },
  },
});

4. 编辑器 API

4.1 getConfig

获取编辑器所有配置

const handleCreated = (editor) => {
  const result = editor.getConfig();
  console.log(result);
};

4.2 getAllMenuKeys

获取编辑器所有 menu 的 key

const handleCreated = (editor) => {
  const result = editor.getAllMenuKeys();
  console.log(result);
};

4.3 getMenuConfig

获取单个 menu 的配置。

const handleCreated = (editor) => {
  const result = editor.getMenuConfig("color");
  console.log(result);
};

4.4 alert

编辑器 alert ,可通过 customAlert 配置。

editor.alert("错误信息", "error");

customAlert 配置

<script setup lang="ts">
  import { message } from "antd";
  const customAlert = (s, t) => {
    switch (t) {
      case "success":
        message.success(s);
        break;
      case "info":
        message.info(s);
        break;
      case "warning":
        message.warning(s);
        break;
      case "error":
        message.error(s);
        break;
      default:
        message.info(s);
        break;
    }
  };
</script>

<template>
  <div class="editor-content-view">
    <Toolbar />
    <Editor @customAlert="customAlert" />
  </div>
</template>

4.5 handleTab

控制编辑器按 tab 键时,输入什么。

<script setup lang="ts">
  const handleCreated = (editor: any) => {
    editor.handleTab = () => editor.insertText("aaaaa");
  };
</script>
<template>
  <div class="editor-content-view">
    <Toolbar />
    <Editor @onCreated="handleCreated" />
  </div>
</template>

4.6 getHtml

editor.getHtml() 获取非格式化的 html

const result = editor.getHtml();
console.log(result);

4.7 getText

获取当前编辑器的纯文本内容

const handleCreated = (editor) => {
  const result = editor.getText();
  console.log(result);
};

4.8 setHtml

重置编辑器的 HTML 内容。

editor.setHtml("<p>hello</p>");

4.9 isEmpty

判断当前编辑器内容是否为空(只有一个空段落)

editor.isEmpty();

4.10 getSelectionText

获取选中的文本

const result = editor.getSelectionText();
console.log(result);

4.11 getElemsByType

通过 type 获取编辑器的 element 列表。

editor.getElemsByType("image"); // 所有图片
editor.getElemsByType("link"); // 所有链接

4.12 getElemsByTypePrefix

通过 type 前缀获取编辑器的 element 列表。

editor.getElemsByTypePrefix("header"); // 获取所有标题 header1 header2 header3...

4.13 deleteBackward

向前删除,相当于按 backspace 键。

editor.deleteBackward();

4.14 deleteForward

向后删除,相当于按 delete 键

editor.deleteForward();

4.15 deleteFragment

删除选中的内容

editor.deleteFragment();

4.16 getFragment

获取选中的内容,json 格式

editor.getFragment();

4.17 insertBreak

在选区回车换行

editor.insertBreak();

4.18 insertText

在选区插入文本

editor.insertText("aaaa");

4.19 dangerouslyInsertHtml

插入富文本

editor.dangerouslyInsertHtml(`<h1>标题</h1><p>文本 <b>加粗</b></p>`);

4.20 clear

清空编辑器内容

editor.clear();

4.21 undo

撤销

editor.undo();

4.22 redo

重做

editor.redo();

4.23 insertNode

在选区插入一个节点

const node = { type: "paragraph", children: [{ text: "simple text" }] };
editor.insertNode(node);

4.24 insertNodes

在选区插入多个节点

import { SlateTransforms } from "@wangeditor/editor";

const node1 = { type: "paragraph", children: [{ text: "aaa" }] };
const node2 = { type: "paragraph", children: [{ text: "bbb" }] };
const nodeList = [node1, node2];

SlateTransforms.insertNodes(editor, nodeList);

4.25 removeNodes

删除选区所在的节点

import { SlateTransforms } from "@wangeditor/editor";

SlateTransforms.removeNodes(editor);

4.26 获取选中节点

可使用 SlateEditor.nodes 获取选中的节点。

import { SlateEditor, SlateElement, SlateNode } from "@wangeditor/editor";

const nodeEntries = SlateEditor.nodes(editor, {
  match: (node) => {
    if (SlateElement.isElement(node)) {
      if (node.type === "paragraph") {
        return true; // 匹配 paragraph
      }
    }
    return false;
  },
  universal: true,
});

if (nodeEntries == null) {
  console.log("当前未选中的 paragraph");
} else {
  for (let nodeEntry of nodeEntries) {
    const [node, path] = nodeEntry;
    console.log("选中了 paragraph 节点", node);
    console.log("节点 path 是", path);
  }
}

4.27 setNodes

设置选中节点的属性

import { SlateTransforms } from "@wangeditor/editor";

SlateTransforms.setNodes(
  editor,
  {
    // @ts-ignore
    textAlign: "right",
  },
  {
    mode: "highest", // 针对最高层级的节点
  }
);

4.28 getParentNode

获取一个节点的父节点

const parentNode = editor.getParentNode(node); // 返回 node 或者 null

4.29 toDOMNode

获取一个节点对应的 DOM 节点

const elem = editor.toDOMNode(node); // 返回 HTMLElement

4.30 isInline

判断一个节点是否是 inline

const inline = editor.isInline(node);

4.31 isVoid

判断一个节点是否是 void

const void = editor.isVoid(node)

4.32 isText

判断一个节点是否是 text

import { SlateText } from "@wangeditor/editor";

SlateText.isText(node); // true/false

4.33 isElement

判断一个节点是否是 elem

import { SlateElement } from "@wangeditor/editor";

SlateElement.isElement(node); // true/false

4.34 addMark

为选中的文本添加标记(文本样式)

editor.addMark("bold", true); // 加粗
editor.addMark("color", "#999"); // 文本颜色

4.35 removeMark

对选中的文字,取消标记(文本样式)

editor.removeMark("bold"); // 取消加粗

4.36 marks

获取选中文字的标记(文本样式)

import { SlateEditor } from "@wangeditor/editor";

SlateEditor.marks(editor); // 例如 { bold: true, color: "#595959" }

4.37 id

获取编辑器 id

editor.id; // 如 'wangEditor-1'

4.38 isFullScreen

编辑器是否全屏

editor.isFullScreen; // true/false

4.39 focus

聚焦到编辑器

editor.focus();

// editor.focus(true) // 选区定位到最后

4.40 blur

失焦编辑器

editor.blur();

4.41 isFocused

判断当前编辑器是否聚焦?

editor.isFocused(); // true/false

4.42 updateView

强制更新视图

editor.updateView();

4.43 scrollToElem

滚动到指定元素,类似锚点。

editor.scrollToElem(elemId);

4.44 showProgressBar

显示进度条,一般用于上传功能

editor.showProgressBar(progress); // progress 为 0-100 的数字

4.45 hidePanelOrModal

隐藏当前的弹框 (如插入链接) 和下拉列表(如设置标题、设置字体)

editor.hidePanelOrModal();

4.46 fullScreen

设置为全屏

editor.fullScreen();

4.47 unFullScreen

取消全屏

editor.unFullScreen();

4.48 disable

禁用编辑器,设置为只读

editor.disable();

4.49 isDisabled

判断当前编辑器是否只读?

editor.isDisabled(); // true/false

4.50 enable

取消禁用,取消只读

editor.enable();

4.51 destroy

销毁编辑器和工具栏

editor.destroy();

4.52 getEditableContainer

获取编辑区域容器 DOM 节点

editor.getEditableContainer();

4.53 selection

获取编辑器当前的选区。如果未选中,则返回 null 。

editor.selection; // selection 或 null

selection 数据结构如下:

{
  "anchor": { "path": [1, 0], "offset": 8 },
  "focus": { "path": [1, 0], "offset": 10 }
}

4.54 select

选中一个指定的选区。

const newSelection = {
  anchor: { path: [1, 0], offset: 8 },
  focus: { path: [1, 0], offset: 10 },
};
editor.select(newSelection);

4.55 selectAll

选中所有内容

editor.selectAll();

4.56 deselect

取消选中

editor.deselect();

4.57 move

移动光标

editor.move(3); // 移动 3 个字符

4.58 moveReverse

反向移动光标

editor.moveReverse(2); // 反向移动 2 个字符

4.59 restoreSelection

恢复最近一次非 null 选区。如编辑器 blur 之后,再重新恢复选区。

editor.restoreSelection();

4.60 isSelectedAll

判断编辑器是否全部选中。

editor.isSelectedAll(); // true/false

4.61 getSelectionPosition

获取选区的定位,将视情况返回 left right top bottom 的其中几个。

editor.getSelectionPosition(); // 例如 { left: "80.15px", top: "116px" }

4.62 getNodePosition

获取某个节点的定位,将视情况返回 left right top bottom 的其中几个。

editor.getNodePosition(node); // 例如 { left: "80.15px", top: "116px" }

4.63 on

监听某个事件

editor.on("event-key", fn);

4.64 off

取消监听

editor.off("event-key", fn);

4.65 once

只监听一次

editor.once("event-key", fn);

4.66 emit

触发事件

editor.emit("event-key");

4.67 内置的事件

editor.on("fullScreen", () => {
  console.log("fullScreen");
});
editor.on("unFullScreen", () => {
  console.log("unFullScreen");
});
editor.on("scroll", () => {
  console.log("scroll");
});
editor.on("modalOrPanelShow", (modalOrPanel) => {
  console.log(modalOrPanel);
});
editor.on("modalOrPanelHide", () => {
  console.log("modalOrPanelHide");
});
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yqcoder

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值