vue+vue-quill-editor:实现富文本编辑器

1,522 阅读4分钟

实现效果

51ac1f9b0e9217d7e38adc17e6c4b0484945dcd5a7dabf0a39d7515cbb3aa82fQzpcVXNlcnNcNjY2XEFwcERhdGFcUm9hbWluZ1xEaW5nVGFsa1wyMTA1NTI2ODY3X3YyXEltYWdlRmlsZXNcMTcxOTM3MjMzNDE5OF81RUM4MUEzQy01QkNGLTQ2MTMtQUE2OC1GOTIxMDUyMEM4QjIucG5n.png

1.版本

"vue-quill-editor": "^3.0.6",

2.组件使用

<template>
    <div>
        <div style="height:300px">
            <quillEditorComponent v-model.trim="formData.context" :isReadOnly="isReadOnly" @getEditorContent="getEditorContent">
            </quillEditorComponent>
        </div>
    </div>
</template>
<script>
import quillEditorComponent from '@/components/quillEditorComponent'
export default{
data () {
    return{
        formData: {
            title: '',
            kind: '',
            context: '',
        },
    }
},
methods: {
        getEditorContent (value) {
            console.log(value, 'value')
            this.$set(this.formData, 'context', value)
        },
    }
}
</script>

3.组件封装

<template>
    <div>
        <quill-editor ref="QuillEditor" class="editor" v-model.trim="content" :style="{height:height}" :options="editorOption" :disabled="isReadOnly"  @blur="onEditorBlur($event)" @focus="onEditorFocus($event)" @ready="onEditorReady($event)" @change="onEditorChange($event)">
        </quill-editor>
    </div>
</template>
<script>
import { quillEditor } from 'vue-quill-editor'
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'
import * as Quill from "quill";
import { ImageDrop } from 'quill-image-drop-module'
import ImageResize from 'quill-image-resize-module'
import {ImageExtend} from 'quill-image-paste-module'
import {toolbarOptions, titleConfig, fontSize, fonts} from './data.js'

const Size = Quill.import('attributors/style/size')
Size.whitelist = fontSize
Quill.register(Size, true)
const Font = Quill.import('formats/font')
Font.whitelist = fonts
Quill.register(Font, true)
Quill.register('modules/imageDrop', ImageDrop)
Quill.register('modules/imageResize', ImageResize)
Quill.register('modules/ImageExtend', ImageExtend)
export default {
    components: {quillEditor},
    name: 'quillEditorComponent',
    props: {
        value: {
            type: String,
            default: {}
        },
        isReadOnly: {
            type: Boolean,
            default: false,
        },
        height: {
            type: String,
            default: '520px',
        },
        placeholder: {
            type: String,
            default: '请输入信息内容',
        }
    },
    data () {
        return {
            content: '',
            once: true,
            editorOption: {
                history: {
                    delay: 1000,
                    maxStack: 50,
                    userOnly: false
                },
                modules: {
                    toolbar: {
                        container: toolbarOptions,
                    },
                    // 拖拽上传
                    imageDrop: true,
                    // 调整图片大小
                    // imageResize: {
                    //     displayStyles: {
                    //         backgroundColor: 'black',
                    //         border: 'none',
                    //         color: 'white'
                    //     },
                    //     modules: [ 'Resize', 'DisplaySize', 'Toolbar' ]
                    // },
                },
                placeholder: this.placeholder,
            },
        }
    },
    watch: {
        value (newVal) {
            if (newVal) {
                this.content = newVal
            }
        }
    },
    mounted () {
        this.initTitle()
    },
    methods: {
        initTitle () {
            document.getElementsByClassName('ql-editor')[0].dataset.placeholder = ''
            for (let item of titleConfig) {
                let tip = document.querySelector('.quill-editor ' + item.Choice)
                if (!tip) continue
                tip.setAttribute('title', item.title)
            }
        },
        // 失去焦点
        onEditorBlur (editor) {
            // console.log(editor, this.content, 'onEditorBlur');
            this.$emit('getEditorContent', this.content)
        },
        // 获得焦点
        onEditorFocus (editor) {
            // console.log(editor, this.content, 'onEditorFocus');
        },
        // 开始
        onEditorReady (editor) {
            // console.log(editor,this.content, 'onEditorReady');
        },
        // 值发生变化
        onEditorChange (editor) {
            // 如果需要手动控制数据同步,父组件需要显式地处理changed事件
            // this.content = editor.html;
            // console.log(editor, 'onEditorChange');
            this.$emit('getEditorContent', this.content)
        },

    }
}
</script>
<style>
@import './index.css';
</style>

4.样式文件 index.css

/* 字体风格 */
.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='Microsoft-YaHei']::before,
.ql-snow
  .ql-picker.ql-font
  .ql-picker-item[data-value='Microsoft-YaHei']::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='Times-New-Roman']::before,
.ql-snow
  .ql-picker.ql-font
  .ql-picker-item[data-value='Times-New-Roman']::before {
  content: 'Times New Roman';
  font-family: 'Times New Roman';
}
 
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value='sans-serif']::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value='sans-serif']::before {
  content: 'sans-serif';
  font-family: 'sans-serif';
}
 
.ql-font-SimSun {
  font-family: 'SimSun';
}
 
.ql-font-SimHei {
  font-family: 'SimHei';
}
 
.ql-font-Microsoft-YaHei {
  font-family: 'Microsoft YaHei';
}
 
.ql-font-KaiTi {
  font-family: 'KaiTi';
}
 
.ql-font-FangSong {
  font-family: 'FangSong';
}
 
.ql-font-Arial {
  font-family: 'Arial';
}
 
.ql-font-Times-New-Roman {
  font-family: 'Times New Roman';
}
 
.ql-font-sans-serif {
  font-family: 'sans-serif';
}
/* 字体大小 */
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="false"]::before{
    content: 'Normal';
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="10px"]::before{
    content: '10px';
}
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="10px"]::before {
    content: '10px';
    font-size: 10px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="14px"]::before{
    content: '14px';
}
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="14px"]::before {
    content: '14px';
    font-size: 14px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="16px"]::before{
    content: '16px';
}
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="16px"]::before {
    content: '16px';
    font-size: 16px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="18px"]::before {
    content: '18px';
}
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="18px"]::before {
    content: '18px';
    font-size: 18px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="20px"]::before{
    content: '20px';
}
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="20px"]::before {
    content: '20px';
    font-size: 20px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="24px"]::before{
    content: '24px';
}
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="24px"]::before {
    content: '24px';
    font-size: 24px;
}
/* 段落大小 */
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
    content: '标题1';
}
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
    content: '标题2';
}
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
    content: '标题3';
}
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
    content: '标题4';
}
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
    content: '标题5';
}
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="false"]::before {
    content: '正常';
}
/* 默认设置 */
.ql-snow .ql-editor{
    font-size: 14px;
}
/* 查看样式 */
.view-editor .ql-toolbar{
 display: none;
}
.view-editor .ql-container.ql-snow{
    border: 0;
}
.view-editor .ql-container.ql-snow .ql-editor{
    padding: 0;
}
/* 编辑样式 */
.edit-editor .ql-toolbar{
    display: block;
}
.edit-editor .ql-container.ql-snow{
    border: 1px solid #ccc;
    min-height: inherit;
}

5.配置文件 data.js

//toolbarOptions配置
export const toolbarOptions = [
    ['bold', 'italic', 'underline', 'strike'], // 加粗,倾斜,下划线,删除线
    ['blockquote', 'code-block'], // 引号,代码
   
    [{ list: 'ordered' }, { list: 'bullet' }], // 有序列表,无序列表
   
    [{ indent: '-1' }, { indent: '+1' }], // 左移,右移
    [{ direction: 'rtl' }], // 左右对齐
   
    [{ size: [false, '10px', '14px', '16px', '18px', '20px', '24px'] }], // 字体大小
    [{ header: [1, 2, 3, 4, 5, false] }], // 标题大小
   
    [{ color: [] }, { background: [] }], // 文字颜色,文字背景
    [{ font: ['SimSun', 'SimHei', 'Microsoft-YaHei', 'KaiTi', 'FangSong', 'Arial', 'Times-New-Roman', 'sans-serif'] }], // 字体风格
    [{ align: [] }], // 对齐方式
   
    ['image', 'video', 'link', 'clean']// 图片,链接,清除
]

// toolbar标题
export const titleConfig = [
    { 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-header .ql-picker-item[data-value="1"]', title: '标题一' },
    { Choice: '.ql-header .ql-picker-item[data-value="2"]', title: '标题二' },
    { Choice: '.ql-header .ql-picker-item[data-value="3"]', title: '标题三' },
    { Choice: '.ql-header .ql-picker-item[data-value="4"]', title: '标题四' },
    { Choice: '.ql-header .ql-picker-item[data-value="5"]', title: '标题五' },
    { Choice: '.ql-header .ql-picker-item[data-value="6"]', title: '标题六' },
    { Choice: '.ql-header .ql-picker-item:last-child', title: '标准' },
    { Choice: '.ql-size .ql-picker-item[data-value="small"]', title: '小号' },
    { Choice: '.ql-size .ql-picker-item[data-value="large"]', title: '大号' },
    { Choice: '.ql-size .ql-picker-item[data-value="huge"]', title: '超大号' },
    { Choice: '.ql-size .ql-picker-item:nth-child(2)', 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: '两端对齐' }
  ]
export const fontSize = [false, '10px', '14px', '16px', '18px', '20px', '24px']
  export const fonts  = ['SimSun', 'SimHei', 'Microsoft-YaHei', 'KaiTi', 'FangSong', 'Arial', 'Times-New-Roman', 'sans-serif']