当前位置: 首页 > news >正文

Vue2 + Quill富文本编辑器

本例quill使用的版本是2.0.0-dev.4,2.0.2测试下来有很多不兼容的地方,原因未知。
1、先引入依赖

npm install quill@2.0.0-dev.4

2、创建编辑器组件
components/Editor.vue

<template>
  <div>
    <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";

export default {
  name: "Editor",
  props: {
    /* 编辑器的内容 */
    value: {
      type: String,
      default: "",
    },
    /* 高度 */
    height: {
      type: Number,
      default: 600,
    },
    /* 最小高度 */
    minHeight: {
      type: Number,
      default: 400,
    },
  },
  data() {
    return {
      quill: null,
      currentValue: "",
      options: {
        theme: "snow",
        bounds: document.body,
        debug: "warn",
        modules: {
          table: true,
          // 工具栏配置
          toolbar: {
            container: [
              ["wordBox", "bold", "italic", "underline", "strike"], //加粗,斜体,下划线,删除线
              ["blockquote", "code-block"], //引用,代码块
              [{header: 1}, {header: 2}], // 标题,键值对的形式;1、2表示字体大小
              [{list: "ordered"}, {list: "bullet"}], //列表
              [{script: "sub"}, {script: "super"}], // 上下标
              [
                {table: 'TD'},
                {'table-insert-row': 'TIR'},
                {'table-insert-column': 'TIC'},
                {'table-delete-row': 'TDR'},
                {'table-delete-column': 'TDC'}
              ],
              [{indent: "-1"}, {indent: "+1"}], // 缩进
              [{direction: "rtl"}], // 文本方向
              [{'size': ['12px', '14px', '16px', '20px', '24px', '32px']}], // 字体大小
              [{header: [1, 2, 3, 4, 5, 6, false]}], //几级标题
              [{color: []}, {background: []}], // 字体颜色,字体背景颜色
              [{'font': ['SimSun', 'SimHei', 'Microsoft-YaHei', 'KaiTi', 'FangSong', 'Arial']}], //字体
              [{align: []}], //对齐方式
              ["clean"], //清除字体样式
              ["link", "image"], //上传图片、上传视频
            ],
            handlers: {
              //实现首行缩进的功能
              wordBox: function (val) {
                let range = this.quill.getSelection();
                this.quill.insertText(range.index, '\t', {'style': 'text-indent: 2em;'});
              },
              //增加表格
              table: function (val) {
                this.quill.getModule('table').insertTable(2, 3)
              },
              //插入行
              'table-insert-row': function () {
                this.quill.getModule('table').insertRowBelow()
              },
              //插入列
              'table-insert-column': function () {
                this.quill.getModule('table').insertColumnRight()
              },
              //删除行
              'table-delete-row': function () {
                this.quill.getModule('table').deleteRow()
              },
              //删除列
              'table-delete-column': function () {
                this.quill.getModule('table').deleteColumn()
              }
            }
          }
        },
        placeholder: "请输入内容",
      },
      // 图标显示对应的文字提示
      titleConfig: [
        {Choice: '.ql-wordBox', title: '首行缩进'},
        {Choice: '.ql-insertMetric', title: '跳转配置'},
        {Choice: '.ql-bold', title: '加粗'},
        {Choice: '.ql-italic', title: '斜体'},
        {Choice: '.ql-underline', title: '下划线'},
        {Choice: '.ql-header', title: '段落格式'},
        {Choice: '.ql-strike', title: '删除线'},
        {Choice: '.ql-blockquote', title: '块引用'},
        {Choice: '.ql-code', title: '插入代码'},
        {Choice: '.ql-code-block', title: '插入代码段'},
        {Choice: '.ql-font', title: '字体'},
        {Choice: '.ql-size', title: '字体大小'},
        {Choice: '.ql-list[value="ordered"]', title: '编号列表'},
        {Choice: '.ql-list[value="bullet"]', title: '项目列表'},
        {Choice: '.ql-direction', title: '文本方向'},
        {Choice: '.ql-header[value="1"]', title: 'h1'},
        {Choice: '.ql-header[value="2"]', title: 'h2'},
        {Choice: '.ql-align', title: '对齐方式'},
        {Choice: '.ql-color', title: '字体颜色'},
        {Choice: '.ql-background', title: '背景颜色'},
        {Choice: '.ql-image', title: '图像'},
        {Choice: '.ql-video', title: '视频'},
        {Choice: '.ql-link', title: '添加链接'},
        {Choice: '.ql-formula', title: '插入公式'},
        {Choice: '.ql-clean', title: '清除字体格式'},
        {Choice: '.ql-script[value="sub"]', title: '下标'},
        {Choice: '.ql-script[value="super"]', title: '上标'},
        {Choice: '.ql-indent[value="-1"]', title: '向左缩进'},
        {Choice: '.ql-indent[value="+1"]', title: '向右缩进'},
        {Choice: '.ql-header .ql-picker-label', title: '标题大小'},
        {Choice: '.ql-align .ql-picker-item:first-child', title: '居左对齐'},
        {Choice: '.ql-align .ql-picker-item[data-value="center"]', title: '居中对齐'},
        {Choice: '.ql-align .ql-picker-item[data-value="right"]', title: '居右对齐'},
        {Choice: '.ql-align .ql-picker-item[data-value="justify"]', title: '两端对齐'},
        {Choice: '.ql-table', title: '添加表格'},
        {Choice: '.ql-table-insert-row', title: '插入行'},
        {Choice: '.ql-table-insert-column', title: '插入列'},
        {Choice: '.ql-table-delete-row', title: '删除行'},
        {Choice: '.ql-table-delete-column', title: '删除列'},
      ]
    }
  },
  computed: {
    styles() {
      // 设置宽高
      let style = {width: '1200px'};
      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 === '<p><br></p>') ? "" : val;
          if (this.quill) {
            this.quill.clipboard.dangerouslyPasteHTML(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.clipboard.dangerouslyPasteHTML(this.currentValue);
      this.quill.on("text-change", (delta, oldDelta, source) => {
        let html = this.$refs.editor.children[0].innerHTML;
        if (html.indexOf('<table>') !== -1) {
        //这里是特殊处理,因为是邮件模板,保存的html会丢失表格样式,需要在内容前面加上
         let _class = "<head>"
          +"<style>"
          +"table {table-layout: fixed; width: 100%; border-collapse: collapse; }"
          +"td, th { padding: 2px 5px; border: 1px solid #000; outline: none; display: table-cell; vertical-align: inherit; unicode-bidi: isolate;}"
          +"img { max-width: 100%; height: auto; }"
          +"</style>"
          +"</head>";
          html = _class + html;
        }
        this.currentValue = html;
        const text = this.quill.getText();
        const quill = this.quill;
        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);
      });
	  // 首行缩进的图标
      document.querySelector('.ql-wordBox').innerHTML = '<i class="el-icon-s-unfold"/>'
      // 鼠标悬浮在图标上显示对应的标题
      this.titleConfig.forEach(item => {
        const tip = document.querySelector(item.Choice)
        if (tip) {
          tip.setAttribute('title', item.title)
        }
      });
    },
  },
};
</script>

<style lang="less" scoped>
::v-deep {
  .ql-toolbar {
   /* 要与styles()方法中的width保持一致 */
    width: 1200px; 
  }
  /*设置字体和字体大小*/
  .ql-picker.ql-font .ql-picker-label[data-value=SimSun]::before, .ql-picker.ql-font .ql-picker-item[data-value=SimSun]::before {
    content: "宋体";
    font-family: "SimSun" !important;
  }

  .ql-picker.ql-font .ql-picker-label[data-value=SimHei]::before, .ql-picker.ql-font .ql-picker-item[data-value=SimHei]::before {
    content: "黑体";
    font-family: "SimHei";
  }

  .ql-picker.ql-font .ql-picker-label[data-value=Microsoft-YaHei]::before, .ql-picker.ql-font .ql-picker-item[data-value=Microsoft-YaHei]::before {
    content: "微软雅黑";
    font-family: "Microsoft YaHei";
  }

  .ql-picker.ql-font .ql-picker-label[data-value=KaiTi]::before, .ql-picker.ql-font .ql-picker-item[data-value=KaiTi]::before {
    content: "楷体";
    font-family: "KaiTi" !important;
  }

  .ql-picker.ql-font .ql-picker-label[data-value=FangSong]::before, .ql-picker.ql-font .ql-picker-item[data-value=FangSong]::before {
    content: "仿宋";
    font-family: "FangSong";
  }

  .ql-picker.ql-header .ql-picker-label[data-value='1']::before, .ql-picker.ql-header .ql-picker-item[data-value='1']::before {
    content: '一级标题';
  }
  .ql-picker.ql-header .ql-picker-label[data-value='2']::before, .ql-picker.ql-header .ql-picker-item[data-value='2']::before {
    content: '二级标题';
  }
  .ql-picker.ql-header .ql-picker-label[data-value='3']::before, .ql-picker.ql-header .ql-picker-item[data-value='3']::before {
    content: '三级标题';
  }
  .ql-picker.ql-header .ql-picker-label[data-value='4']::before, .ql-picker.ql-header .ql-picker-item[data-value='4']::before {
    content: '四级标题';
  }
  .ql-picker.ql-header .ql-picker-label[data-value='5']::before, .ql-picker.ql-header .ql-picker-item[data-value='5']::before {
    content: '五级标题';
  }
  .ql-picker.ql-header .ql-picker-label[data-value='6']::before, .ql-picker.ql-header .ql-picker-item[data-value='6']::before {
    content: '六级标题';
  }
  .ql-picker.ql-header .ql-picker-label::before, .ql-picker.ql-header .ql-picker-item::before {
    content: '标准';
  }

  .ql-picker.ql-size .ql-picker-label[data-value='12px']::before, .ql-picker.ql-size .ql-picker-item[data-value='12px']::before {
    content: '12px';
  }

  .ql-picker.ql-size .ql-picker-label[data-value='14px']::before, .ql-picker.ql-size .ql-picker-item[data-value='14px']::before {
    content: '14px';
  }

  .ql-picker.ql-size .ql-picker-label[data-value='16px']::before, .ql-picker.ql-size .ql-picker-item[data-value='16px']::before {
    content: '16px';
  }

  .ql-picker.ql-size .ql-picker-label[data-value='20px']::before, .ql-picker.ql-size .ql-picker-item[data-value='20px']::before {
    content: '20px';
  }

  .ql-picker.ql-size .ql-picker-label[data-value='24px']::before, .ql-picker.ql-size .ql-picker-item[data-value='24px']::before {
    content: '24px';
  }

  .ql-picker.ql-size .ql-picker-label[data-value='32px']::before, .ql-picker.ql-size .ql-picker-item[data-value='32px']::before {
    content: '32px';
  }

  .ql-snow .ql-picker.ql-size .ql-picker-label[data-value='32px']::before, .ql-snow .ql-picker.ql-size .ql-picker-item[data-value='32px']::before {
    content: '32px';
  }

 /* 设置表格操作的几个按钮图标,可以在 www.iconfont.cn 找 */
  .ql-toolbar .ql-table-insert-row {
    background-image: url('../assets/icon/insert_row.svg');
    background-size: 14px 14px;
    background-repeat: no-repeat;
    background-position: center;
  }
  .ql-toolbar .ql-table-insert-column {
    background-image: url('../assets/icon/insert_column.svg');
    background-size: 14px 14px;
    background-repeat: no-repeat;
    background-position: center;
  }
  .ql-toolbar .ql-table-delete-row {
    background-image: url('../assets/icon/delete_row.svg');
    background-size: 14px 14px;
    background-repeat: no-repeat;
    background-position: center;
  }
  .ql-toolbar .ql-table-delete-column {
    background-image: url('../assets/icon/delete_column.svg');
    background-size: 14px 14px;
    background-repeat: no-repeat;
    background-position: center;
  }

}
</style>

3、使用

<template>
  <div class="app-container">
	<div class="drawer-bd">
	   <el-form ref="form" :model="form" :rules="rules" label-position="top">
		<el-form-item label="邮件模板" prop="content">
          <editor ref="editor" :value="form.content" v-model="form.content"/>
        </el-form-item>
      </el-form>
    </div>
    <div class="drawer-ft">
      <el-button @click="reset">清 空</el-button>
      <el-button type="primary" @click="submitForm">保 存</el-button>
    </div>
  </div>
</template>
<script>
import Editor from "@/components/Editor";

export default {
  name: 'Demo',
  components: {Editor},
  data() {
  	return {
      // 表单参数
      form: {
		content: undefined
	  },
	  rules: {
	    content: [
          {required: true, message: '请输入邮件模板', trigger: 'blur'},
        ],
      },
    }
  },
  mounted() {
    this.$nextTick(() => {
      // 加载数据
    })
  },
  methods: {
  	reset() {
  	  // ... ...
  	},
  	submitForm: function () {
      this.$refs["form"].validate(valid => {
        if (valid) {
          // ... ...
        }
      })
    }
  }
}
</script>

4、实际效果
在这里插入图片描述

相关文章:

  • Netty笔记9:粘包半包
  • golang程序员如何3天完成python学习
  • 清理pip和conda缓存
  • Elasticsearch 限制索引大小与索引模板匹配冲突解决方案
  • 开源架构与人工智能的融合:开启技术新纪元
  • 单元测试与仿真程序之间的选择
  • 【计算机网络入门】初学计算机网络(十一)重要
  • EasyDSS视频推拉流/直播点播平台:Mysql数据库接口报错502处理方法
  • centos和ubuntu下安装redis
  • 【大模型基础_毛玉仁】1.1 基于统计方法的语言模型
  • TMS320F28P550SJ9学习笔记1:CCS导入工程以及测试连接单片机仿真器
  • post get 给后端传参数
  • 三、数据提取
  • 二分题目leetcode
  • not support ClassForName
  • SQL语句初学
  • 网络原理--HTTP协议
  • LeeCode题库第四十三题
  • JSON格式化工具
  • com.mysql.jdbc.Driver 和 com.mysql.cj.jdbc.Driver的区别
  • 专门做简历的网站有哪些/开个网站平台要多少钱
  • 南昌地宝网招聘信息网最新招聘/网站怎样优化文章关键词
  • 网站建设优化/足球比赛今日最新推荐
  • 上海智能网站建设设计/咸宁网站seo
  • html5 + css3 网站/杭州网站推广优化
  • 公司网站制作注意事项/搜索引擎优化实训报告