Skip to content

富文本方案

注意

  1. 富文本 我用的方案就是 wangeditor

  2. 官方网站 https://www.wangeditor.com/

安装

bash

npm i @wangeditor/editor @wangeditor/editor-for-vue@next -S

自己封装组件

  • components/Wangeditor.vue

这里特别注意 就是 只能在 onMounted 里面初始化富文本,父组件传递进来 上传图片点击不了

vue
<template>
  <div style="border: 1px solid #ccc">
    <!-- 工具栏 -->
    <Toolbar
      :editor="editorRef"
      :defaultConfig="toolbarConfig"
      style="border-bottom: 1px solid #ccc"
    />
    <!-- 编辑器 -->
    <Editor
      v-model="valueHtml"
      :defaultConfig="editorConfig"
      @onChange="handleChange"
      @onCreated="handleCreated"
      @onDestroyed="handleDestroyed"
      @onFocus="handleFocus"
      @onBlur="handleBlur"
      @customAlert="customAlert"
      @customPaste="customPaste"
      style="height: 500px"
    />
    <div>
      <button @click="getHtml">获取 html</button>
    </div>
  </div>
</template>

<script setup>
import { Boot } from "@wangeditor/editor";
import "@wangeditor/editor/dist/css/style.css"; // 引入 css
import { Editor, Toolbar } from "@wangeditor/editor-for-vue";
// 编辑器实例,必须用 shallowRef
const editorRef = shallowRef();
// 内容 HTML
const valueHtml = ref("");
// 模拟 ajax 异步获取内容
onMounted(() => {
  setTimeout(() => {
    valueHtml.value = "<p>模拟 Ajax 异步设置内容</p>";
  }, 1500);
});

// 编辑器配置
const editorConfig = {
  debug: true,
  placeholder: "请输入内容...",
  MENU_CONF: {
    uploadImage: {
      server: "/api/upload",
      headers: { Authorization: "5656" },
      "tenant-id": "1",
      fieldName: "file",
      customInsert(res, insertFn) {
        if (res.code == 0) {
          let url = res.data.url; // 图片 src ,必须
          let alert = "图片"; // 图片描述文字,非必须
          let href = ""; // 图片的链接,非必须
          insertFn(url, alert, href);
        }
      },
    },
  },
};
// 测试:注册 button menu
class MyButtonMenu {
  constructor() {
    (this.title = "menu1"), (this.tag = "button");
  }
  getValue() {
    return "";
  }
  isActive() {
    return false;
  }
  isDisabled() {
    return false;
  }
  exec(editor) {
    console.log(editor);
    alert("menu1 exec");
  }
}
const menuConf = {
  key: "my-menu-1", // menu key ,唯一。注册之后,需通过 toolbarKeys 配置到工具栏
  factory() {
    return new MyButtonMenu();
  },
};
Boot.registerMenu(menuConf);

// 工具栏配置
const toolbarConfig = {
  // toolbarKeys: [
  //     'headerSelect',
  //     'bold',
  //     'my-menu-1', // 一些常用的菜单 key
  //     'bold', // 加粗
  //     'italic', // 斜体
  //     'through', // 删除线
  //     'underline', // 下划线
  //     'bulletedList', // 无序列表
  //     'numberedList', // 有序列表
  //     'color', // 文字颜色
  //     'fontSize', // 字体大小
  //     'lineHeight', // 行高
  //     'uploadImage', // 上传图片
  //     'delIndent', // 缩进
  //     'indent', // 增进
  //     'deleteImage', //删除图片
  //     'divider', // 分割线
  //     'justifyCenter', // 居中对齐
  //     'justifyJustify', // 两端对齐
  //     'justifyLeft', // 左对齐
  //     'justifyRight', // 右对齐
  //     'undo', // 撤销
  //     'redo', // 重做
  //     'clearStyle' // 清除格式
  // ],
  // excludeKeys: [],
  insertKeys: {
    index: 0,
    keys: "my-menu-1",
  },
};

// 编辑器回调函数
const handleCreated = (editor) => {
  console.log("created", editor);

  editorRef.value = editor; // 记录 editor 实例,重要!

  // window.editor = editor // 临时测试使用,用完删除
};
const handleChange = (editor) => {
  console.log("change:", editor.children);
};
const handleDestroyed = (editor) => {
  console.log("destroyed", editor);
};
const handleFocus = (editor) => {
  console.log("focus", editor);
};
const handleBlur = (editor) => {
  console.log("blur", editor);
};
const customAlert = (info, type) => {
  alert(`【自定义提示】${type} - ${info}`);
};
const customPaste = (editor, event, callback) => {
  console.log("ClipboardEvent 粘贴事件对象", event);

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

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

// 及时销毁编辑器
onBeforeUnmount(() => {
  const editor = editorRef.value;
  if (editor == null) return;

  // 销毁,并移除 editor
  editor.destroy();
});

const getHtml = () => {
  const editor = editorRef.value;
  if (editor == null) return;

  console.log(editor.getHtml());
};

defineExpose({
  getHtml,
});
</script>

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

父组件调用

vue
<template>
  <WangEdior ref="wangeditor"></WangEdior>
  <el-button type="primary" @click="handleclick">获取富文本数据</el-button>
</template>
<script setup>
// 富文本编辑器
const wangeditor = ref(null);

const handleclick = () => {
  wangeditor.value.getHtml();
};
</script>