使用 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 事件代替。

相关推荐
2301_780356707 小时前
物联网数据中台智慧医院企业厂家——全视通
1024程序员节
TiAmo zhang7 小时前
微信小程序开发案例 | 简易登录小程序
微信小程序·小程序·1024程序员节
再卷也是菜7 小时前
算法基础篇(8)贪心算法
算法·贪心算法·1024程序员节
时间的情敌7 小时前
Vite 工作原理
1024程序员节
可惜我是水瓶座__7 小时前
[Spark] TaskMetrics指标收集
spark·1024程序员节
qq_4696035897 小时前
最新选题-基于Spark的二氧化碳排放量数据分析系统设计
1024程序员节·大数据项目·co2分析·二氧化碳分析
天下·第二8 小时前
【大模型与OCR】配合应用的示例demo
1024程序员节
Greedy Alg8 小时前
LeetCode 39. 组合总和
1024程序员节
im_AMBER8 小时前
React 04
前端·react.js·前端框架·1024程序员节