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

相关推荐
学传打活5 天前
【边打字.边学昆仑正义文化】_进阶篇5 宇宙星系的运转(续3)
微信公众平台·1024程序员节·汉字·昆仑正义文化
学传打活11 天前
【边打字.边学昆仑正义文化】_进阶篇5 宇宙星系的运转(续1)
微信公众平台·1024程序员节·汉字·昆仑正义文化
fangzhanpeng16818 天前
快速掌握C#语言基础知识点(1.基础数据类型)
开发语言·c#·1024程序员节
无己心21 天前
面向多平台 AI 搜索引擎的适配层架构设计
前端·人工智能·搜索引擎·ai·1024程序员节
u0103055271 个月前
AI驱动的安卓UI自动生成功能实现
人工智能·1024程序员节
u0103055271 个月前
长株潭智能体赋能腾讯元器链接共享
1024程序员节
云贝贝贝1 个月前
OceanBase认证体系介绍与备考指南
运维·服务器·oceanbase·1024程序员节
u0103055271 个月前
Java图像处理实战指南
人工智能·1024程序员节
u0103055271 个月前
Java AWT鼠标事件全解析
人工智能·1024程序员节
u0103055271 个月前
Java实用类应用精讲
人工智能·1024程序员节