vue 富文本编辑器 quill (含代码高亮、自定义字体、汉化、鼠标悬浮提示、组件封装等)

简介: vue 富文本编辑器 quill (含代码高亮、自定义字体、汉化、鼠标悬浮提示、组件封装等)

安装依赖

npm i quill

.vue文件

<div ref="editor" :style="finalStyle"></div>
 
import Quill from "quill";
    import "quill/dist/quill.snow.css";
 

              let Options = {
                    theme: "snow",
                    placeholder: "请在这里输入",
                    modules: {
                        toolbar: {
                            container: [
                                // [{ 'header': 1 }, { 'header': 2 }], // 标题 —— 独立平铺
                                [{header: [1, 2, 3, 4, 5, 6, false]}], // 标题 —— 下拉选择
                                [{size: ["small", false, "large", "huge"]}], // 字体大小
                                [{list: "ordered"}, {list: "bullet"}], // 有序、无序列表
                                ["blockquote", "code-block"], // 引用  代码块
                                // 链接按钮需选中文字后点击
                                ["link", "image", "video"], // 链接、图片、视频
                                [{align: []}], // 对齐方式// text direction
                                [{indent: "-1"}, {indent: "+1"}], // 缩进
                                ["bold", "italic", "underline", "strike"], // 加粗 斜体 下划线 删除线
                                [{color: []}, {background: []}], // 字体颜色、字体背景颜色
                                [{'script': 'sub'}, {'script': 'super'}],      // 下标/上标
                                [{'font': []}],//字体
                                ["clean"], // 清除文本格式
                            ]
                        }
                    }
                }

new Quill(this.$refs.editor, Options)

代码高亮

需安装依赖

npm i highlight.js
 
import hljs from 'highlight.js'
    import 'highlight.js/styles/monokai-sublime.css'

在Options的modules里添加如下的syntax

                    modules: {
                        syntax: {
                            highlight: text => {
                                return hljs.highlightAuto(text).value; // 这里就是代码高亮需要配置的地方
                            }
                        },

自定义字体

                // 自定义字体
                let fontList = ['SimSun', 'SimHei', 'Microsoft-YaHei', 'KaiTi', 'FangSong']
                Quill.import('formats/font').whitelist = fontList; //将字体加入到白名单

在Options的toolbar里传入fontList

[{'font': fontList}],//字体

汉化

主要通过css实现

   .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[data-value=SimSun]::before,
    .ql-snow .ql-picker.ql-font .ql-picker-item[data-value=SimSun]::before {
        content: "宋体";
    }

    .ql-font-SimSun {
        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: "黑体";
    }


    .ql-font-SimHei {
        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: "微软雅黑";
    }

    .ql-font-Microsoft-YaHei {
        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: "楷体";
    }

    .ql-font-KaiTi {
        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: "仿宋";
    }

    .ql-font-FangSong {
        font-family: "FangSong";
    }

鼠标悬浮提示

new Quill 后执行方法即可

new Quill(this.$refs.editor, Options);
                this.addQuillTitle();
 
addQuillTitle() {
                const titleConfig = {
                    'ql-bold': '加粗',
                    'ql-color': '颜色',
                    'ql-font': '字体',
                    'ql-code': '插入代码',
                    'ql-italic': '斜体',
                    'ql-link': '添加链接',
                    'ql-background': '背景颜色',
                    'ql-size': '字体大小',
                    '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-video': '视频',
                    'ql-clean': '清除字体样式'
                }
                let oToolBar = document.querySelector('.ql-toolbar')
                if (!oToolBar) {
                    return
                }
                let aButton = oToolBar.querySelectorAll('button')
                let aSelect = oToolBar.querySelectorAll('select')
                aButton.forEach(function (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 {
                        item.title = titleConfig[item.classList[0]]
                    }
                })
                aSelect.forEach(function (item) {
                    item.parentNode.title = titleConfig[item.classList[0]]
                })
            }

组件封装

pub-editor_quill.vue

<template>
    <div ref="editor" :style="finalStyle"></div>
</template>
<script>
    import Quill from "quill";
    import "quill/dist/quill.snow.css";
    import hljs from 'highlight.js'
    import 'highlight.js/styles/monokai-sublime.css'

    export default {
        watch: {
            value(newVal) {
                if (newVal && newVal !== this.newValue) {
                    // 父组件传入新值,且父组件传入的新值不为子组件当前的值(子组件的内容发生改变时传给父组件的值)时
                    this.newValue = newVal
                    this.quill.pasteHTML(newVal)
                } else if (!newVal) {
                    this.quill.setText('')
                }
            },
            disabled(newVal) {
                this.quill.enable(!newVal)
            }
        },
        props: {
            hideTool: {
                type: Boolean,
                default: false
            },
            value: String,
            disabled: {
                type: Boolean,
                default: false
            },
            editorStyle: {
                type: Object,
                default: () => {
                    return {
                        minHeight: '100px'
                    }
                }
            },
            disabledStyle: {
                type: Object,
                default: () => {
                    // 与elementui表单禁用风格一致
                    return {
                        background: "#f5f7fa",
                        color: "#c0c4cc",
                        cursor: "not-allowed",
                        borderRadius: "4px",
                        border: "1px solid #dcdfe6"
                    }
                }
            },
            options: {
                type: Object,
                required: false,
                default: () => ({})
            },
        },
        methods: {
            disEditor() {
                this.finalStyle = Object.assign({}, this.finalStyle, this.disabledStyle)
            },
            init() {
                if (JSON.stringify(this.editorStyle) !== "{}") {
                    this.finalStyle = this.editorStyle
                }
                // 自定义字体
                let fontList = ['SimSun', 'SimHei', 'Microsoft-YaHei', 'KaiTi', 'FangSong']
                Quill.import('formats/font').whitelist = fontList; //将字体加入到白名单
                let defaultOptions = {
                    theme: "snow",
                    placeholder: "请在这里输入",
                    modules: {
                        syntax: {
                            highlight: text => {
                                return hljs.highlightAuto(text).value; // 这里就是代码高亮需要配置的地方
                            }
                        },
                        toolbar: {
                            container: [
                                // [{ 'header': 1 }, { 'header': 2 }], // 标题 —— 独立平铺
                                [{header: [1, 2, 3, 4, 5, 6, false]}], // 标题 —— 下拉选择
                                [{size: ["small", false, "large", "huge"]}], // 字体大小
                                [{list: "ordered"}, {list: "bullet"}], // 有序、无序列表
                                ["blockquote", "code-block"], // 引用  代码块
                                // 链接按钮需选中文字后点击
                                ["link", "image", "video"], // 链接、图片、视频
                                [{align: []}], // 对齐方式// text direction
                                [{indent: "-1"}, {indent: "+1"}], // 缩进
                                ["bold", "italic", "underline", "strike"], // 加粗 斜体 下划线 删除线
                                [{color: []}, {background: []}], // 字体颜色、字体背景颜色
                                [{'script': 'sub'}, {'script': 'super'}],      // 下标/上标
                                [{'font': fontList}],//字体
                                ["clean"], // 清除文本格式
                            ]
                        }
                    }
                }
                let finalOptions = Object.assign({}, defaultOptions, this.options)
                if (this.hideTool) {
                    finalOptions.modules.toolbar = null
                }
                this.quill = new Quill(this.$refs.editor, finalOptions);
                this.addQuillTitle();
                if (this.value) {
                    this.quill.pasteHTML(this.value)
                }
                // 调整光标到最后
                this.quill.setSelection(this.quill.getLength() + 1);
                this.quill.enable(!this.disabled)
                if (this.disabled) {
                    this.disEditor()
                }
                this.quill.on('selection-change', range => {
                    if (!range) {
                        this.$emit('blur', this.quill)
                    } else {
                        this.$emit('focus', this.quill)
                    }
                })
                this.quill.on('text-change', () => {
                    let html = this.$refs.editor.children[0].innerHTML
                    if (html === '<p><br></p>') html = ''
                    this.newValue = html
                    this.$emit('input', this.newValue)
                    this.$emit('change', {
                        html: this.newValue,
                        text: this.quill.getText()
                    })
                })
            },
            addQuillTitle() {
                const titleConfig = {
                    'ql-bold': '加粗',
                    'ql-color': '颜色',
                    'ql-font': '字体',
                    'ql-code': '插入代码',
                    'ql-italic': '斜体',
                    'ql-link': '添加链接',
                    'ql-background': '背景颜色',
                    'ql-size': '字体大小',
                    '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-video': '视频',
                    'ql-clean': '清除字体样式'
                }
                let oToolBar = document.querySelector('.ql-toolbar')
                if (!oToolBar) {
                    return
                }
                let aButton = oToolBar.querySelectorAll('button')
                let aSelect = oToolBar.querySelectorAll('select')
                aButton.forEach(function (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 {
                        item.title = titleConfig[item.classList[0]]
                    }
                })
                aSelect.forEach(function (item) {
                    item.parentNode.title = titleConfig[item.classList[0]]
                })
            }
        },
        mounted() {
            this.init()
        },
        beforeDestroy() {
            this.quill = null
            delete this.quill
        },
        data() {
            return {
                quill: null,
                newValue: null,
                finalStyle: {}
            }
        },
    }
</script>
<style>
    .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[data-value=SimSun]::before,
    .ql-snow .ql-picker.ql-font .ql-picker-item[data-value=SimSun]::before {
        content: "宋体";
    }

    .ql-font-SimSun {
        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: "黑体";
    }


    .ql-font-SimHei {
        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: "微软雅黑";
    }

    .ql-font-Microsoft-YaHei {
        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: "楷体";
    }

    .ql-font-KaiTi {
        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: "仿宋";
    }

    .ql-font-FangSong {
        font-family: "FangSong";
    }
</style>

使用方法

        <pub-editor_quill
                v-model="content"
                @blur="onEditorBlur()"
                @focus="onEditorFocus()"
                @change="onEditorChange()"
        />
        data() {
            return {
                content: '编辑器的初始值',
            }
        },
目录
相关文章
程序技术好文:自定义GridLookUpEdit编辑器
程序技术好文:自定义GridLookUpEdit编辑器
|
4月前
|
存储 JavaScript 前端开发
Vue中通过集成Quill富文本编辑器实现公告的发布。Vue项目中vue-quill-editor的安装与使用【实战开发应用】
文章展示了在Vue项目中通过集成Quill富文本编辑器实现公告功能的完整开发过程,包括前端的公告发布、修改、删除操作以及后端的数据存储和处理逻辑。
Vue中通过集成Quill富文本编辑器实现公告的发布。Vue项目中vue-quill-editor的安装与使用【实战开发应用】
|
4月前
|
JavaScript
在Vue项目中vue-quill-editor的安装与使用【详细图解+过程+包含图片的缩放拖拽】
文章详细介绍了在Vue项目中安装和使用`vue-quill-editor`的步骤,包括如何通过npm安装、局部挂载、在Vue页面中引入和配置使用。同时,还提供了如何实现图片的缩放和拖拽功能的进阶教程,涉及到安装额外的插件`quill-image-drop-module`和`quill-image-resize-module`,以及对Webpack配置的调整。最后,文章还提供了实际效果展示和一些后续可能的拓展功能,如上传视频和将图片上传到服务器等。
在Vue项目中vue-quill-editor的安装与使用【详细图解+过程+包含图片的缩放拖拽】
|
4月前
|
缓存 前端开发
ProFlow 流程编辑器框架问题之创建一个自定义节点如何解决
ProFlow 流程编辑器框架问题之创建一个自定义节点如何解决
50 1
|
5月前
|
JavaScript
【vue】 vue2 中使用 Tinymce 富文本编辑器
【vue】 vue2 中使用 Tinymce 富文本编辑器
633 6
|
5月前
|
JSON JavaScript 数据格式
文本-----wangEditor的使用,设置和获取内容,展示HTML无样式怎么办????console同步展示怎样写,Vue的配置在Vue3配置文件中的配置,是editor中的v-model绑定的值
文本-----wangEditor的使用,设置和获取内容,展示HTML无样式怎么办????console同步展示怎样写,Vue的配置在Vue3配置文件中的配置,是editor中的v-model绑定的值
|
5月前
|
JavaScript
【vue】 Tinymce 富文本编辑器 不想让上传的图片转换成base64,而是链接
【vue】 Tinymce 富文本编辑器 不想让上传的图片转换成base64,而是链接
342 0
|
5月前
|
前端开发
【若依】 若依内置富文本框quill,editor居中无效
【若依】 若依内置富文本框quill,editor居中无效
979 0
文本---富文本编辑器------Vue3使用富文本编辑器,Quill,全局样式和组件和样式的写法
文本---富文本编辑器------Vue3使用富文本编辑器,Quill,全局样式和组件和样式的写法
|
5月前
|
SQL JavaScript
vue 代码编辑器 vue-codemirror
vue 代码编辑器 vue-codemirror
74 0