(1) rich text compiler

Speaking of rich text compilers, we have to introduce several major compilers in the current market

(1) UEditor compiler

The UEditor compiler can be found at github.com/fex-team/ue…

(2) Simditor compiler

For details on how to use Simditor, see simditor.tower.im/

(3) wangEditor compiler

For details on how to use wangEditor, see www.wangeditor.com/

(4) CKEditor compiler

CKEditor can be found at ckeditor.com/

(5) Quill compiler

The Quill compiler can be found at quilljs.com

“Note” the mainstream rich text compiler on the market is far more than these, I will not list here, if you are interested, you can go to Baidu search

(2) Enter the theme

Using the Quill compiler to achieve rich text in VUE, the effect is as follows:

I won’t say much about the Quill compiler, but if you’re interested, see,www.kancloud.cn/liuwave/qui…

There are two ways to achieve the above effect

(1) Directly used in HTML, but not in VUE

1.1 the introduction of CSS
< link href = ` ` "https://cdn.quilljs.com/1.3.6/quill.snow.css" ` ` rel = ` ` "stylesheet" ` ` >Copy the code
1.2 Writing HTML code
<div id="editor"> <p>Quill compiler! < / p > < p > based on Vue zhongfu text < strong > compiler Quill implementation of < / strong > < / p > < p > < br > < / p > < / div >Copy the code
1.3 Import JS files and edit JS code
< script SRC = "https://cdn.quilljs.com/1.3.6/quill.js" > < / script > <! -- Initialize Quill editor --> <script> var quill = new Quill('#editor', { theme: 'snow' }); </script>Copy the code

Summary: Complete HTML code

<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta  name="viewport" content="width=device-width, Initial - scale = 1.0 "> < title > Document < / title > < link href =" https://cdn.quilljs.com/1.3.6/quill.snow.css "rel =" stylesheet "> </head> <body> <div id="editor"> <p>Hello World! </p> <p>Some initial <strong>bold</strong> text</p> <p><br></p> </div> </body> <! - Include the Quill library - > < script SRC = "https://cdn.quilljs.com/1.3.6/quill.js" > < / script > <! -- Initialize Quill editor --> <script> var quill = new Quill('#editor', { theme: 'snow' }); </script> </html>Copy the code

The results are as follows:

(2) Using the compiler Quill in VUE to achieve rich text

2.1 installation quill
NPM I quill or YARN Add quillCopy the code
2.2 Write HTML code in template
  <div>
    <div class="editor" ref="editor" :style="styles"></div>
    <input
      id="file"
      type="file"
      style="display: none"
      accept="image/png, image/gif, image/jpeg, image/bmp, image/x-icon"
      @change="onFileChange"
    />
  </div>
Copy the code
2.3 Introducing the Quill and CSS files
import Quill from "quill";
import "quill/dist/quill.core.css";
import "quill/dist/quill.snow.css";
import "quill/dist/quill.bubble.css";
Copy the code
2.4 Write JS code under Export defaul
Export default {name: "Editor", props: {/* Editor content */ value: {type: String, default: ",}, /* height */ height: {type: Number, default: null,}, /* minHeight */ minHeight: {type: Number, default: null,},}, data() {return {Quill: Null, currentValue: "", options: {theme: "snow", Bounds: document.body, debug: "warn", modules: {// Toolbar configuration toolbar: [["bold", "italic", "underline", "strike"], // Underline delete line ["blockquote", "code-block"], // reference code block [{list: "Ordered"}, {list: "bullet"}], / / orderly and unordered list [{text-indent: "1"}, {text-indent: "+ 1"}], [{/ / indent size: [" small ", false, "large", "huge"]}], / / font size [{header: [1, 2, 3, 4, 5, 6, false]}], / / title [{color: []}, {background:} []], [{/ / font colors, fonts, background color align: []}], / / alignment (" clean "), / / clear text format (" link ", "image", "video"], / / link, pictures, video,}, placeholder: "please enter the content", readOnly: false, }, }; }, computed: { styles() { let style = {}; if (this.minHeight) { style.minHeight = `${this.minHeight}px`; } if (this.height) { style.height = `${this.height}px`; } return style; }, }, watch: { value: { handler(val) { if (val ! == this.currentValue) { this.currentValue = val === null ? "" : val; if (this.Quill) { this.Quill.pasteHTML(this.currentValue); } } }, immediate: true, }, }, mounted() { this.init(); }, beforeDestroy() { this.Quill = null; }, methods: { init() { const editor = this.$refs.editor; this.Quill = new Quill(editor, this.options); this.Quill.pasteHTML(this.currentValue); this.Quill.on("text-change", (delta, oldDelta, source) => { const html = this.$refs.editor.children[0].innerHTML; const text = this.Quill.getText(); const quill = this.Quill; this.currentValue = html; this.$emit("input", html); this.$emit("on-change", { html, text, quill }); }); this.Quill.on("text-change", (delta, oldDelta, source) => { 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); }); let toolbar = this.Quill.getModule("toolbar"); AddHandler ("image", () => {document.getelementById ("file").click(); }); }, onFileChange(event) {// Let tar = event. Target; let files = tar.files; If (files[0]. Size > 1024 * 1024 * 20) {this.$message.error(" image size cannot exceed 20M"); return false; } let reader = new FileReader(); reader.onload = (e) => { let base64 = e.target.result; var formData = new FormData(); formData.append("imageFile", this.convertBase64UrlToBlob(base64)); }; reader.readAsDataURL(files[0]); }, /** * convert base64 image data to Blob */ convertBase64UrlToBlob(urlData) {var bytes = window.atob(urldata.split (",")[1]); Var ab = new ArrayBuffer(bytes.length); var ab = new ArrayBuffer(bytes.length); var ia = new Uint8Array(ab); for (var i = 0; i < bytes.length; i++) { ia[i] = bytes.charCodeAt(i); } return new Blob([ab], { type: "image/png" }); ,}}};Copy the code
2.5 Introduce CSS styles in Style
.editor, .ql-toolbar { white-space: pre-wrap ! important; line-height: normal ! important; } .quill-img { display: none; }.ql-snow. Ql-tooltip [data-mode="link"]::before {content: "Please input the link address :"; } .ql-snow .ql-tooltip.ql-editing a.ql-action::after { border-right: 0px; Content: "save "; padding-right: 0px; }.ql-snow. Ql-tooltip [data-mode="video"]::before {content: "Please input the video address :"; } .ql-snow .ql-picker.ql-size .ql-picker-label::before, .ql-snow .ql-picker.ql-size .ql-picker-item::before { content: "14px"; } .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="small"]::before, .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="small"]::before { content: "10px"; } .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="large"]::before, .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="large"]::before { content: "18px"; } .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="huge"]::before, .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="huge"]::before { content: "32px"; } .ql-snow .ql-picker.ql-header .ql-picker-label::before, Ql-snow.ql-picker.ql-header. ql-picker-item::before {content: "text "; } .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before, Ql-snow.ql-picker.ql-header. ql-picker-item[data-value="1"]::before {content: "title 1"; } .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before, Ql-snow.ql-picker.ql-header. ql-picker-item[data-value="2"]::before {content: "title 2"; } .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before, Ql-snow.ql-picker.ql-header. ql-picker-item[data-value="3"]::before {content: "title 3"; } .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before, Ql-picker.ql-header. ql-picker-item[data-value="4"]::before {content: "title 4"; } .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before, Ql-picker.ql-header. ql-picker-item[data-value="5"]::before {content: "title 5"; } .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before, Ql-picker.ql-header. ql-picker-item[data-value="6"]::before {content: "title 6"; } .ql-snow .ql-picker.ql-font .ql-picker-label::before, .ql-snow .ql-picker.ql-font .ql-picker-item::before { content: "Standard font "; } .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="serif"]::before, Ql-snow.ql-picker.ql-font. Ql-picker-item [data-value="serif"]::before {content: "Serif "; } .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="monospace"]::before, Ql-snow.ql-picker.ql-font. Ql-picker-item [data-value="monospace"]::before {content: "monospace"; }Copy the code

Summary: Complete vUE page code

<template>
  <div>
    <div class="editor" ref="editor" :style="styles"></div>
    <input
      id="file"
      type="file"
      style="display: none"
      accept="image/png, image/gif, image/jpeg, image/bmp, image/x-icon"
      @change="onFileChange"
    />
  </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";

export default {
  name: "Editor",
  props: {
    /* 编辑器的内容 */
    value: {
      type: String,
      default: "",
    },
    /* 高度 */
    height: {
      type: Number,
      default: null,
    },
    /* 最小高度 */
    minHeight: {
      type: Number,
      default: null,
    },
  },
  data() {
    return {
      Quill: null,
      currentValue: "",
      options: {
        theme: "snow",
        bounds: document.body,
        debug: "warn",
        modules: {
          // 工具栏配置
          toolbar: [
            ["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"], // 清除文本格式
            ["link", "image", "video"], // 链接、图片、视频
          ],
        },
        placeholder: "请输入内容",
        readOnly: false,
      },
    };
  },
  computed: {
    styles() {
      let style = {};
      if (this.minHeight) {
        style.minHeight = `${this.minHeight}px`;
      }
      if (this.height) {
        style.height = `${this.height}px`;
      }
      return style;
    },
  },
  watch: {
    value: {
      handler(val) {
        if (val !== this.currentValue) {
          this.currentValue = val === null ? "" : val;
          if (this.Quill) {
            this.Quill.pasteHTML(this.currentValue);
          }
        }
      },
      immediate: true,
    },
  },
  mounted() {
    this.init();
  },
  beforeDestroy() {
    this.Quill = null;
  },
  methods: {
    init() {
      const editor = this.$refs.editor;
      this.Quill = new Quill(editor, this.options);
      this.Quill.pasteHTML(this.currentValue);
      this.Quill.on("text-change", (delta, oldDelta, source) => {
        const html = this.$refs.editor.children[0].innerHTML;
        const text = this.Quill.getText();
        const quill = this.Quill;
        this.currentValue = html;
        this.$emit("input", html);
        this.$emit("on-change", { html, text, quill });
      });
      this.Quill.on("text-change", (delta, oldDelta, source) => {
        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);
      });
      let toolbar = this.Quill.getModule("toolbar"); //获取工具栏模块
      //对工具栏的image添加回调函数覆盖原本的方法
      toolbar.addHandler("image", () => {
        document.getElementById("file").click();
      });
    },
    onFileChange(event) {
      //通过input本身的change钩子,对图片进行处理
      let tar = event.target;
      let files = tar.files;
      if (files[0].size > 1024 * 1024 * 20) {
        this.$message.error("图片大小不能超过20M");
        return false;
      }

      //使用FileReader读取图片成base64编码,再插入到编辑框中
      let reader = new FileReader();
      reader.onload = (e) => {
        let base64 = e.target.result;
        var formData = new FormData();
        formData.append("imageFile", this.convertBase64UrlToBlob(base64));
      
      };
      reader.readAsDataURL(files[0]);
    },
    /**
     * 将以base64的图片数据转换为Blob
     */
    convertBase64UrlToBlob(urlData) {
      var bytes = window.atob(urlData.split(",")[1]); //去掉url的头,并转换为byte
      //处理异常,将ascii码小于0的转换为大于0
      var ab = new ArrayBuffer(bytes.length);
      var ia = new Uint8Array(ab);
      for (var i = 0; i < bytes.length; i++) {
        ia[i] = bytes.charCodeAt(i);
      }
      return new Blob([ab], { type: "image/png" });
    },
  },
};
</script>

<style>
.editor,
.ql-toolbar {
  white-space: pre-wrap !important;
  line-height: normal !important;
}
.quill-img {
  display: none;
}
.ql-snow .ql-tooltip[data-mode="link"]::before {
  content: "请输入链接地址:";
}
.ql-snow .ql-tooltip.ql-editing a.ql-action::after {
  border-right: 0px;
  content: "保存";
  padding-right: 0px;
}

.ql-snow .ql-tooltip[data-mode="video"]::before {
  content: "请输入视频地址:";
}

.ql-snow .ql-picker.ql-size .ql-picker-label::before,
.ql-snow .ql-picker.ql-size .ql-picker-item::before {
  content: "14px";
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="small"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="small"]::before {
  content: "10px";
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="large"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="large"]::before {
  content: "18px";
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="huge"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="huge"]::before {
  content: "32px";
}

.ql-snow .ql-picker.ql-header .ql-picker-label::before,
.ql-snow .ql-picker.ql-header .ql-picker-item::before {
  content: "文本";
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
  content: "标题1";
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
  content: "标题2";
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
  content: "标题3";
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
  content: "标题4";
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
  content: "标题5";
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
  content: "标题6";
}

.ql-snow .ql-picker.ql-font .ql-picker-label::before,
.ql-snow .ql-picker.ql-font .ql-picker-item::before {
  content: "标准字体";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="serif"]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="serif"]::before {
  content: "衬线字体";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="monospace"]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="monospace"]::before {
  content: "等宽字体";
}
</style>
Copy the code

The following error may be reported after copying the code at this point

Solution: Download Core-js

npm i  core-js
Copy the code

The final result is as follows:

These are the two ways the compiler Quill implements rich text

Strict mode

Strict mode is used in a project if the following occurs during project execution

In this case, check whether the eslintrc.js file is in strict mode

The code in the configuration file.eslintrc.js is as follows

module.exports = { "env": { "browser": true, "es6": true, "node": true }, "extends": "Eslint :recommended", "parserOptions": {"ecmaVersion": 2015, "sourceType": "module"}, "rules": {// Indent ": ["error", 4 // My editor automatically formatted, not using tabs, but four Spaces], "linebreak-style": ["error", "Windows"], // quotes" quotes": (1, "single"], / / a semicolon "semi" : [" error ", "always"], "no - unused - vars" : [2, {/ / allow statement did not use the variable "vars" : "Local", / / parameters do not check "args" : "none"}], / / empty line 100 "no - multiple - empty - lines" : [0, {" Max ": 100}], "no-mixed-spaces-and-tabs": [0], // Can't use console" no-console": 'off', // can't use "no-undef" for undefined variables: 0, // No Spaces after the end of a line "no-trailing Spaces ": 1, // Force the hump method to name "camelcase": 2, // Object literals can not have a comma "comma-dangle": (2, "never"], / / this alias "consistent - this" : [2, "that"],}};Copy the code

If vue-cli needs to cancel strict mode, you need to add the. Eslintignore file in the root directory (the same directory as the SRC directory of the entry file)

The code for the.eslintignore file is as follows

/build/
/config/
/dist/
/*.js
src/*
Copy the code

The overall catalog looks like this