Bootstrap

quill-editor实现自定义按钮,上传图片到服务器,获取文本内容并展示

quill-editor是一款非常强大的富文本组件,在最近的项目里也是使用到了,附上:插件源码

使用也是非常简单:

一.下载Vue-Quill-Editor


npm install vue-quill-editor --save

二.在vue里的组件中使用

<template>
    <div class="edit_container">
        <quill-editor 
            v-model="content" 
            ref="myQuillEditor" 
            :options="editorOption" 
            @blur="onEditorBlur($event)" @focus="onEditorFocus($event)"
            @change="onEditorChange($event)">
        </quill-editor>
    </div>
</template>

<script>
import { quillEditor } from "vue-quill-editor"; //调用编辑器

export default {
    components: {
        quillEditor
    },
    data() {
        return {
            content: `<p></p><p><br></p><ol><li><strong><em>Or drag/paste an image here.</em></strong></li><li><strong><em>rerew</em></strong></li><li><strong><em>rtrete</em></strong></li><li><strong><em>tytrytr</em></strong></li><li><strong><em>uytu</em></strong></li></ol>`,
            editorOption: {}
        }
    },
    methods: {
        onEditorReady(editor) {} // 准备编辑器,
        onEditorBlur(){}, // 失去焦点事件
        onEditorFocus(){}, // 获得焦点事件
        onEditorChange(){}, // 内容改变事件
    },
    computed: {
        editor() {
            return this.$refs.myQuillEditor.quill;
        },
    }
}
</script>

<style lang="scss">
    .edit_container  {
        width: 800px;
    } 

    //输入框
    .ql-container {
        height: 400px;
    }
</style>

三.获取富文本里的内容

我们发现富文本里数据是双向数据绑定到content这个变量上面的,那我们可以在methods里面写一个方法,在页面上添加一个按钮,当点击按钮时我们输出内容

//html
<button @click="getQuillEditorContent">点击输出内容</button>

//methods里面的方法
getQuillEditorContent() {
    console.log(this.content) //输出的是一段html
    // console.log(this.$refs.myQuillEditor.quill.getText())
    // console.log(this.$refs.myQuillEditor) //获取到富文本对象
    //这里一定要注意写法,这里获取的是一个Delta对象
    console.log(this.$refs.myQuillEditor.quill.getContents())
    // console.log(this.$refs.myQuillEditor.quill.getText())
   	// console.log(this.$refs.myQuillEditor.quill.root.innerHTML)
}

在这里插入图片描述

一定要注意第二个console.log里面的写法:

1.this.$refs.myQuillEditor是获取到富文本对象 console.log(this.$refs.myQuillEditor)
2.一定要点quill否则拿不到quill的api(各种方法)

由此可见,获取富文本内容的三种方式:

  1. content双向数据绑定的方式
  2. getContents() —— 返回的是Delta对象,一种JSON数组
  3. getText() —— 返回文本域里对应字符串

四 展示富文本数据

我们获取到的富文本是一段纯标签的数据,里面没有带上行内样式,为了原样展示出富文本的数据,专门写了一个展示的组件

//展示子组件
<template>
    <div>
       <div style='height:500px;width:700px;'>
            <div class='quill-editor'>
                <div class="ql-container">
                	<!-- 利用v-html标签做到原样展示 -->
                    <div class='ql-editor' v-html="quillHtml">
                    </div> 
                </div>
            </div>
        </div>
    </div>
</template>

<script>
import 'quill/dist/quill.core.css';
import 'quill/dist/quill.snow.css';
import 'quill/dist/quill.bubble.css';

export default {
    name: "quillShow",
    data(){
        return {
          quillHtml: ""  
        }
    },
    created(){
        //这里的created钩子只会执行一次,所以做不到传递异步数据的效果

        // this.quillHtml = this.quillHtmlStr
        
        console.log(this.quillHtml,this.quillHtmlStr)
    },
    watch: {
      //利用watch来实现传递异步数据
      "quillHtmlStr": function(newVal,oldVal){
          this.quillHtml = newVal
      }  
    },
    props: {
        quillHtmlStr: {
            type: String
        }
    }
}
</script>

在需要展示的父组件里引入这个组件并传值就可以了

//使用
<quill-show :quillHtmlStr.sync="quillHtmlStr"></quill-show>

//然后在方法里调用就可以了
getQuillEditorContent() {
     // console.log(this.content)
     // console.log(this.$refs.myQuillEditor)
     // console.log(this.$refs.myQuillEditor.quill.getContents())
     // console.log(this.$refs.myQuillEditor.quill.getText())
     // console.log(this.$refs.myQuillEditor.quill.root.innerHTML)

     this.quillHtmlStr = this.content
},

五.自定义一个图标

自定义一个图标的方式有两种方式:官方文档

1.利用options来配置:

//这里是全部的图标,你想要选择什么就留下什么就可以了
var toolbarOptions = [
  ['bold', 'italic', 'underline', 'strike'],        // toggled buttons
  ['blockquote', 'code-block'],

  [{ 'header': 1 }, { 'header': 2 }],               // custom button values
  [{ 'list': 'ordered'}, { 'list': 'bullet' }],
  [{ 'script': 'sub'}, { 'script': 'super' }],      // superscript/subscript
  [{ 'indent': '-1'}, { 'indent': '+1' }],          // outdent/indent
  [{ 'direction': 'rtl' }],                         // text direction

  [{ 'size': ['small', false, 'large', 'huge'] }],  // custom dropdown
  [{ 'header': [1, 2, 3, 4, 5, 6, false] }],

  [{ 'color': [] }, { 'background': [] }],          // dropdown with defaults from theme
  [{ 'font': [] }],
  [{ 'align': [] }],

  ['clean']                                         // remove formatting button
];

具体含义:

背景颜色 - background
加粗- bold
颜色 - color
字体 - font
内联代码 - code
斜体 - italic
链接 - link
大小 - size
删除线 - strike
上标/下标 - script
下划线 - underline
引用- blockquote
标题 - header
缩进 - indent
列表 - list
文本对齐 - align
文本方向 - direction
代码块 - code-block
公式 - formula
图片 - image
视频 - video
清除字体样式- clean

现在我们在vue的组件里配置:

//这里配置的是部分图标
const toolbarOptions = [
  ['bold', 'italic', 'underline', 'strike'],        // toggled buttons
  ['blockquote', 'code-block'],
  ['clean']                                         // remove formatting button
];
export default {
    components: {
        quillEditor
    },
    
    data() {
        return {
            content: ``,
            editorOption: {
                modules:{
                    toolbar: toolbarOptions
                }
            }
        }
    },
}

配置出来的效果:
在这里插入图片描述
2.利用solt的形式
solt形式就是利用button来实现和这个效果

3.增加一个图标

3.1 新建一个quill-config.js

在这个js里面重写配置一个操作按钮的数组,其中的[‘sourceEditor’]就是新的操作按钮的名字,而handlers 就是点击这个按钮应该做的事情,initButton是初始化这个按钮的样式函数,必须在mounted钩子去初始化这个按钮.

const toolbarOptions = [
  ['bold', 'italic', 'underline', 'strike'],        // toggled buttons
  ['blockquote', 'code-block'],
  ['clean']                                         // remove formatting button
  ['sourceEditor'] //新添加的工具
];

//重写事件方法
const handlers = {
  shadeBox:null,
  sourceEditor: function(){     //添加工具方法
       alert('我新添加的工具方法');
  }
};

//默认导出一个对象
export default {
  placeholder: '',
  theme: 'snow',  // 主题
  modules: {
    toolbar: {
      container: toolOptions,  // 工具栏选项
      handlers: handlers  // 事件重写
    }
  },
  initButton:function(){      //在使用的页面中初始化按钮样式
    const sourceEditorButton = document.querySelector('.ql-sourceEditor');
    sourceEditorButton.style.cssText = "width:80px; border:1px solid #ccc; border-radius:5px;";
    sourceEditorButton.innerText="源码编辑";
  }
}

3.2 在页面上使用

在页面上使用的时候,注意的是要在mounted钩子里面初始化这个按钮,不然页面上的按钮将没有样式.


<template>
  <quill-editor ref="myTextEditor"
    	v-model="content" :options="quillOption">
  </quill-editor>
</template>
 
<script>
 
  import { quillEditor } from 'vue-quill-editor'
  import quillConfig from './quill-config.js'
 
  export default {
    components: {
      quillEditor
    },
    mounted(){
      //在mounted钩子里面初始化富文本的样式
      quillConfig.initButton();
    },
    data() {
      return {
        content: '',
        quillOption: quillConfig,
      }
    }
  }
</script>

五 上传图片到服务器

这里的方法最主要是借鉴了quill-editor-vue组件在github里issue的实现方法,地址

<template>
    <div>
        <!-- 图片上传组件辅助-->
        <el-upload
                class="avatar-uploader"
                :action="serverUrl"
                name="img"
                :headers="header"
                :show-file-list="false"
                :on-success="uploadSuccess"
                :on-error="uploadError"
                :before-upload="beforeUpload">
        </el-upload>
        <!--富文本编辑器组件-->
       <el-row v-loading="uillUpdateImg">
        <quill-editor
                v-model="detailContent"
                ref="myQuillEditor"
                :options="editorOption"
                @change="onEditorChange($event)"
                @ready="onEditorReady($event)"
        >
        </quill-editor>
       </el-row>
    </div>
</template>
<script>
    export default {
    	// 工具栏配置
		const toolbarOptions = [
		  ['bold', 'italic', 'underline', 'strike'],        // toggled buttons
		  ['blockquote', 'code-block'],
		
		  [{'header': 1}, {'header': 2}],               // custom button values
		  [{'list': 'ordered'}, {'list': 'bullet'}],
		  [{'script': 'sub'}, {'script': 'super'}],      // superscript/subscript
		  [{'indent': '-1'}, {'indent': '+1'}],          // outdent/indent
		  [{'direction': 'rtl'}],                         // text direction
		
		  [{'size': ['small', false, 'large', 'huge']}],  // custom dropdown
		  [{'header': [1, 2, 3, 4, 5, 6, false]}],
		
		  [{'color': []}, {'background': []}],          // dropdown with defaults from theme
		  [{'font': []}],
		  [{'align': []}],
		  ['link', 'image', 'video'],
		  ['clean']                                         // remove formatting button
		]
        data() {
            return {
                serverUrl: '',  // 这里写你要上传的图片服务器地址
                header: {token: sessionStorage.token},  // 有的图片服务器要求请求头需要有token之类的参数,写在这里
                detailContent: '', // 富文本内容
                editorOption: {
                    placeholder: '',
                    theme: 'snow',  // or 'bubble'
                    modules: {
                        toolbar: {
                            container: toolbarOptions,  // 工具栏
                            handlers: {
                                'image': function (value) {
                                    if (value) {
                                        document.querySelector('#quill-upload input').click()
                                    } else {
                                        this.quill.format('image', false);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        methods: {
            // 富文本图片上传前
            beforeUpload() {
                // 显示loading动画
                this.quillUpdateImg = true
            },
            
            uploadSuccess(res, file) {
                // res为图片服务器返回的数据
                // 获取富文本组件实例
                let quill = this.$refs.myQuillEditor.quill
                // 如果上传成功
                if (res.code === '200' && res.info !== null) {
                    // 获取光标所在位置
                    let length = quill.getSelection().index;
                    // 插入图片  res.info为服务器返回的图片地址
                    quill.insertEmbed(length, 'image', res.info)
                    // 调整光标到最后
                    quill.setSelection(length + 1)
                } else {
                    this.$message.error('图片插入失败')
                }
                // loading动画消失
                this.quillUpdateImg = false
            },
       
            // 富文本图片上传失败
            uploadError() {
                // loading动画消失
                this.quillUpdateImg = false
                this.$message.error('图片插入失败')
            }
        }
    }
</script>

上面的代码是与element-ui结合实现的,其实与可以自己实现一个图片上传框,比如:

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        .box {
            width: 500px;
            height: 550px;
            margin: 150px auto;
        }

        input {
            height: 20px;
            margin-bottom: 10px;
            border: 1px solid #000;
        }

        .imgShow {
            width: 500px;
            height: 400px;
            border: 5px solid #000; 
        }
    </style>
</head>
<body>

    <div class="box">
        <!-- 利用label标签可以美化上传按钮 -->
        <label for="fileBtn" class="uploadFile">上传文件</label>
        <input type="file" id="fileBtn" style="display: none">
        <div class="imgShow"></div>
        <img src="" alt="">	
    </div>

</body>
</html>
<script>
    document.getElementById('fileBtn').onchange = function(){

        //获取到文件
        console.log(this.files[0]);
        // console.dir(this);

        //限制上传的代码格式与大小
        var fileSize = this.files[0].size 
        var size = fileSize / 1024

        if(size>2000){  
            alert("附件不能大于2M");
            return
        }

        //限制上传的文件格式
        var name = this.files[0].name;
        var fileName = name.substring(name.lastIndexOf(".")+1).toLowerCase();
        if( fileName !="jpg"  
        	&& fileName !="jpeg" 
        	&& fileName !="pdf" 
            && fileName !="png" 
            && fileName !="dwg" 
            && fileName !="gif" ){
	            alert("请选择图片格式文件上传(jpg,png,gif,dwg,pdf,gif等)!");
	            this.files[0].name = "";
	            return
        }

        // 将文件生成一个url路径字符串
        var url = URL.createObjectURL(this.files[0]);

        // 将图片展示在div的盒子里面
         document.querySelector('.imgShow').style.background = "url("+ url + ") no-repeat center/cover";
	
		//也可以将图片显示在img单标签里面
		 document.querySelector('img').src = url;
    }
</script>

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;