富文本编辑器,基于vue3+vue-class-component

186 阅读1分钟

基于"wangeditor": "^4.6.10"+ vue3+ vue-class-component 温馨提醒:其它项目插件肯定不一定全部存在,但是实现方案是这样的可以参考参考

先上效果:

image.png

实现代码
<template>
  <div class="content">
    <p name="editor" id="editor" ref="editor" style="z-index: -1"></p>
  </div>
</template>

<script lang="ts">
import { reactive } from "vue";
import { Options, Vue } from "vue-class-component";
import EWangEditor from "wangeditor";
import { updateFile } from "@/https/apis";

@Options({
  props: {
    modelValue: String,//富文本插件绑定的值
    isShowDetails: Boolean,//是否是显示详情,显示详情不可编辑
    height:{
      type: Number,
      default: 300
    }
  },
  emits: ["update:modelValue"],
})
export default class MyEditor extends Vue {
  modelValue!: string;
  isShowDetails!: boolean;
  state = reactive<any>({});
  editor: any;
  height: number;
  mounted() {
    this.createEditor();
    this.editor.txt.html(this.modelValue);//设置内容
    if (this.isShowDetails) {
      this.editor.disable();//禁用编辑器
    }
  }

  createEditor() {
    this.editor = new EWangEditor("#editor");
    // this.editor.config.uploadImgShowBase64 = true;
    this.editor.config.showLinkImg = false;
    // 配置触发 onchange 的时间频率,默认为 200ms
    this.editor.config.onchangeTimeout = 400;
    this.editor.config.placeholder = "";//没有内容时的输入提示
    this.editor.config.height = this.height;//设置编辑区域高度
    // 如果插件绑定的值发生改变,触发这个函数执行
    this.editor.config.onchange = (html) => {
      this.state.editorContent = html;
      this.$emit("update:modelValue", html);
    };

    this.editor.config.customAlert = (err) => {
      console.log(err);
    };//自定义 alert

    //以下为新增
    const menuItem = [
      //工具栏里有哪些工具
      "head", // 标题
      "bold", // 粗体
      "fontSize", //字号
      "fontName", //字体
      "italic", // 斜体
      "underline", //下划线
      "strikeThrough", //删除线
      "indent", //缩进
      "lineHeight", //行高
      "foreColor", //文字颜色
      "backColor", //文字背景颜色
      "link", //链接,插入一个链接地址,如果填写了描述,则高亮显示描述。若没有,则高亮显示链接
      "list", // 序列(有序列表、无序列表)
      "todo", //待办事项
      "justify", // 对齐方式
      "quote", //引用
      // "emoticon", //表情
      "image", //插入图片
      // "video", //插入视频
      "table", //表格
      "code", //代码
      "splitLine", //分割线
      "undo", //撤销
      "redo", //恢复
    ];

    this.editor.config.menus = [...menuItem]; /* 自定义显示哪些菜单和菜单的顺序 */

    this.editor.config.colors = [
      "#000000",
      "#eeece0",
      "#1c487f",
      "#4d80bf",
    ]; /* 文字颜色、背景色可以选择哪些颜色? */

    this.editor.config.fontNames = [
      "黑体",
      "仿宋",
      "楷体",
      "标楷体",
      "华文仿宋",
      "华文楷体",
      "宋体",
      "微软雅黑",
    ];/* 字体工具提供哪些字体? */
    // 自定义上传图片
    this.editor.config.customUploadImg = function (files, insert) {
      // files 是 input 中选中的文件列表
      // insert 是获取图片 url 后,插入到编辑器的方法
      // 在这里进行一系列的校验
      // const formData = new FormData();
      // formData.append("file", files[0]);
      // axios
      //   .post(
      //     "http://localhost:8080/itkb/square/article/uploadArticleImage",
      //     formData,
      //     {
      //       "Content-type": "multipart/form-data",
      //     }
      //   )
      //   .then(
      //     (res) => {
      //       // 上传成功后的处理
      //       // 上传代码返回结果之后,将图片插入到编辑器中
      //       insert(res.data);
      //     },
      //     (err) => {
      //       // 出现错误时的处理
      //       console.log("上传图片失败" + err.data);
      //     }
      //   );
      updateFile(files[0]).then((res) => {
        insert(process.env.VUE_APP_IMG_URL + res.data);
      });
    };
    //创建编辑器
    this.editor.create();
  }

  unmounted() {
    this.editor = null;
  }
}
</script>    

<style scoped>
.content {
  position: relative;
  z-index: 100;
}

</style>
使用
import myEditor from "@/components/myEditor.vue"
<myEditor v-model:modelValue="state.formData.value2" :isShowDetails="isShowDetails"/>
功能拓展

现菜单是否展示,以及是否有边框,最小高度等拓展需求的封装

<template>
  <div class="content">
    <p
      v-if="isHaveMenu"
      name="editor1"
      class="border"
      id="editor1"
      ref="editor1"
      style="z-index: -1"
    ></p>
    <p
      name="editor2"
      :class="isHaveBodyBorder ? 'border' : ''"
      id="editor2"
      ref="editor2"
      :style="{ zIndex: -1,minHeight:'300px' }"
    ></p>
  </div>
</template>

<script lang="ts">
import { reactive } from "vue";
import { Options, Vue } from "vue-class-component";
import EWangEditor from "wangeditor";
import { updateAnnex } from "@/https/notification-manager-apis"

@Options({
  props: {
    modelValue: String,//富文本插件绑定的值
    isShowDetails: Boolean,//是否是显示详情,显示详情不可编辑
    height: {
      type: Number,
      default: 300
    },
    isHaveMenu: {//是否有顶部菜单栏
      type: Boolean,
      default: true
    },
    isHaveBodyBorder: {//是否内容区域外边框
      type: Boolean,
      default: true
    }
  },
  emits: ["update:modelValue"],
})
export default class MyEditor extends Vue {
  modelValue!: string;
  isShowDetails!: boolean;
  isHaveMenu: boolean;
  isHaveBodyBorder: boolean;
  state = reactive<any>({});
  editor: any;
  height: number;
  mounted() {
    this.createEditor();
    this.editor.txt.html(this.modelValue);//设置内容
    if (this.isShowDetails) {
      this.editor.disable();//禁用编辑器
    }
  }

  createEditor() {
    this.editor = new EWangEditor("#editor1", "#editor2");
    // this.editor.config.uploadImgShowBase64 = true;
    this.editor.config.showLinkImg = false;
    // 配置触发 onchange 的时间频率,默认为 200ms
    this.editor.config.onchangeTimeout = 400;
    this.editor.config.placeholder = "";//没有内容时的输入提示
    this.editor.config.height = this.height;//设置编辑区域高度
    // 如果插件绑定的值发生改变,触发这个函数执行
    this.editor.config.onchange = (html) => {
      this.state.editorContent = html;
      this.$emit("update:modelValue", html);
    };

    this.editor.config.customAlert = (err) => {
      console.log(err);
    };//自定义 alert

    //以下为新增
    const menuItem = [
      //工具栏里有哪些工具
      "head", // 标题
      "bold", // 粗体
      "fontSize", //字号
      "fontName", //字体
      "italic", // 斜体
      "underline", //下划线
      "strikeThrough", //删除线
      "indent", //缩进
      "lineHeight", //行高
      "foreColor", //文字颜色
      "backColor", //文字背景颜色
      "link", //链接,插入一个链接地址,如果填写了描述,则高亮显示描述。若没有,则高亮显示链接
      "list", // 序列(有序列表、无序列表)
      "todo", //待办事项
      "justify", // 对齐方式
      "quote", //引用
      // "emoticon", //表情
      // "image", //插入图片
      // "video", //插入视频
      "table", //表格
      "code", //代码
      "splitLine", //分割线
      "undo", //撤销
      "redo", //恢复
    ];

    this.editor.config.menus = [...menuItem]; /* 自定义显示哪些菜单和菜单的顺序 */

    this.editor.config.colors = [
      "#000000",
      "#eeece0",
      "#1c487f",
      "#4d80bf",
    ]; /* 文字颜色、背景色可以选择哪些颜色? */

    this.editor.config.fontNames = [
      "黑体",
      "仿宋",
      "楷体",
      "标楷体",
      "华文仿宋",
      "华文楷体",
      "宋体",
      "微软雅黑",
    ];/* 字体工具提供哪些字体? */
    // 自定义上传图片
    this.editor.config.customUploadImg = function(files, insert) {
      // files 是 input 中选中的文件列表
      // insert 是获取图片 url 后,插入到编辑器的方法
      // 在这里进行一系列的校验
      // const formData = new FormData();
      // formData.append("file", files[0]);
      // axios
      //   .post(
      //     "http://localhost:8080/itkb/square/article/uploadArticleImage",
      //     formData,
      //     {
      //       "Content-type": "multipart/form-data",
      //     }
      //   )
      //   .then(
      //     (res) => {
      //       // 上传成功后的处理
      //       // 上传代码返回结果之后,将图片插入到编辑器中
      //       insert(res.data);
      //     },
      //     (err) => {
      //       // 出现错误时的处理
      //       console.log("上传图片失败" + err.data);
      //     }
      //   );
      updateAnnex(files[0]).then((res) => {
        insert(process.env.VUE_APP_IMG_URL + res.data);
      });
    };
    //创建编辑器
    this.editor.create();
  }

  unmounted() {
    this.editor = null;
  }
}
</script>    

<style scoped>
.content {
  position: relative;
  z-index: 100;
}
.border {
  border: 1px solid #ccc;
}
</style>