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");
});

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/598054.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【前端】输入时字符跳动动画实现

输入时字符跳动动画实现 在前端开发中&#xff0c;为了提升用户体验&#xff0c;我们经常需要为用户的交互行为提供即时的反馈。这不仅让用户知道他们的操作有了响应&#xff0c;还可以让整个界面看起来更加生动、有趣。本文将通过一个简单的例子讲解如何实现在用户输入字符时…

Linux实验 Linux操作基础

实验目的&#xff1a; 掌握Linux系统登录模式的切换&#xff1b;掌握Linux系统的注销与关闭&#xff1b;掌握Linux系统Shell简单命令&#xff1b;掌握Linux系统Shell命令的基础操作及高级操作。 实验内容&#xff1a; 在VMware中启动已经安装好的CentOS&#xff0c;在下图登录…

全域团购外卖怎么样,哪家全域运营系统好?

当前&#xff0c;全域团购外卖逐渐兴起&#xff0c;全域团购外卖怎么样等问题也逐渐成为人们热议的焦点。事实上&#xff0c;从理论上来说&#xff0c;全域团购外卖确实大有可为&#xff0c;全域团购外卖运营服务商的收益也非常值得期待&#xff01; 首先&#xff0c;就团购外卖…

测试人员在面试时的注意事项

一、技术方面面试 在某种程度上来说&#xff0c;技术面试重要到能够决定你是否被聘用。在技术岗位方面&#xff0c;在个人品德没有问题的前提下&#xff0c;招聘公司对技术是最关心的。 我现在并不能给你分析具体的面试题&#xff0c;因为与笔试题相比&#xff0c;面试题千变万…

封装js方法实现无缝循环滚动效果

如下图&#xff0c;又遇到了无缝循环滚动这个需求 突然想到我之前有分享过一篇关于这个的文章https://blog.csdn.net/chuenst/article/details/137125377&#xff0c;果断打开csdn准备cv 经过我一顿操作&#xff0c;很快实现了需求&#xff0c;但是这风扇直接嗡嗡转&#xff0c…

Dropout作为贝叶斯近似: 表示深度学习中的模型不确定性

摘要 深度学习工具在应用机器学习领域受到了极大的关注。然而&#xff0c;这些用于回归和分类的工具并没有捕捉到模型的不确定性。相比之下&#xff0c;贝叶斯模型提供了一个基于数学的框架来推理模型的不确定性&#xff0c;但通常会带来令人望而却步的计算成本。本文提出了一…

Vue3工程化配置

Vue3工程化配置 目录 Vue3工程化配置创建项目vue-clivite(推荐) 快速体验2和3的差别vue3vue2 ref和reactive 创建项目 vue-cli 具体环境配置请点这里 记得新建配置时这里选vue3 vite(推荐) 注&#xff1a;Vite 需要 Node.js 版本 18&#xff0c;20 1.选定路径后再cmd输入创建…

ScienceDirect文献如何下载

ScienceDirect是爱思唯尔公司的全文数据库平台&#xff0c;是全球最大的科学、技术与医学全文电子资源数据库&#xff0c;是我们在查找外文文献常用的数据库。但是&#xff0c;ScienceDirect数据库的文献是需要使用权限才可获取的。如果你没有该数据库资源要如何查询下载文献呢…

likeshop多商户单商户商城_likeshop跑腿源码_likeshop物品租赁系统开源版怎么配置小程序对接?

本人是商业用户所以能持续得到最新商业版&#xff0c;今天我说下likeshop里面怎么打包小程序&#xff0c;大家得到程序时候会发现它有admin目录 app目录 server目录 这三个目录分别是做什么呢&#xff1f; 1.admin目录 下面都是架构文件使用得是Node.js打包得&#xff0c;至于…

教你快速记录每日待办事项,并提醒自己按时完成不忘记

在忙碌的日常生活中&#xff0c;我们经常会面临待办事项繁杂、时间紧迫的困扰。为了更高效地管理时间和任务&#xff0c;我们需要一个能够快速记录并准时提醒我们完成待办事项的工具。此时&#xff0c;敬业签这类的待办软件就成为了很多人的首选工具。 敬业签是一款功能强大的…

信息系统项目管理师0091:项目经理的能力(6项目管理概论—6.3项目经理的角色—6.3.3项目经理的能力)

点击查看专栏目录 文章目录 6.3.3项目经理的能力1.概述2.项目管理技能3.战略和商务管理技能4.领导力技能5.领导力与管理记忆要点总结6.3.3项目经理的能力 1.概述 项目经理需要重点关注三个方面的关键技能包括项目管理、战略和商务、领导力

合肥先进光源束测步进电机控制机箱接线方式

合肥先进光源束测步进电机控制方案介绍 对上篇文里的接线方式做个修订&#xff1a; EtherCat电机控制机箱接线规范 驱动器 控制器 接线方式 使用鸣志 STF05-ECX-H驱动器&#xff0c;每个机箱配8个驱动器使用汇川的H5U-1614MTD etherCat控制器每个驱动器的电源从2分8的分配端子…

AJ-Report开源数据大屏远程命令执行漏洞

文章目录 描述漏洞原理影响版本漏洞复现修复方案 描述 AJ-Report是全开源的一个BI平台&#xff0c;酷炫大屏展示&#xff0c;能随时随地掌控业务动态&#xff0c;让每个决策都有数据支撑。     多数据源支持&#xff0c;内置mysql、elasticsearch、kudu驱动&#xff0c;支持…

Linux基本指令(下下)

各位大佬好 &#xff0c;这里是阿川的博客 &#xff0c; 祝您变得更强 个人主页&#xff1a;在线OJ的阿川 大佬的支持和鼓励&#xff0c;将是我成长路上最大的动力 阿川水平有限&#xff0c;如有错误&#xff0c;欢迎大佬指正 本篇博客续我之前的Linux指令&#xff08;下&a…

美国站群服务器在站群管理中的防护和数据安全保障?

美国站群服务器在站群管理中的防护和数据安全保障? 美国站群服务器的防护和数据安全保障是站群管理中的关键问题。站群服务器位于美国&#xff0c;通常由专业的服务器提供商管理和维护&#xff0c;其安全性受到多方面因素的影响。 美国站群服务器在站群管理中的防护和数据安全…

流畅的python-学习笔记_一等函数

函数对象 函数也是对象&#xff0c;操作可像对象一般操作 高阶函数 高阶函数指接受参数为函数&#xff0c;或返回函数的函数 不少高阶函数在py3已经有了替代品。map&#xff0c; filter可通过生成式实现&#xff0c;reduce&#xff08;在functools里&#xff09;可通过sum实…

【强训笔记】day11

NO.1 思路&#xff1a;枚举&#xff0c;设一号大礼包的数量为x&#xff0c;二号大礼包的数量为y&#xff0c;用循环枚举一号大礼包的个数得到二号大礼包的数量&#xff0c;使得某一时刻axby的值最大。 代码实现&#xff1a; #include<iostream>using namespace std;lo…

存储故障后oracle报—ORA-01122/ORA-01207故障处理---惜分飞

客户存储异常,通过硬件恢复解决存储故障之后,oracle数据库无法正常启动(存储cache丢失),尝试recover数据库报ORA-00283 ORA-01122 ORA-01110 ORA-01207错误 以前处理过比较类似的存储故障case:又一起存储故障导致ORA-00333 ORA-00312恢复存储故障,强制拉库报ORA-600 kcbzib_kcr…

代码随想录打卡第14天第18天

二叉树 1 二叉树部分的一些新知 &#xff08;1&#xff09;二叉树的定义&#xff0c;C方法一定要知道&#xff0c;相对于链表而言&#xff0c;二叉树就是多了两个指针&#xff0c;即左右子节点 struct TreeNode {int val;TreeNode *left;TreeNode *right;TreeNode(int x) : …

SSL证书中DV、OV和EV有什么区别,又该如何选择

SSL&#xff08;安全套接层&#xff09;证书作为一种加密工具&#xff0c;确保了网站与其用户之间传输的信息的安全性。而在选择SSL证书时&#xff0c;我们通常会看到三种类型&#xff1a;域名验证&#xff08;DV&#xff09;、组织验证&#xff08;OV&#xff09;和扩展验证&a…
最新文章