富文本组件的封装:使用vue-quill-editor插件

Editor组件代码

<template>
  <div class="box">
    <el-upload
      style="display: none"
      class="avatar"
      :action="uploadPath"
      :headers="headers"
      :show-file-list="false"
      :on-success="uploadSuccess"
      :on-error="uploadError"
      :before-upload="beforeUpload"
      accept=".jpg,.jpeg,.png,.gif"
    >
    </el-upload>
    <el-upload
      :show-file-list="false"
      :on-success="handleFileSuccess"
      :before-upoad="handleFileBeforeUpload"
      type="drag"
      :headers="headers"
      :action="uploadPath"
      class="uploadFile"
    ></el-upload>
    <quill-editor
      class="ql-editor"
      v-model="content"
      ref="myQuillEditor"
      :options="editorOption"
      @blur="onEditorBlur($event)"
      @focus="onEditorFocus($event)"
      @change="onEditorChange($event)"
    >
    </quill-editor>
  </div>
</template>

<script>
import { Quill } from "vue-quill-editor";
import "../../../node_modules/quill/dist/quill.snow.css";
import { getToken } from "@/utils/auth";//请求头
import { getStore } from "@/utils/storage";//请求头
import { addQuillTitle } from "@/utils/quill/quill-title.js";
import { lineHeightStyle } from "@/utils/quill/lineHeight";
import ImageResize from "quill-image-resize-module"; // 引用,调整图片大小
Quill.register("modules/imageResize", ImageResize);

// 自定义插入a链接
var Link = Quill.import("formats/link");
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.setAttribute("download", value.innerText); // 左键点击即下载
      node.innerText = value.innerText;
      node.download = value.innerText;
    }
    return node;
  }
}
FileBlot.blotName = "link";
FileBlot.tagName = "A";
Quill.register(FileBlot);

let fonts = [
  "MicrosoftYaHei",
  "SimSun",
  "SimHei",
  "KaiTi",
  "FangSong",
  "Arial",
  "TimesNewRoman",
  "sansSerif",
];
Quill.imports["formats/font"].whitelist = fonts;
Quill.register(Quill.imports["formats/font"]);

let fontSizeStyle = Quill.import("attributors/style/size");
fontSizeStyle.whitelist = ["16px", "20px", "24px", "30px", "36px", "42px"];
Quill.register(fontSizeStyle, true);

const toolbarOptions = [
  ["bold", "italic", "underline", "strike"], // 切换按钮
  [{ header: 1 }, { header: 2 }], //自定义按钮的值
  [{ list: "ordered" }, { list: "bullet" }],
  [{ indent: "-1" }, { indent: "+1" }], //缩进
  [{ direction: "rtl" }], // 文本方向
  [{ size: fontSizeStyle.whitelist }], //自定义字号
  [{ lineheight: ["initial", "1", "1.5", "1.75", "2", "3", "4", "5"] }], // 行高
  [{ header: [1, 2, 3, 4, 5, 6, false] }],
  [{ color: [] }, { background: [] }], // 下拉菜单,带有主题的默认值
  [{ font: fonts }],
  [{ align: [] }],
  ["link", "image", "upload"],
  ["clean"],
];
Quill.register({ "formats/lineHeight": lineHeightStyle }, true);

export default {
  name: "Editor",
  components: { Quill },
  props: {
    /* 编辑器的内容 */
    value: {
      type: String,
    },
    /* 图片大小 */
    maxSize: {
      type: Number,
      default: 4000, //kb
    },
  },
  data() {
    return {
      content: this.value,
      uploadPath: "",
      editorOption: {
        placeholder: "",
        theme: "snow", //	bubble/snow
        modules: {
          toolbar: {
            container: toolbarOptions,
            handlers: {
              image: function (value) {
                if (value) {
                  // 触发input框选择图片文件
                  document.querySelector(".avatar input").click();
                } else {
                  this.quill.format("image", false);
                }
              },
              upload: (value) => {
                if (value) {
                  document.querySelector(".uploadFile input").click();
                }
              },
              lineheight: function (value) {
                if (value) {
                  this.quill.format("lineHeight", value);
                } else {
                  console.log(value);
                }
              },
            },
          },
          imageResize: {
            displayStyles: {
              backgroundColor: "black",
              border: "none",
              color: "white",
            },
            modules: ["Resize", "DisplaySize", "Toolbar"],
          },
        },
      },
      uploadPath: process.env.VUE_APP_BASE_API + "/common/upload/file", // 上传的图片服务器地址
      headers: {
        // 请求头
        Authorization: getToken() ? getToken() : getStore("h5Token"),
      },
    };
  },
  watch: {
    value: function () {
      this.content = this.value;
    },
  },
  mounted() {
    addQuillTitle();
  },
  methods: {
    onEditorBlur() {
      //失去焦点事件
    },
    onEditorFocus() {},
    handleAvatarSuccess(res, file) {
      this.form.thumbnail = res.data;
      this.thumbnail = res.data;
    },
    uploadSuccess(res, file) {
      // res为图片服务器返回的数据
      // 获取富文本组件实例
      let quill = this.$refs.myQuillEditor.quill;
      // 如果上传成功
      if (res.status == 200) {
        // 获取光标所在位置
        let length = quill.getSelection().index;
        // 插入图片 res.data为服务器返回的图片地址
        quill.insertEmbed(length, "image", res.data.full);
        // 调整光标到最后
        quill.setSelection(length + 1);
      } else {
        this.$message.error("图片插入失败");
      }
      // loading动画消失
      this.quillUpdateImg = false;
    },
    handleFileSuccess(res, file) {
      let fileNameLength = file.name.length;
      let quill = this.$refs.myQuillEditor.quill;
      let length = quill.getSelection().index;
      quill.insertEmbed(
        length,
        "link",
        { href: res.data.full, innerText: file.name },
        "api"
      );
      quill.setSelection(length + fileNameLength);
    },
    // 富文本图片上传失败
    uploadError() {
      // loading动画消失
      this.quillUpdateImg = false;
      this.$message.error("图片插入失败");
    },
    // 富文本图片上传前
    beforeUpload() {
      // 显示loading动画
      this.quillUpdateImg = true;
    },
    handleFileBeforeUpload() {
      // 显示loading动画
      this.quillUploadFile = true;
    },
    onEditorChange({ editor, html, text }) {
      //内容改变事件
      this.$emit("input", html);
    },
    beforeAvatarUpload(file) {
      const isLt2M = file.size / 1024 / 1024 < 5;

      if (!isLt2M) {
        this.$message.error("上传图片大小不能超过 5MB!");
      }
      return isLt2M;
    },
  },
};
</script> 

<style >
@import "../../assets/styles/font.css";
.box {
  margin-top: -20px;
}
.uploadFile {
  height: 0;
}
.ql-editor {
  width: 100%;
}
.ql-editor img {
  max-width: 100%;
}
.avatar-uploader .el-upload {
  border: 1px dashed #d9d9d9;
  border-radius: 6px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
}
.avatar-uploader .el-upload:hover {
  border-color: #409eff;
}
.avatar-uploader-icon {
  font-size: 28px;
  color: #8c939d;
  width: 178px;
  height: 178px;
  line-height: 178px;
  text-align: center;
}
/* .avatar {
  width: 178px;
  height: 178px;
  display: block;
} */

.ql-container {
  overflow-y: auto;
  height: 20rem !important;
}

.ql-container ::-webkit-scrollbar {
  width: 10px; /*竖向滚动条的宽度*/
  height: 10px; /*横向滚动条的高度*/
}
.ql-container ::-webkit-scrollbar-thumb {
  /*滚动条里面的小方块*/
  background: #666666;
  border-radius: 5px;
}
.ql-container ::-webkit-scrollbar-track {
  /*滚动条轨道的样式*/
  background: #ccc;
  border-radius: 5px;
}
.editor {
  line-height: normal !important;
  height: 300px;
}
.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: "等宽字体";
}
.ql-snow.ql-toolbar .ql-upload {
  background: url("../../assets/image/upload.png");
  background-size: 16px 16px;
  background-position: center center;
  background-repeat: no-repeat;
  /* background: red; */
}
</style>

组件中引入的各个文件代码
1、import { addQuillTitle } from “@/utils/quill/quill-title.js”;
2、import { lineHeightStyle } from “@/utils/quill/lineHeight”;
3、 @import “…/…/assets/styles/font.css”;

1、quill-title.js


const titleConfig = {
    'ql-bold': '加粗',
    'ql-color': '字体颜色',
    'ql-font': '字体',
    'ql-code': '插入代码',
    'ql-italic': '斜体',
    'ql-link': '添加链接',
    'ql-background': '背景颜色',
    'ql-size': '字体大小',
    'ql-lineheight': '行高',
    'ql-strike': '删除线',
    'ql-script': '上标/下标',
    'ql-underline': '下划线',
    'ql-blockquote': '引用',
    'ql-header': '标题',
    'ql-indent': '缩进',
    'ql-list': '列表',
    'ql-align': '文本对齐',
    'ql-direction': '文本方向',
    'ql-code-block': '代码块',
    'ql-formula': '公式',
    'ql-image': '图片',
    'ql-upload': '附件',
    'ql-video': '视频',
    'ql-clean': '清除字体样式'
};

export function addQuillTitle () {
    const oToolBar = document.querySelector('.ql-toolbar'),
        aButton = oToolBar.querySelectorAll('button'),
        aSelect = oToolBar.querySelectorAll('select'),
        aSpan = oToolBar.querySelectorAll('span');
    aButton.forEach((item) => {
        if (item.className === 'ql-script') {
            item.value === 'sub' ? item.title = '下标' : item.title = '上标';
        } else if (item.className === 'ql-indent') {
            item.value === '+1' ? item.title = '向右缩进' : item.title = '向左缩进';
        } else if (item.className === 'ql-list') {
            item.value === 'ordered' ? item.title = '有序列表' : item.title = '无序列表'
        } else if (item.className === 'ql-header') {
            item.value === '1' ? item.title = '标题H1' : item.title = '标题H2';
        } else {
            item.title = titleConfig[item.classList[0]];
        }
    });
    aSelect.forEach((item) => {
        if (item.className != 'ql-color' && item.className != 'ql-background') {
            item.parentNode.title = titleConfig[item.classList[0]];
        }
    });
    aSpan.forEach((item) => {
        if (item.classList[0] === 'ql-color') {
            item.title = titleConfig[item.classList[0]];
        } else if (item.classList[0] === 'ql-background') {
            item.title = titleConfig[item.classList[0]];
        }
    });
}

2、lineHeight

import Quill from "quill";
let Parchment = Quill.import("parchment");
// console.log('Parchment', Parchment);
class lineHeightAttributor extends Parchment.Attributor.Style { }
const lineHeightStyle = new lineHeightAttributor("lineHeight", "line-height", {
    scope: Parchment.Scope.INLINE,
    whitelist: ["initial", "1", "1.5", "1.75", "2", "3", "4","5"]
});

export { lineHeightStyle };

3.font.css

.ql-editor{
  font-family: "Microsoft YaHei";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=SimSun]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=SimSun]::before {
  content: "宋体";
  font-family: "SimSun";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=SimHei]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=SimHei]::before {
  content: "黑体";
  font-family: "SimHei";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=MicrosoftYaHei]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=MicrosoftYaHei]::before {
  content: "微软雅黑";
  font-family: "Microsoft YaHei";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=KaiTi]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=KaiTi]::before {
  content: "楷体";
  font-family: "KaiTi";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=FangSong]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=FangSong]::before {
  content: "仿宋";
  font-family: "FangSong";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=Arial]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=Arial]::before {
  content: "Arial";
  font-family: "Arial";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=TimesNewRoman]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=TimesNewRoman]::before {
  content: "Times New Roman";
  font-family: "Times New Roman";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=sansSerif]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=sansSerif]::before {
  content: "sans-serif";
  font-family: "sans-serif";
}

.ql-font-SimSun {
  font-family: "SimSun";
}
.ql-font-SimHei {
  font-family: "SimHei";
}
.ql-font-MicrosoftYaHei {
  font-family: "Microsoft YaHei";
}
.ql-font-KaiTi {
  font-family: "KaiTi";
}
.ql-font-FangSong {
  font-family: "FangSong";
}
.ql-font-Arial {
  font-family: "Arial";
}
.ql-font-TimesNewRoman {
  font-family: "Times New Roman";
}
.ql-font-sansSerif {
  font-family: "sans-serif";
}

.ql-snow .ql-picker.ql-size .ql-picker-label[data-value='16px']::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value='16px']::before {
  content: '16px';
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value='20px']::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value='20px']::before {
  content: '20px';
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value='24px']::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value='24px']::before {
  content: '24px';
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value='30px']::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value='30px']::before {
  content: '30px';
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value='36px']::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value='36px']::before {
  content: '36px';
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value='42x']::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value='42px']::before {
  content: '42px';
}

.ql-snow .ql-picker.ql-lineheight .ql-picker-label::before {
  content: '行高';
}
.ql-snow .ql-picker.ql-lineheight .ql-picker-item[data-value='initial']::before {
  content: '默认';
}
.ql-snow .ql-picker.ql-lineheight .ql-picker-item[data-value='1']::before {
  content: '1';
}
.ql-snow .ql-picker.ql-lineheight .ql-picker-item[data-value='1.5']::before {
  content: '1.5';
}
.ql-snow .ql-picker.ql-lineheight .ql-picker-item[data-value='1.75']::before {
  content: '1.75';
}
.ql-snow .ql-picker.ql-lineheight .ql-picker-item[data-value='2']::before {
  content: '2';
}
.ql-snow .ql-picker.ql-lineheight .ql-picker-item[data-value='3']::before {
  content: '3';
}
.ql-snow .ql-picker.ql-lineheight .ql-picker-item[data-value='4']::before {
  content: '4';
}
.ql-snow .ql-picker.ql-lineheight .ql-picker-item[data-value='5']::before {
  content: '5';
}
.ql-snow .ql-picker.ql-lineheight {
  width: 70px;
}

全局引用Editor组件

// Quill富文本引入
import VueQuillEditor from "vue-quill-editor";
Vue.use(VueQuillEditor);
import "quill/dist/quill.core.css";
import "quill/dist/quill.snow.css";
import "quill/dist/quill.bubble.css";

// 封装的富文本组件
import quill from "@/components/Editor/index.vue";
Vue.component("quill", quill);

使用

在组件中使用

<template>
   <quill @input="input" :value="form.content"></quill>
</template>

<script>
export default {
  methods: {
   // 富文本
    input(content) {
      this.form.content = content;
    },
   }
 }
</script>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值