vue3中富文本编辑器的使用

165 阅读2分钟

封装组件(MyQuillEdito.vue)

<template>
  <div>
    <QuillEditor
      ref="myQuillEditor"
      theme="snow"
      :content="content"
      :options="editorOption"
      content-type="html"
      @update:content="setValue"
    />
    <!-- 使用自定义图片上传 -->
    <el-upload class="avatar-uploader" action="" :limit="1" :http-request="fileChange"></el-upload>
        <div class="linkDialog">
              <el-input v-model="linkUrl.text" placeholder="请输入链接名称" style="width: 200px"></el-input>
              <el-input v-model="linkUrl.href" placeholder="请输入链接url" style="width: 200px"></el-input>
              <span class="dialog-footer">
                    <el-button @click="handleClose">取 消</el-button>
                    <el-button type="primary" @click="linkAdd">确 定</el-button>
              </span>
        </div>
  </div>
</template>

<script setup lang="ts">
import { ref, toRaw, onMounted, nextTick, reactive, defineProps } from "vue";
// npm install @vueup/vue-quill       版本号   "@vueup/vue-quill": "^1.2.0",
import { QuillEditor, Quill } from "@vueup/vue-quill";
import "@vueup/vue-quill/dist/vue-quill.snow.css";
// 引入文件上传方法接口
import { uploadImg } from "@/api/modules/upload";
import { ElMessage } from "element-plus";

// 接受参数
const props = defineProps({
  value: String
});

// 在Quill富文本编辑器中,自定义链接(link)的行为,可以通过扩展Quill的Blot类来实现

// 源码中是import直接倒入,这里要用Quill.import引入
const Link = Quill.import("formats/link");
// 自定义a链接
class FileBlot extends Link {
  // 继承Link Blot
  static create(value) {
    let node = undefined;
    if (value && !value.href) {
      // 适应原本的Link Blot
      node = super.create(value);
    } else {
      // 自定义Link Blot
      node = super.create(value.href);
      node.href = value.href;
      node.innerText = value.innerText;
      // node.setAttribute('download', value.innerText);  // 左键点击即下载
    }
    return node;
  }
}
FileBlot.blotName = "link"; // 这里不用改,如果需要也可以保留原来的,这里用个新的blot
FileBlot.tagName = "A";
// 传递别名注册(some-unique-name),可以避免覆盖link格式
Quill.register(FileBlot, "some-unique-name"); // 注册link


const emit = defineEmits(["func"]);
const myQuillEditor = ref();
const content = ref("");
const linkUrl = reactive({
  href: "",
  text: ""
});
const toolBarOption = [
      ["bold", "italic", "underline", "strike"], // 加粗、斜体、下划线、删除线
      ["blockquote", "code-block"], // 引用、代码块
      [{ header: 1 }, { header: 2 }], // 标题
      [{ list: "ordered" }, { list: "bullet" }], // 有序列表、无序列表
      [{ script: "sub" }, { script: "super" }], // 下标、上标
      [{ indent: "-1" }, { indent: "+1" }], // 缩进
      [{ direction: "rtl" }], // 文本方向
      [{ size: ["small", false, "large", "huge"] }], // 字体大小
      [{ header: [1, 2, 3, 4, 5, 6, false] }], // 标题大小
      [{ color: [] }, { background: ["black"] }], // 文本颜色、背景颜色
      [{ font: [] }], // 字体
      [{ align: [] }], // 对齐方式
      ["clean"], // 清除格式
      ["link", "image", "video"] // 链接、图片、视频
];
const editorOption = reactive({
      // 富文本编辑器选项
      placeholder: "请输入文章正文内容,支持上传图片,视频",
      theme: "snow", //主题 snow/bubble
      syntax: true, //语法检测
      lang: "zh-CN",
      modules: {
        toolbar: {
          container: toolBarOption
        }
      }
});
const handleClose = () => {
      document.querySelector(".linkDialog").style.display = "none";
      linkUrl.href = "";
      linkUrl.text = "";
};
const linkAdd = () => {
  try {
        const quill = toRaw(myQuillEditor.value).getQuill();
        const length = quill.getSelection(true).index;
        console.log(quill, length);
        quill.insertEmbed(length, "link", { href: linkUrl.href, innerText: linkUrl.text }, "api");
        quill.setSelection(length + linkUrl.text.length);
        handleClose();
      } catch (e) {
            console.log(e);
            ElMessage({
              message: "链接添加异常!",
              type: "error"
            });
      }
};
const fileChange = file => {
  const reader = new FileReader();
  reader.readAsDataURL(file.file);
  reader.addEventListener("load", () => {
    fileUpload(file.file.name, reader.result);
  });
};
const fileUpload = async (name, file, type) => {
  const params = {
    file: file,
    filename: name
  };=
  try {
    let formData = new FormData();
    formData.append("file", params.file);
    let res = await uploadImg(params);=
    if (res.code === 200) {
          ElMessage({
            message: "操作成功!",
            type: "success"
          });
          const quill = toRaw(myQuillEditor.value).getQuill();
          if (!quill) {
            return;
          }
          const length = quill.getSelection().index;
          quill.insertEmbed(length, "image", res.data.fileUrl);
          quill.setSelection(length + 1);
    } else {
      ElMessage({
        message: "异常!" + res?.msg,
        type: "error"
      });
    }
  } catch (e) {
    console.log(e);
    ElMessage({
      message: "异常!" + e,
      type: "error"
    });
  }
};
const imgHandler = state => {
  if (state) {
    document.querySelector(".avatar-uploader input").click();
  }
};
const linkClick = state => {
  if (state) {
    document.querySelector(".linkDialog").style.display = "block";
  }
};
// 抛出更改内容,此处避免出错直接使用文档提供的getHTML方法
const setValue = () => {
      const text = toRaw(myQuillEditor.value).getHTML();
      console.log("text", text);
      emit("func", text);
};
// 初始化编辑器
onMounted(async () => {
  const quill = toRaw(myQuillEditor.value).getQuill();
  console.log(props.value, "props.value");
  if (myQuillEditor.value) {
    quill.getModule("toolbar").addHandler("image", imgHandler);
    quill.getModule("toolbar").addHandler("link", linkClick);
    await nextTick();
    setTimeout(() => {
      myQuillEditor.value.setContents(props.value);
    }, 300);
  }
});
</script>
<style scoped lang="scss">
.linkDialog {
      width: fit-content;
      position: fixed;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      background-color: #fff;
      display: none;
}
</style>

页面使用

<template>
  <div class="quill-editor">
    <MyQuillEditor style="width: 100%; height: 100%" :value="dynamicValidateForm.content" @func="updateContent" />
  </div>
</template>

<script setup>
import { ref } from "vue";
import MyQuillEditor from "./MyQuillEdito.vue";
const dynamicValidateForm = ref({
  // 默认输入的内容
  content: ""
});
const updateContent = data => {
  dynamicValidateForm.value.content = data;
};
</script>

<style lang="scss" scoped>
.quill-editor {
  width: 100%;
  height: 100%;
}
</style>

效果展示

image.png