使用 Quill 实现编辑器功能

使用到的技术:vue2、Element-ui2.0 、Quill 2.0

效果:

Quill编辑器上传文件和图片功能

1.安装 Quill 包

复制代码
// npm 安装
npm install quill vue-quill-editor --save

// yarn 安装
yarn add quill vue-quill-editor

完整代码

复制代码
<template>
  <div>
    <!-- 隐藏上传组件,图片 / 文件上传复用 -->
    <el-upload :action="uploadUrl" :before-upload="handleBeforeUpload" :on-success="handleUploadSuccess"
      :on-error="handleUploadError" name="file" :show-file-list="false" :headers="headers" ref="upload"
      style="display: none" v-if="type === 'url'"></el-upload>

    <!-- 编辑器容器 -->
    <div class="editor" ref="editor" :style="styles"></div>
  </div>
</template>

<script>
import Quill from "quill";
import "quill/dist/quill.core.css";
import "quill/dist/quill.snow.css";
import "quill/dist/quill.bubble.css";
import { getToken } from "@/utils/auth";

export default {
  name: "Editor",
  props: {
    value: { type: String, default: "" },
    height: { type: Number, default: null },
    minHeight: { type: Number, default: null },
    readOnly: { type: Boolean, default: false },
    fileSize: { type: Number, default: 5 }, // 上传文件大小限制(MB)
    type: { type: String, default: "url" },
  },
  data() {
    return {
      uploadUrl: process.env.VUE_APP_BASE_API + "/common/upload",
      headers: { Authorization: "Bearer " + getToken() },
      Quill: null,
      currentValue: "",
      uploadType: "image", // 当前上传类型
      options: {
        theme: "snow",
        bounds: document.body,
        debug: "warn",
        modules: {
          toolbar: {
            container: [
              ["bold", "italic", "underline", "strike"],
              ["blockquote", "code-block"],
              [{ list: "ordered" }, { list: "bullet" }],
              [{ indent: "-1" }, { indent: "+1" }],
              [{ size: ["small", false, "large", "huge"] }],
              [{ header: [1, 2, 3, 4, 5, 6, false] }],
              [{ color: [] }, { background: [] }],
              [{ align: [] }],
              ["clean"],
              ['bold', 'italic', 'underline'],
              ["link", "image", "video"], // 新增 file 按钮
              // ['image', 'file'] // 确保包含 file
            ],
            handlers: {}
          },
        },
        placeholder: "请输入内容",
        readOnly: this.readOnly,
      },
    };
  },
  computed: {
    styles() {
      const style = {};
      if (this.minHeight) style.minHeight = `${this.minHeight}px`;
      if (this.height) style.height = `${this.height}px`;
      return style;
    },
  },
  watch: {
    value: {
      handler(val) {
        if (this.Quill && val !== this.currentValue) {
          this.currentValue = val || "";
          const html = this.Quill.root.innerHTML;
          if (html !== this.currentValue) {
            this.Quill.clipboard.dangerouslyPasteHTML(this.currentValue);
          }
        }
      },
      immediate: true,
    },
  },
  mounted() {
    this.init();
  },
  beforeDestroy() {
    this.Quill = null;
  },
  methods: {
    /** 初始化 Quill */
    init() {
      const editor = this.$refs.editor;
      this.Quill = new Quill(editor, this.options);

      // 初始化内容
      if (this.value) {
        this.currentValue = this.value;
        this.Quill.clipboard.dangerouslyPasteHTML(this.value);
      }

      // 富文本事件
      this.Quill.on("text-change", (delta, oldDelta, source) => {
        const html = this.$refs.editor.children[0].innerHTML;
        const text = this.Quill.getText();
        this.currentValue = html;
        this.$emit("input", html);
        this.$emit("on-change", { html, text, quill: this.Quill });
        this.$emit("on-text-change", delta, oldDelta, source);
      });

      this.Quill.on("selection-change", (range, oldRange, source) => {
        this.$emit("on-selection-change", range, oldRange, source);
      });

      this.Quill.on("editor-change", (eventName, ...args) => {
        this.$emit("on-editor-change", eventName, ...args);
      });

      // 如果是 URL 类型(即后端上传)
      if (this.type === "url") {
        const toolbar = this.Quill.getModule("toolbar");

        /** 图片上传按钮 */
        toolbar.addHandler("image", (value) => {
          console.log("image");
          this.uploadType = "image";
          if (value) {
            this.$refs.upload.$children[0].$refs.input.accept = "image/*";
            this.$refs.upload.$children[0].$refs.input.click();
          }
        });

        /** 文件上传按钮 */
        toolbar.addHandler("link", (value) => {
          this.uploadType = "file";
          if (value) {
            this.$refs.upload.$children[0].$refs.input.accept =
              ".pdf,.doc,.docx,.xls,.xlsx,.txt";
            this.$refs.upload.$children[0].$refs.input.click();
          }
        });

      }
    },

    /** 上传前校验 */
    handleBeforeUpload(file) {
      console.log("handleBeforeUpload");
      if (this.fileSize) {
        const isLt = file.size / 1024 / 1024 < this.fileSize;
        if (!isLt) {
          this.$message.error(`上传文件大小不能超过 ${this.fileSize} MB!`);
          return false;
        }
      }
      return true;
    },

    /** 上传成功回调 */
    handleUploadSuccess(res, file) {
      const quill = this.Quill;

      if (res.code === 200) {
        const url = process.env.VUE_APP_BASE_API + res.fileName;
        const length = quill.getSelection(true).index;

        if (this.uploadType === "image") {
          // 插入图片
          quill.insertEmbed(length, "image", url);
        } else if (this.uploadType === "file") {
          // 插入文件链接
          const fileName = file.name;
          quill.insertText(length, fileName, {
            link: url,
          });
        }

        quill.setSelection(length + 1);
      } else {
        this.$message.error("文件插入失败");
      }
    },

    /** 上传失败回调 */
    handleUploadError() {
      this.$message.error("文件插入失败");
    },
  },
};
</script>



<style scoped>

</style>

注意:

uploadUrl:需要的地址需要修改成自己地址

headers:看自己需要是否需要添加

Quill 2.0版本中 handlers 没有监听file的事件,但是可以用 link 事件代替。

相关推荐
u0103055272 天前
AI驱动的安卓UI自动生成功能实现
人工智能·1024程序员节
u0103055272 天前
长株潭智能体赋能腾讯元器链接共享
1024程序员节
云贝贝贝3 天前
OceanBase认证体系介绍与备考指南
运维·服务器·oceanbase·1024程序员节
u0103055274 天前
Java图像处理实战指南
人工智能·1024程序员节
u0103055274 天前
Java AWT鼠标事件全解析
人工智能·1024程序员节
u0103055276 天前
Java实用类应用精讲
人工智能·1024程序员节
u0103055277 天前
Java I/O核心操作实战
人工智能·1024程序员节
u0103055278 天前
ArrayList操作详解与实战应用
人工智能·1024程序员节
u0103055278 天前
Java模块化编程实战指南
人工智能·笔记·1024程序员节
u01030552710 天前
使用BufferedReader读取控制台输入
人工智能·1024程序员节